Merge "Add checks where fetching vdm could cause NPE" into udc-dev
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 29ab455..e60ed4a 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 @@
// 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 1971a11..537a670 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 @@
}
}
+ /**
+ * 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 9ada8dc..47b7e13 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.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 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 @@
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 140cca6..15d6766 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.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 @@
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 @@
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 @@
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 a6a3aaf..6a4a52a 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 8a6bb7b..2db3eae 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -6784,7 +6784,7 @@
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 @@
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 @@
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 77fb8d0..fbc30de 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1699,7 +1699,7 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 58b93b3..43fa6e2 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2181,6 +2181,7 @@
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 @@
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 6422865..3615435 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -198,6 +198,26 @@
* 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 @@
}
/**
- * 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 @@
}
/**
- * 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 6d9d6a5..dfdfd0e 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -36,6 +36,7 @@
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.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.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.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 @@
// 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 @@
}
}
+ /**
+ * 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 a99815c..6301ad7 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -556,12 +556,7 @@
@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 fe40a4c..f48181b 100644
--- a/core/java/android/app/BroadcastOptions.java
+++ b/core/java/android/app/BroadcastOptions.java
@@ -60,7 +60,6 @@
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 @@
"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 @@
}
/**
- * {@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 @@
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 @@
}
/**
- * 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 @@
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 f653e13..5c38c42 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -341,12 +341,6 @@
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 69693fc..f4cd60d 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -38,7 +38,10 @@
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 @@
};
/**
+ * 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 91efa75..d2f2c3c 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -873,7 +873,7 @@
* 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 5a2f261..303ada0 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -453,6 +453,7 @@
&& 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 @@
.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 e750f49..5848521 100644
--- a/core/java/android/app/backup/BackupManager.java
+++ b/core/java/android/app/backup/BackupManager.java
@@ -1091,7 +1091,6 @@
*
* @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 f56c8c3..dcac59c 100644
--- a/core/java/android/app/backup/BackupTransport.java
+++ b/core/java/android/app/backup/BackupTransport.java
@@ -656,11 +656,14 @@
}
/**
- * 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 4d2c7cb..6cc4c8a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -89,14 +89,6 @@
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 @@
* 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 @@
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 @@
*/
@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 @@
}
}
- /**
- * 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 d8076b5..9f3b601 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -35,6 +35,7 @@
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 @@
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 7da9c32..3cb0572 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 @@
*/
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 e097189..f7af283 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 @@
*/
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 6d45365..ffbdff8 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.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 @@
*/
@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 @@
@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 @@
parcel.writeInt(mType);
parcel.writeString8(mName);
parcel.writeString8(mVendor);
+ parcel.writeInt(mFlags);
}
/**
@@ -92,22 +108,64 @@
}
/**
+ * 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 @@
*/
@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 @@
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 795c77f..d3502c5 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -389,6 +389,8 @@
@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 beec880..d0accb7 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -4276,7 +4276,7 @@
* <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 bd3cf5f..afc2285 100644
--- a/core/java/android/content/IntentFilter.java
+++ b/core/java/android/content/IntentFilter.java
@@ -20,9 +20,6 @@
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 @@
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 @@
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 @@
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 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 d927c5e..9388823 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -5720,17 +5720,6 @@
}
/**
- * @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 45e0efb..f440ac7 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 7ca92c3..c35e678 100644
--- a/core/java/android/content/pm/UserPackage.java
+++ b/core/java/android/content/pm/UserPackage.java
@@ -18,14 +18,16 @@
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 @@
*/
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 @@
@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 @@
}
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 @@
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 @@
}
}
}
+
+ @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 ffdc7b38..6b09c30 100644
--- a/core/java/android/content/res/FontScaleConverterFactory.java
+++ b/core/java/android/content/res/FontScaleConverterFactory.java
@@ -18,6 +18,7 @@
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 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;
+ }
- 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);
+ }
+
+ 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 d12d920..a7bce12 100644
--- a/core/java/android/content/res/OWNERS
+++ b/core/java/android/content/res/OWNERS
@@ -4,3 +4,5 @@
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 b756a43..c89a5c6 100644
--- a/core/java/android/credentials/CreateCredentialRequest.java
+++ b/core/java/android/credentials/CreateCredentialRequest.java
@@ -244,7 +244,7 @@
/** 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 0000000..6bd9de4
--- /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 3c10e81..dcfef56 100644
--- a/core/java/android/credentials/ui/IntentFactory.java
+++ b/core/java/android/credentials/ui/IntentFactory.java
@@ -22,6 +22,7 @@
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 @@
}
/**
+ * 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 1a3e0b0..73157e6 100644
--- a/core/java/android/hardware/SystemSensorManager.java
+++ b/core/java/android/hardware/SystemSensorManager.java
@@ -92,7 +92,8 @@
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 @@
/** @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 @@
}
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 @@
}
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 b766cd1..50dd7a0 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -989,24 +989,6 @@
/**
* 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 @@
@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 @@
* 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 @@
* 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 f6a2e33..6b56a06 100644
--- a/core/java/android/hardware/display/VirtualDisplayConfig.java
+++ b/core/java/android/hardware/display/VirtualDisplayConfig.java
@@ -18,121 +18,44 @@
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;
+ 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;
- /**
- * 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 VirtualDisplayConfig(
@NonNull String name,
@IntRange(from = 1) int width,
@IntRange(from = 1) int height,
@@ -142,219 +65,200 @@
@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;
}
- @DataClass.Generated.Member
- public static final @NonNull Parcelable.Creator<VirtualDisplayConfig> CREATOR
+ @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();
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<VirtualDisplayConfig> CREATOR
= new Parcelable.Creator<VirtualDisplayConfig>() {
@Override
public VirtualDisplayConfig[] newArray(int size) {
@@ -368,229 +272,161 @@
};
/**
- * 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.
- */
- @DataClass.Generated.Member
- public @NonNull Builder setSurface(@NonNull Surface value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x20;
- mSurface = value;
- return this;
- }
-
- /**
- * The unique identifier for the display. Shouldn't be displayed to the user.
+ * 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)}.
+ */
+ @NonNull
+ public Builder setSurface(@Nullable Surface surface) {
+ mSurface = surface;
+ return this;
+ }
+
+ /**
+ * 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;
- return this;
- }
-
- /** @see #setDisplayCategories */
- @DataClass.Generated.Member
- public @NonNull Builder addDisplayCategory(@NonNull String value) {
- if (mDisplayCategories == null) setDisplayCategories(new ArrayList<>());
- mDisplayCategories.add(value);
+ @NonNull
+ public Builder setDisplayCategories(@NonNull List<String> displayCategories) {
+ mDisplayCategories.clear();
+ mDisplayCategories.addAll(Objects.requireNonNull(displayCategories));
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.
+ * Adds a display category.
+ *
+ * @see #setDisplayCategories
*/
- @DataClass.Generated.Member
- public @NonNull Builder setRequestedRefreshRate(float value) {
- checkNotUsed();
- mBuilderFieldsSet |= 0x400;
- mRequestedRefreshRate = value;
+ @NonNull
+ public Builder addDisplayCategory(@NonNull String displayCategory) {
+ mDisplayCategories.add(Objects.requireNonNull(displayCategory));
return this;
}
- /** Builds the instance. This builder should not be touched after calling this! */
- public @NonNull VirtualDisplayConfig build() {
- checkNotUsed();
- mBuilderFieldsSet |= 0x800; // Mark builder used
+ /**
+ * 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()
+ */
+ @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;
+ }
- 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 @@
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 0954013..793a70e 100644
--- a/core/java/android/nfc/cardemulation/ApduServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/ApduServiceInfo.java
@@ -660,7 +660,7 @@
* @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 f8f7dfe..7a36b26 100644
--- a/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
+++ b/core/java/android/nfc/cardemulation/NfcFServiceInfo.java
@@ -340,7 +340,7 @@
* @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 0000000..202e1cf
--- /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 de94b86..8d8deaa 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -3057,6 +3057,7 @@
*
* @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 54e4909..d7cd61567 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -99,7 +99,6 @@
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.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 @@
}
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 @@
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 @@
mArray.close();
} catch (IOException e) {
Log.e(TAG, "Error closing backing array", e);
- if (mErrorHandler != null) {
- mErrorHandler.run();
- }
}
}
}
@@ -3088,8 +3086,21 @@
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 @@
@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 @@
}
}
- 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 @@
// 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 @@
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);
+ Log.i(TAG, "Received generation tracker for setting:"
+ + name
+ + " type:" + mUri.getPath()
+ + " in package:" + cr.getPackageName()
+ + " and user:" + userHandle
+ + " 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(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 @@
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 @@
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 @@
return keyValues;
}
}
- if (mGenerationTracker != null) {
- currentGeneration = mGenerationTracker.getCurrentGeneration();
- }
+ currentGeneration = generationTracker.getCurrentGeneration();
+ } else {
+ needsGenerationTracker = true;
}
}
@@ -3450,20 +3467,20 @@
// 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 @@
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 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 @@
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 @@
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();
+ 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) {
- 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.");
- }
+ 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 @@
"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 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 6896e02..d14abfd 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -4937,4 +4937,66 @@
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 f648ad4..434b1c7 100644
--- a/core/java/android/telephony/TelephonyRegistryManager.java
+++ b/core/java/android/telephony/TelephonyRegistryManager.java
@@ -53,9 +53,7 @@
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 @@
* 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 1a2c4df..b4e1f59 100644
--- a/core/java/android/util/SparseArrayMap.java
+++ b/core/java/android/util/SparseArrayMap.java
@@ -90,6 +90,14 @@
}
/**
+ * 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 e02e600..3947738 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -20,7 +20,6 @@
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 @@
// 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 9e17620..b574ecf 100644
--- a/core/java/android/view/OWNERS
+++ b/core/java/android/view/OWNERS
@@ -31,6 +31,7 @@
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 f430ec3..71a3a7b 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10078,9 +10078,12 @@
}
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 @@
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 aa631cf..9504852 100644
--- a/core/java/android/view/accessibility/AccessibilityManager.java
+++ b/core/java/android/view/accessibility/AccessibilityManager.java
@@ -2068,12 +2068,14 @@
* {@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 @@
* @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 26c59a6..622b0e2 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 ec1badb..8b55494 100644
--- a/core/java/android/view/inputmethod/InputMethodInfo.java
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -18,6 +18,7 @@
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 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 @@
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 bf9d3c2..fd86769 100644
--- a/core/java/android/window/ITaskOrganizer.aidl
+++ b/core/java/android/window/ITaskOrganizer.aidl
@@ -34,8 +34,9 @@
* 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/IWindowlessStartingSurfaceCallback.aidl b/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl
deleted file mode 100644
index a081356..0000000
--- a/core/java/android/window/IWindowlessStartingSurfaceCallback.aidl
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package android.window;
-
-import android.view.SurfaceControl;
-
-/**
- * Interface to be invoked when a windowless starting surface added.
- *
- * @param addedSurface The starting surface.
- * {@hide}
- */
-interface IWindowlessStartingSurfaceCallback {
- void onSurfaceAdded(in SurfaceControl addedSurface);
-}
diff --git a/core/java/android/window/SnapshotDrawerUtils.java b/core/java/android/window/SnapshotDrawerUtils.java
index 071c20f..1a58fd5 100644
--- a/core/java/android/window/SnapshotDrawerUtils.java
+++ b/core/java/android/window/SnapshotDrawerUtils.java
@@ -329,21 +329,6 @@
}
/**
- * 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 @@
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 451acbe..1b64e61 100644
--- a/core/java/android/window/StartingWindowInfo.java
+++ b/core/java/android/window/StartingWindowInfo.java
@@ -22,12 +22,9 @@
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 @@
/** @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 @@
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 @@
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 @@
* @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 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 @@
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 @@
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 5181236..384dacf 100644
--- a/core/java/android/window/StartingWindowRemovalInfo.java
+++ b/core/java/android/window/StartingWindowRemovalInfo.java
@@ -67,16 +67,6 @@
*/
public float roundedCornerRadius;
- /**
- * Remove windowless surface.
- */
- public boolean windowlessSurface;
-
- /**
- * Remove immediately.
- */
- public boolean removeImmediately;
-
public StartingWindowRemovalInfo() {
}
@@ -97,8 +87,6 @@
playRevealAnimation = source.readBoolean();
deferRemoveForIme = source.readBoolean();
roundedCornerRadius = source.readFloat();
- windowlessSurface = source.readBoolean();
- removeImmediately = source.readBoolean();
}
@Override
@@ -109,8 +97,6 @@
dest.writeBoolean(playRevealAnimation);
dest.writeBoolean(deferRemoveForIme);
dest.writeFloat(roundedCornerRadius);
- dest.writeBoolean(windowlessSurface);
- dest.writeBoolean(removeImmediately);
}
@Override
@@ -119,9 +105,7 @@
+ " 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 d4728c1..02878f8 100644
--- a/core/java/android/window/TaskOrganizer.java
+++ b/core/java/android/window/TaskOrganizer.java
@@ -92,10 +92,13 @@
* 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 @@
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 a8c2b2f..8066f50 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -357,6 +357,11 @@
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 7c237e69..ace8451 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_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.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 @@
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 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 62f1599..d93fff9 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_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 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 @@
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 @@
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 @@
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 506902f6..426cd74 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 @@
* 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 @@
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 @@
volatile long mEventsAdded;
volatile long mEventsRemoved;
- FastEventQueue(SchedulableEvent event) {
+ public FastEventQueue(SchedulableEvent event) {
mFirst = event;
mLast = mFirst;
mEventsAdded = 1;
@@ -149,7 +152,8 @@
* @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 @@
// 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 @@
}
}
- 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 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 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 @@
}
}
}
- mLock.wait((int) millisToWait);
+ lock.wait((int) millisToWait);
}
}
return event;
@@ -242,10 +248,25 @@
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 0000000..16e4abe
--- /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 7b01904..1b2934d 100644
--- a/core/java/com/android/internal/midi/MidiEventScheduler.java
+++ b/core/java/com/android/internal/midi/MidiEventScheduler.java
@@ -79,7 +79,7 @@
/**
* 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 8a9445d..28b98d6 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 dalvik.system.RuntimeHooks;
import dalvik.system.VMRuntime;
-import dalvik.system.ZipPathValidator;
import libcore.content.type.MimeMap;
@@ -265,31 +260,10 @@
*/
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 f72c4c3..59f6d2b 100644
--- a/core/java/com/android/internal/widget/LockPatternUtils.java
+++ b/core/java/com/android/internal/widget/LockPatternUtils.java
@@ -170,6 +170,8 @@
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 @@
}
/**
+ * @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 939a0e4..9c6a534 100644
--- a/core/jni/android_hardware_SensorManager.cpp
+++ b/core/jni/android_hardware_SensorManager.cpp
@@ -266,7 +266,8 @@
}
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 @@
}
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 @@
{"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 0000000..6a62c91
--- /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 2f94ed7..bf253ab 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 12646a0..e2b46d0 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 f97099d..16ed3ef 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 @@
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 @@
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 @@
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 0000000..5114e2cf
--- /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 ef106bc..ee1b2aa 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 @@
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 @@
@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 2e31bb5..b6fc137 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.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 @@
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>> mStorage;
+ private HashMap<String, HashMap<String, String>> mConfigsStorage;
+ private HashMap<String, String> mSettingsStorage;
+
@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,32 +125,87 @@
});
// 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,
- mCacheGenerationStore);
- bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, 0);
+ mConfigsCacheGenerationStore);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
- mCacheGenerationStore.get(0));
+ 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,
+ mSettingsCacheGenerationStore);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_INDEX_KEY, index);
+ bundle.putInt(Settings.CALL_METHOD_GENERATION_KEY,
+ mSettingsCacheGenerationStore.get(index));
+ }
+ return bundle;
+ });
+ }
+
+ @After
+ public void cleanUp() throws IOException {
+ mConfigsStorage.clear();
+ mSettingsStorage.clear();
}
@Test
@@ -135,16 +213,13 @@
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 @@
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 @@
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 @@
@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 ca1367a..0692052 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -248,17 +248,22 @@
@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 1db6587..6fa8f11 100644
--- a/core/tests/coretests/src/android/view/InsetsSourceTest.java
+++ b/core/tests/coretests/src/android/view/InsetsSourceTest.java
@@ -19,6 +19,7 @@
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 @@
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 @@
}
@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 b035c23..fde1a6d 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -248,6 +248,18 @@
}
@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 c540a15..0000000
--- 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 f3318f4..5cb5ffa0 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -471,7 +471,7 @@
<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 87a8053..0f2f879 100644
--- a/graphics/java/android/graphics/Color.java
+++ b/graphics/java/android/graphics/Color.java
@@ -767,7 +767,7 @@
* 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 6a1313e..66fabec 100644
--- a/graphics/java/android/graphics/Mesh.java
+++ b/graphics/java/android/graphics/Mesh.java
@@ -341,7 +341,6 @@
* @hide so only calls from module can utilize it
*/
long getNativeWrapperInstance() {
- nativeUpdateMesh(mNativeMeshWrapper, mIsIndexed);
return mNativeMeshWrapper;
}
@@ -383,5 +382,4 @@
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 b6fd0bb..585f81c 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 @@
}
@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 713dd39..69f0bad 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 @@
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 @@
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 8a18271..7ac4d51 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 @@
}
/**
+ * 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 @@
}
}
- /** 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 @@
// 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 @@
// 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 eb3c1df..00361d9 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 @@
}
}
- 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 3b2db51..76d9152 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 @@
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 @@
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 efd4594..b22c9c7 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 @@
@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 @@
}
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 10f25d0..2440838 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 @@
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 d1f4398..948bf2d 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 @@
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 @@
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 b59fe18..4cfaae6 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.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.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 @@
* 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 @@
mMainExecutor.executeDelayed(() -> {
mDisplayController.addDisplayWindowListener(this);
}, 0);
- mShellController.addConfigurationChangeListener(this);
}
/**
@@ -180,6 +180,7 @@
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 @@
if (pd == null) {
return;
}
+ pd.context.unregisterComponentCallbacks(this);
pd.wm.removeViewImmediate(pd.rootView);
mDisplayDropTargets.remove(displayId);
}
@@ -328,18 +330,29 @@
return mimeTypes;
}
- @Override
- public void onThemeChanged() {
- for (int i = 0; i < mDisplayDropTargets.size(); i++) {
- mDisplayDropTargets.get(i).dragLayout.onThemeChange();
- }
- }
-
+ // 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);
- }
+ 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 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 44fd8ee..fe42822a 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 @@
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 @@
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 @@
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 @@
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 @@
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 c7ad4fd..94b9e90 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 @@
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 c96323a..e1c0895 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 @@
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 8b24d86..427d79e 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 @@
new IRemoteAnimationFinishedCallback.Stub() {
@Override
public void onAnimationFinished() throws RemoteException {
- onRemoteAnimationFinishedOrCancelled(false /* cancel */, evictWct);
+ onRemoteAnimationFinishedOrCancelled(evictWct);
finishedCallback.onAnimationFinished();
}
};
@@ -953,7 +953,7 @@
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
- onRemoteAnimationFinishedOrCancelled(true /* cancel */, evictWct);
+ onRemoteAnimationFinishedOrCancelled(evictWct);
try {
adapter.getRunner().onAnimationCancelled(isKeyguardOccluded);
} catch (RemoteException e) {
@@ -974,15 +974,14 @@
}
}
- 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 @@
// 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 @@
}
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 @@
/**
* 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 @@
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 0359761..a841b7f 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 @@
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 @@
}
/**
- * 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 @@
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 @@
} 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 1ddd8f9..0000000
--- 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 20c4d5a..0000000
--- 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 79cd891..ebb957b 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_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 @@
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 4db81e2..0000000
--- 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 ff06db3..4f07bfe 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();
- @VisibleForTesting
- final StartingWindowRecordManager mWindowRecords = new StartingWindowRecordManager();
- // Windowless surface could co-exist with starting window in a task.
- @VisibleForTesting
- final StartingWindowRecordManager mWindowlessRecords = new StartingWindowRecordManager();
+ 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;
+
/**
* @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);
+ mSplashscreenContentDrawer = new SplashscreenContentDrawer(mContext, iconProvider, pool);
+ mSplashScreenExecutor.execute(() -> mChoreographer = Choreographer.getInstance());
+ mWindowManagerGlobal = WindowManagerGlobal.getInstance();
+ mDisplayManager.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);
+ @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 @@
*
* @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 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 @@
* @param taskId The Task id on which the splash screen was attached
*/
public void onAppSplashScreenViewRemoved(int taskId) {
- mSplashscreenWindowCreator.onAppSplashScreenViewRemoved(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, @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) {
+ removeWindowNoAnimate(taskId);
+ saveSplashScreenRecord(appToken, taskId, view, suggestType);
+ }
+ return shouldSaveView;
+ }
+
+ @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);
+ }
+
+ private void removeWindowNoAnimate(int taskId) {
+ mTmpRemovalInfo.taskId = taskId;
+ removeWindowSynced(mTmpRemovalInfo, true /* immediately */);
}
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);
+ final StartingWindowRecord record = mStartingWindowRecords.get(taskId);
+ if (record != null && record.mTaskSnapshotWindow != null
+ && record.mTaskSnapshotWindow.hasImeSurface()) {
+ removeWindowNoAnimate(taskId);
}
}
- static class WindowlessStartingWindow extends WindowlessWindowManager {
- SurfaceControl mChildSurface;
+ 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);
+ }
- 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();
}
- return mChildSurface;
- }
- }
- abstract static class StartingWindowRecord {
- protected int mBGColor;
- abstract void removeIfPossible(StartingWindowRemovalInfo info, boolean immediately);
- int getBGColor() {
- return mBGColor;
- }
- }
-
- 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);
+ 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);
+ }
+ }
+
+ 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 */);
+ }
+
+ /**
+ * 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 scheduleRemove(boolean deferRemoveForIme) {
- // Show the latest content as soon as possible for unlocking to home.
- if (mActivityType == ACTIVITY_TYPE_HOME) {
- removeImmediately();
+ private void setSplashScreenView(SplashScreenView splashScreenView) {
+ if (mSetSplashScreen) {
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);
+ mContentView = splashScreenView;
+ mSetSplashScreen = true;
}
- protected abstract boolean hasImeSurface();
-
- @CallSuper
- protected void removeImmediately() {
- mRemoveExecutor.removeCallbacks(mScheduledRunnable);
- }
- }
-
- static class StartingWindowRecordManager {
- private final StartingWindowRemovalInfo mTmpRemovalInfo = new StartingWindowRemovalInfo();
- private final SparseArray<StartingWindowRecord> mStartingWindowRecords =
- new SparseArray<>();
-
- 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);
+ 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;
}
- for (int i = taskSize - 1; i >= 0; --i) {
- removeWindow(taskIds[i], true);
+ if (a.getBoolean(R.styleable.Window_windowLightNavigationBar, false)) {
+ mSystemBarAppearance |= WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
}
+ a.recycle();
}
- void addRecord(int taskId, StartingWindowRecord record) {
- mStartingWindowRecords.put(taskId, record);
- }
-
- 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);
+ // 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;
}
- }
-
- void removeWindow(int taskId, boolean immediately) {
- mTmpRemovalInfo.taskId = taskId;
- removeWindow(mTmpRemovalInfo, immediately);
- }
-
- StartingWindowRecord getRecord(int taskId) {
- return mStartingWindowRecords.get(taskId);
- }
-
- @VisibleForTesting
- int recordSize() {
- return mStartingWindowRecords.size();
+ 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 bec4ba3..be2e793 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_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.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 @@
/**
* 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 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 c964df1..a05ed4f 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 @@
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 @@
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 @@
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 @@
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 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 @@
int backgroundColor = taskDescription.getBackgroundColor();
mBackgroundPaint.setColor(backgroundColor != 0 ? backgroundColor : WHITE);
mOrientationOnCreation = currentOrientation;
+ mActivityType = activityType;
mClearWindowHandler = clearWindowHandler;
mHasImeSurface = snapshot.hasImeSurface();
}
@@ -170,7 +186,23 @@
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 1445478..0000000
--- 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 12a0d40..0000000
--- 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 72fc8686..bb43d7c 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_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_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 @@
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 @@
+ "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 766c4cd..2aa6d12 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.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.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.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 @@
private final InputMonitorFactory mInputMonitorFactory;
private TaskOperations mTaskOperations;
+ private Optional<SplitScreenController> mSplitScreenController;
+
public DesktopModeWindowDecorViewModel(
Context context,
Handler mainHandler,
@@ -88,7 +94,8 @@
DisplayController displayController,
SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController,
- Optional<DesktopTasksController> desktopTasksController) {
+ Optional<DesktopTasksController> desktopTasksController,
+ Optional<SplitScreenController> splitScreenController) {
this(
context,
mainHandler,
@@ -98,6 +105,7 @@
syncQueue,
desktopModeController,
desktopTasksController,
+ splitScreenController,
new DesktopModeWindowDecoration.Factory(),
new InputMonitorFactory());
}
@@ -112,6 +120,7 @@
SyncTransactionQueue syncQueue,
Optional<DesktopModeController> desktopModeController,
Optional<DesktopTasksController> desktopTasksController,
+ Optional<SplitScreenController> splitScreenController,
DesktopModeWindowDecoration.Factory desktopModeWindowDecorFactory,
InputMonitorFactory inputMonitorFactory) {
mContext = context;
@@ -120,6 +129,7 @@
mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
mTaskOrganizer = taskOrganizer;
mDisplayController = displayController;
+ mSplitScreenController = splitScreenController;
mSyncQueue = syncQueue;
mDesktopModeController = desktopModeController;
mDesktopTasksController = desktopTasksController;
@@ -230,6 +240,15 @@
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 @@
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 @@
* @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 @@
}
// 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 @@
* 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 @@
return;
}
}
- focusedDecor.checkClickEvent(ev);
+ relevantDecor.checkClickEvent(ev);
break;
}
case MotionEvent.ACTION_CANCEL: {
@@ -491,6 +506,38 @@
}
@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 d6adaa7..b6696c7 100644
--- a/libs/WindowManager/Shell/tests/flicker/Android.bp
+++ b/libs/WindowManager/Shell/tests/flicker/Android.bp
@@ -41,6 +41,8 @@
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 65923ff..67ca9a1 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 aafd7ed..c5ee7b7 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 @@
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.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 bd18108..ed93045 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 @@
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 @@
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.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 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 e9c805e..983640a 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 996b677..bab81d7 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.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 @@
@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 7358da3..d0bca13 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 @@
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 1a0fbe4..bdfdad5 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 cf696c8..5e85eb8 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 9367a8a..8474ce0 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 @@
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 85a534c..62fa7b4 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
similarity index 92%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLockreenTest.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/bubble/OpenActivityFromBubbleOnLocksreenTest.kt
index 0b1382b..416315e 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 @@
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 @@
/**
* 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 @@
@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 50507bf..07ba4133 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 94147e8..6c61710 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 4be4dcd..29f76d0 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 7efbcdb..e323ebf 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 88cf15e..1045a5a 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 @@
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 @@
* ```
* 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 88542d5..2d2588e 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 @@
* 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 fb1eb01..02f6010 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 @@
@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 080e033..6c5a344 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 @@
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 @@
@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 f27fa4a..e540ad5 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 @@
* 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 fbada69..05262fe 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 @@
@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 47537c6..11bb0cc 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 @@
* 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 e478050..90f99c0 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 db50489..e079d547 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.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 @@
* 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 @@
@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 @@
@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 ec5f13c..5841666 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 @@
@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 3ef66d7..3272254 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 @@
@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 c3c705e..1f060e9 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 @@
* 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 b487ff4..4390f0b 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 @@
@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 f88f8d6..2001f48 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 @@
@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 d2fbb2a2..313631c 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 @@
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 @@
* 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 8b3755e..eccb85d 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 @@
@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 a9eb18d..93ffdd8d 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 @@
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 @@
* 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 39b1c82..6ab6a1f 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 @@
@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 d577b4f..7d5f740 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 @@
* 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 @@
@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 08db8ae..c096234 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 @@
@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 fcb8af4..0b73aac 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 @@
@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 30050bf..e064bf2 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 @@
@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 39ac49f..9c00744 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 @@
* 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 7db80a8..c23838a 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 @@
@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 be3bd60..d3d77d2 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 @@
@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 ef9920c..6f81116 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 77a8c3c..109354a 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 @@
@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 511a651..c8d5624 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 @@
* 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 e133443..85b2fbc 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 @@
@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 166416a..b30f308 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 @@
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 @@
): 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 3f5d067..3850c1f 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.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 @@
@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 @@
wmHelper
.StateSyncBuilder()
.withPipShown()
- .withRotation(PlatformConsts.Rotation.ROTATION_0)
+ .withRotation(Rotation.ROTATION_0)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
@@ -79,7 +79,7 @@
wmHelper
.StateSyncBuilder()
.withFullScreenApp(pipApp)
- .withRotation(PlatformConsts.Rotation.ROTATION_90)
+ .withRotation(Rotation.ROTATION_90)
.withNavOrTaskBarVisible()
.withStatusBarVisible()
.waitForAndVerify()
@@ -98,7 +98,7 @@
@Presubmit
@Test
fun displayEndsAt90Degrees() {
- flicker.assertWmEnd { hasRotation(PlatformConsts.Rotation.ROTATION_90) }
+ flicker.assertWmEnd { hasRotation(Rotation.ROTATION_90) }
}
@Presubmit
@@ -151,7 +151,7 @@
@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 720fe72..2cf8f61 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 @@
* 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 daf3e1b..b7a2c47 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 36909dd..000ae8f 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 dc1fe47..6104b7b 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.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 247403a..0c9c161 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 @@
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 d3c6820..1b55f39 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.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 @@
@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 b44b681..bd2ffc1 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 @@
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 514365f..7db5ecc 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 @@
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 4e36c36..ffdb87f 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 @@
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 @@
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 5d37e85..792e2b0 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 @@
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 @@
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 d086f7e..c1977e9 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 @@
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 @@
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 795a2c4..da80c6f 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 @@
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 @@
@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 a9cbb74..c453877 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 @@
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 8c0a303..7abdc06 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 4f8cfca..7901f75 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 @@
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.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 @@
// 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 c7b81d9..fbb7c71 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 @@
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 @@
.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 @@
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 940e0e9..d675bfb 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 @@
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 @@
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 8c3bea8..2855c71 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 @@
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 @@
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 06a1449..c29a917 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 @@
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 @@
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 193ab98..4c96b3a 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 @@
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 e8784d7..bc0d93a 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 @@
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 b6dbcf2..523cb66 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.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 @@
@Mock
private ShellExecutor mMainExecutor;
@Mock
- private SplitScreenController mSplitScreenController;
+ private WindowManager mWindowManager;
private DragAndDropController mController;
@@ -100,11 +99,6 @@
}
@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 0bb809d..2e2e49e 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 @@
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 bf62acf..11fda8b 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.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.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 @@
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 @@
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 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 @@
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 @@
// 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) {
+ 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.mWindowlessRecords.addRecord(taskId,
- new StartingSurfaceDrawer.StartingWindowRecord() {
- @Override
- public void removeIfPossible(StartingWindowRemovalInfo info,
- boolean immediately) {
-
- }
- });
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 @@
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 @@
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 3550721..1d1aa79 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.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 @@
@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 @@
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 0000000..a4b0127
--- /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 bcbe706..536bb49 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -332,6 +332,7 @@
"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 @@
"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 @@
"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 e2127ef..a18ba1c 100644
--- a/libs/hwui/DisplayListOps.in
+++ b/libs/hwui/DisplayListOps.in
@@ -52,4 +52,5 @@
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 2f0f7f5..41ced8c 100644
--- a/libs/hwui/MemoryPolicy.h
+++ b/libs/hwui/MemoryPolicy.h
@@ -53,8 +53,8 @@
// 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 0000000..e59bc95
--- /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 0000000..9836817
--- /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 659aec0..0b58406 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 @@
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 @@
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 @@
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::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 @@
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 @@
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 8409e13..1f4ba5d 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 @@
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 @@
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 @@
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 0000000..4d6adf5
--- /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 d0124f5..7a12769 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 @@
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 f2c286a..b785989 100644
--- a/libs/hwui/SkiaCanvas.h
+++ b/libs/hwui/SkiaCanvas.h
@@ -129,8 +129,7 @@
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 2a20191..44ee31d 100644
--- a/libs/hwui/hwui/Canvas.h
+++ b/libs/hwui/hwui/Canvas.h
@@ -16,18 +16,17 @@
#pragma once
-#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>
+#include <androidfw/ResourceTypes.h>
+#include <cutils/compiler.h>
+#include <utils/Functor.h>
+
+#include "Properties.h"
+#include "pipeline/skia/AnimatedDrawables.h"
+#include "utils/Macros.h"
class SkAnimatedImage;
enum class SkBlendMode;
@@ -35,6 +34,7 @@
class SkRRect;
class SkRuntimeShaderBuilder;
class SkVertices;
+class Mesh;
namespace minikin {
class Font;
@@ -227,7 +227,7 @@
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 6b983c1..24f9e82 100644
--- a/libs/hwui/jni/GraphicsJNI.h
+++ b/libs/hwui/jni/GraphicsJNI.h
@@ -46,10 +46,16 @@
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 @@
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 fc3d70b..c71e308 100644
--- a/libs/hwui/jni/Interpolator.cpp
+++ b/libs/hwui/jni/Interpolator.cpp
@@ -24,12 +24,8 @@
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 5e10b9d..3a35875 100644
--- a/libs/hwui/jni/JvmErrorReporter.h
+++ b/libs/hwui/jni/JvmErrorReporter.h
@@ -30,7 +30,10 @@
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 b13d9ba..0000000
--- 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 61c2260..0000000
--- 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 3694ce0..a5e0476 100644
--- a/libs/hwui/jni/Path.cpp
+++ b/libs/hwui/jni/Path.cpp
@@ -182,11 +182,7 @@
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 f99bef7..3dbe1a6 100644
--- a/libs/hwui/jni/PathEffect.cpp
+++ b/libs/hwui/jni/PathEffect.cpp
@@ -35,11 +35,7 @@
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 8a0db1c..75d45e5 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -52,12 +52,7 @@
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 @@
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 @@
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 @@
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 8a4d4e1..8ba7503 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 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 3e453e6..ae22213 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -49,7 +49,7 @@
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 @@
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 d6aad7d..6a7411f 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -94,12 +94,21 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
: 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 @@
}
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 cf6702e..ca667b0 100644
--- a/libs/hwui/jni/android_graphics_Matrix.cpp
+++ b/libs/hwui/jni/android_graphics_Matrix.cpp
@@ -23,8 +23,6 @@
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 0000000..04339dc
--- /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 fcfc4f8..af2d3b3 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 @@
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 @@
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 2a67734..7af31a4 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 @@
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 e1c8877..3ca7eeb 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.cpp
@@ -321,6 +321,11 @@
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 3fd8fa3..a8e4580 100644
--- a/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
+++ b/libs/hwui/pipeline/skia/SkiaRecordingCanvas.h
@@ -81,6 +81,7 @@
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 5a68fdc..31c1936 100644
--- a/packages/AppPredictionLib/Android.bp
+++ b/packages/AppPredictionLib/Android.bp
@@ -25,7 +25,7 @@
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 720e46d..e91d35b 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 89ce451..0e604ce 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.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 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 @@
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 @@
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 @@
}
RequestInfo.TYPE_GET -> UiState(
createCredentialUiState = null,
- getCredentialUiState = getCredentialInitialUiState()!!,
+ getCredentialUiState = getCredentialInitialUiState(originName)!!,
)
else -> throw IllegalStateException("Unrecognized request type: ${requestInfo.type}")
}
@@ -172,11 +181,11 @@
}
// 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 @@
)
}
- 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 @@
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 @@
" \"authenticatorSelection\": {\n" +
" \"residentKey\": \"required\",\n" +
" \"requireResidentKey\": true\n" +
- " }}"
+ " }}",
+ preferImmediatelyAvailableCredentials = true,
)
val credentialData = request.credentialData
return RequestInfo.newCreateRequestInfo(
@@ -428,19 +448,28 @@
}
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 a3e4c81..e8e3974 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -47,6 +47,12 @@
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 @@
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 a1e0823..9b7139c 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 @@
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 ccfc132..a834994 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -50,7 +50,11 @@
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 @@
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 @@
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 @@
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 @@
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 @@
)
} 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 6d07df7..2971433 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 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 b94840f..04a2c07 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.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 @@
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 @@
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 a622e07..cc73089 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 @@
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 @@
if (topAppBar != null) {
topAppBar()
}
- Column(
+ LazyColumn(
modifier = Modifier.padding(
start = 24.dp,
end = 24.dp,
@@ -63,6 +66,7 @@
).fillMaxWidth().wrapContentHeight(),
horizontalAlignment = Alignment.CenterHorizontally,
content = content,
+ verticalArrangement = contentVerticalArrangement,
)
}
}
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt b/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt
deleted file mode 100644
index 23ad53c..0000000
--- a/packages/CredentialManager/src/com/android/credentialmanager/common/ui/Columns.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.credentialmanager.common.ui
-
-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
-
-@Composable
-fun EntryListColumn(content: LazyListScope.() -> Unit) {
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(2.dp),
- content = content,
- )
-}
\ No newline at end of file
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 8f48f6b..c09a692 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 @@
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 8061da7..514ff90 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 @@
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 8f7c37e..8af729e 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 379b3e3..00b7d00 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.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.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.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 @@
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 @@
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 @@
}
)
}
- }
- item {
MoreOptionsDisabledProvidersRow(
disabledProviders = disabledProviderList,
onDisabledProvidersSelected = onDisabledProvidersSelected,
@@ -266,15 +279,17 @@
}
}
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 @@
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 @@
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 @@
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
+ 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
+ )
)
- )
- 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
- )
- },
- 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 @@
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
- )
- }
- )
- Divider(thickness = 24.dp, color = Color.Transparent)
- CredentialContainerCard {
- PrimaryCreateOptionRow(
- requestDisplayInfo = requestDisplayInfo,
- entryInfo = createOptionInfo,
- onOptionSelected = onOptionSelected
+ item {
+ HeadlineIcon(
+ bitmap = providerInfo.icon.toBitmap().asImageBitmap(),
+ tint = Color.Unspecified,
)
}
- Divider(thickness = 24.dp, color = Color.Transparent)
+ 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
+ )
+ }
+ }
+ item { Divider(thickness = 24.dp, color = Color.Transparent) }
var createOptionsSize = 0
var remoteEntry: RemoteInfo? = null
enabledProviderList.forEach { enabledProvider ->
@@ -450,29 +472,33 @@
} 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 @@
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 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 ce4e936..192fa15 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 @@
)
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 @@
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 54f8e5c..c5028c2 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.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 @@
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 @@
// 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,47 +255,45 @@
onNavigationIconClicked = if (isNoAccount) onCancel else onBackButtonClicked,
)
}) {
- LazyColumn {
- // For username
- items(sortedUserNameToCredentialEntryList) { item ->
- PerUserNameCredentials(
- perUserNameCredentialEntryList = item,
+ // For username
+ items(sortedUserNameToCredentialEntryList) { item ->
+ PerUserNameCredentials(
+ perUserNameCredentialEntryList = item,
+ onEntrySelected = onEntrySelected,
+ )
+ }
+ // Locked password manager
+ if (authenticationEntryList.isNotEmpty()) {
+ item {
+ LockedCredentials(
+ authenticationEntryList = authenticationEntryList,
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,
- )
- }
- }
+ }
+ // From another device
+ val remoteEntry = providerDisplayInfo.remoteEntry
+ if (remoteEntry != null) {
item {
- Divider(
- thickness = 1.dp,
- color = Color.LightGray,
- modifier = Modifier.padding(top = 16.dp)
+ RemoteEntryCard(
+ remoteEntry = remoteEntry,
+ onEntrySelected = onEntrySelected,
)
}
- // Manage sign-ins (action chips)
- item {
- ActionChips(
- providerInfoList = providerInfoList,
- 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 @@
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 02e7557..9727d3f 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 @@
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 @@
entrySubkey,
pendingIntent,
fillInIntent,
- shouldTerminateUiUponSuccessfulProviderResult = false,
+ shouldTerminateUiUponSuccessfulProviderResult = true,
)
data class RequestDisplayInfo(
val appName: String,
+ val preferImmediatelyAvailableCredentials: Boolean,
)
/**
@@ -246,7 +248,6 @@
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 b194738..c2aaeac 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 af20a4b..750c156 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -70,7 +70,7 @@
"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 0000000..091df26
--- /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 940f5c9..78a7408 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 42af999..643af75 100644
--- a/packages/SettingsLib/Spa/build.gradle
+++ b/packages/SettingsLib/Spa/build.gradle
@@ -24,7 +24,7 @@
}
}
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 bd7e7ff..53b24b0 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 724588f..3fdb1d1 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 @@
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 02962a5..2d956d5 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 @@
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 888b09f..e846480 100644
--- a/packages/SettingsLib/src/com/android/settingslib/Utils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/Utils.java
@@ -575,9 +575,15 @@
/** 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 2614644..688fc72 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 ComponentName settingsComponentName;
public CharSequence description;
public Drawable previewImage;
+ public boolean supportsComplications = false;
@Override
public String toString() {
@@ -175,6 +176,7 @@
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 6a1cee3..562d20d 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 @@
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 87e97b1..abbdaa7 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 @@
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 59d5674..6b81c1a 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 @@
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 @@
// 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 437c0d4..fc45e89 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 @@
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 1ac20471..346462d 100644
--- a/packages/SettingsProvider/Android.bp
+++ b/packages/SettingsProvider/Android.bp
@@ -48,6 +48,7 @@
"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 210a387..93394f3 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 @@
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 39cd24a..9657862 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 @@
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 5617331..7f3b0ff 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 @@
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 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 MemoryIntArray mBackingStore;
+ 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 574fd5a..11154d1 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 @@
"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 @@
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 @@
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 683c08e..ba275eb 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 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 @@
@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 @@
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 @@
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 @@
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 @@
return false;
}
+ @NonNull
private HashMap<String, String> getAllConfigFlags(@Nullable String prefix) {
if (DEBUG) {
Slog.v(LOG_TAG, "getAllConfigFlags() for " + prefix);
@@ -2316,7 +2315,8 @@
"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 @@
}
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 @@
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 @@
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 @@
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 c0176d5..278ceb9 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -322,6 +322,7 @@
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 @@
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 0000000..d34fe694
--- /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 596ff0e..90bec42 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 9d46961..b236ac5 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -449,6 +449,7 @@
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 40a5e97..c49a487 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 @@
* 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 9668066..3417ffd 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 @@
* - 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 b98b9221..ed8e705 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 @@
* 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 0e2d23b..6946e6b 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 @@
* 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 a96f893..b89a8b0 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 @@
/**
* Interpolate alpha for notification background scrim during shade expansion.
+ *
* @param fraction Shade expansion fraction
*/
@JvmStatic
@@ -16,6 +17,7 @@
/**
* 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 341784e..468a8b1 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 @@
* 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 @@
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 052888b..b5b6037 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 @@
/**
* 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 @@
* 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 @@
* 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 79bc2f4..89871fa7 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 @@
* 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 0000000..30e2a25
--- /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 254a6fb..84f7050 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 @@
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 a4b59fd..a5f832a 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 BadClass(
private val viewModel: ViewModel,
)
- """.trimIndent()
+ """
+ .trimIndent()
)
)
.issues(
@@ -98,7 +99,8 @@
class BadClass(
private val repository: Repository,
)
- """.trimIndent()
+ """
+ .trimIndent()
)
)
.issues(
@@ -136,7 +138,8 @@
private val interactor: Interactor,
private val viewmodel: ViewModel,
)
- """.trimIndent()
+ """
+ .trimIndent()
)
)
.issues(
@@ -176,7 +179,8 @@
class BadClass(
private val interactor: Interactor,
)
- """.trimIndent()
+ """
+ .trimIndent()
)
)
.issues(
@@ -207,7 +211,8 @@
data class Model(
private val name: String,
)
- """.trimIndent()
+ """
+ .trimIndent()
)
private val REPOSITORY_FILE =
TestFiles.kotlin(
@@ -228,7 +233,8 @@
return models
}
}
- """.trimIndent()
+ """
+ .trimIndent()
)
private val INTERACTOR_FILE =
TestFiles.kotlin(
@@ -245,7 +251,8 @@
return repository.getModels()
}
}
- """.trimIndent()
+ """
+ .trimIndent()
)
private val VIEW_MODEL_FILE =
TestFiles.kotlin(
@@ -262,7 +269,8 @@
return interactor.getModels().map { model -> model.name }
}
}
- """.trimIndent()
+ """
+ .trimIndent()
)
private val NON_CLEAN_ARCHITECTURE_FILE =
TestFiles.kotlin(
@@ -282,7 +290,8 @@
)
}
}
- """.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 0000000..3d6cbc7
--- /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 a02954a..08ab146 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 @@
* 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 @@
* 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 cfc38df..d4a81f9 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 @@
.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 edb10c7d..767756e 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 @@
* 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 eb9d625..a80a1f9 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 @@
/**
* 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 @@
/**
* 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 @@
// Constraint the content to be <= than the size of the pager.
.fillParentMaxHeight()
.wrapContentSize()
- ) { pagerScope.content(page) }
+ ) {
+ pagerScope.content(page)
+ }
}
}
} else {
@@ -272,7 +272,9 @@
// 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 2e6ae78..1822a68 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 @@
*
* @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 23122de..9814029 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 @@
/**
* 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 3eeadae..a74e56b 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 @@
*
* @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 3f590df..0484ff4 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 @@
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 ab36d58..00c0a0b 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 @@
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 @@
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 @@
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 2a40f5c..4df7a44 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 @@
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 @@
init {
if (seedColor != null) {
- currentColor = seedColor
+ currentColor = seedColor!!
}
- view.setColors(currentColor, currentColor)
+ view.setColors(DOZE_COLOR, currentColor)
}
override val events =
@@ -141,7 +141,7 @@
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 @@
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 c120876..0d88075 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 @@
*
* 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 ec860b5..18753fd 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 ab4aca5..babe5700 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 @@
/** 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 @@
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 @@
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 @@
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 e99b214..3e34885 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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 faf1b78..7a125ac 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 @@
* 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 68d7890..4773f54 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 @@
*
* @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
deleted file mode 100644
index 2e259c3..0000000
--- a/packages/SystemUI/res-keyguard/drawable-mdpi/ic_lockscreen_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png
deleted file mode 100644
index f4de96a..0000000
--- a/packages/SystemUI/res-keyguard/drawable-xhdpi/ic_lockscreen_sim.png
+++ /dev/null
Binary files differ
diff --git a/packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png b/packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png
similarity index 100%
rename from packages/SystemUI/res-keyguard/drawable-hdpi/ic_lockscreen_sim.png
rename to packages/SystemUI/res-keyguard/drawable/ic_lockscreen_sim.png
Binary files differ
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 4ff2967..0000000
--- 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 411fea5..48769fd 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 7db0fe9..728d861 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"
+ android:layout_gravity="center_horizontal"
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"
- />
- </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 422bd4c..7e24d12 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" />
+ 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">
- <RelativeLayout
- android:id="@+id/row0"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:paddingBottom="4dp"
- >
+ <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"
+ android:contentDescription="@string/keyguard_accessibility_sim_pin_area"
+ android:layout_gravity="center_horizontal"
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: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"
- />
- </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 9092877..fc108a5 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 0000000..2e29cae
--- /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 e08e63b..fa70303 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 aa211bf..71561c0 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" />
- </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>
+ android:paddingHorizontal="@dimen/controls_header_horizontal_padding"
+ android:layout_marginBottom="@dimen/controls_header_bottom_margin"
+ android:orientation="horizontal">
- <ScrollView
+ <!-- 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>
+
+ <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" />
- <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"
- />
+ </ScrollView>
+
+ <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 b1d3ed05..745cfc6 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 bc63c9f..4f38e60 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 7cd1470..59becc6 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 9ed9360..8583f05 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 8b41a44..9248d58 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 8f59df6..2086459 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 ac07d564..8972715 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 7b3caff..a988813 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 12e0b9a..c5979cc 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 @@
} 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 23742c5..454294f 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 @@
* 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 b927155..8690b36 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 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 @@
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 @@
// 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 77a13bd..751a3f8 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 @@
/** @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 7d39c4a..dd60647 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 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 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 @@
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 e0cf7b6..d8085b9 100644
--- a/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
+++ b/packages/SystemUI/src/com/android/keyguard/FaceAuthReason.kt
@@ -132,6 +132,7 @@
/**
* 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 baaef19..f8cb38d 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -44,7 +44,7 @@
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 d221e22..a010c9a 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.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 @@
// 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 92e3641..559db76 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinBasedInputViewController.java
@@ -71,13 +71,17 @@
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 @@
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 @@
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 b53b868..f4c5815 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -21,8 +21,6 @@
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 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
ConfigurationController configurationController,
DozeParameters dozeParameters,
- FeatureFlags featureFlags,
ScreenOffAnimationController screenOffAnimationController,
KeyguardLogger logger) {
super(keyguardStatusView);
@@ -73,8 +70,6 @@
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 bd67115..a4ec8e1 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.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 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.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.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.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.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 @@
}
}
+ 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 @@
}
}
+ 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 @@
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 @@
() -> 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 @@
}
};
+ 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 @@
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 @@
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 @@
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 @@
boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
&& shouldListenBouncerState && shouldListenUdfpsState
- && shouldListenSideFpsState
- && !isFingerprintLockedOut();
+ && shouldListenSideFpsState;
logListenerModelData(
new KeyguardFingerprintListenModel(
System.currentTimeMillis(),
@@ -2843,8 +2883,7 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
* 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 @@
cb.onUserSwitching(userId);
}
}
- try {
- reply.sendResult(null);
- } catch (RemoteException e) {
- mLogger.logException(e, "Ignored exception while userSwitching");
- }
+ latch.countDown();
}
/**
@@ -3963,13 +4001,7 @@
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 38f3e50..0d4889a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -214,7 +214,7 @@
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 @@
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 7e48193..a678edc 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.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 @@
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 @@
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 @@
// 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 b30a0e0..ad66909 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadAnimator.java
@@ -51,7 +51,6 @@
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 @@
}
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 3b0644e..7c7680a 100644
--- a/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
+++ b/packages/SystemUI/src/com/android/keyguard/NumPadKey.java
@@ -50,6 +50,7 @@
private int mDigit = -1;
private int mTextViewResId;
private PasswordTextView mTextView;
+ private boolean mAnimationsEnabled = true;
@Nullable
private NumPadAnimator mAnimator;
@@ -164,11 +165,11 @@
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 @@
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 8554e11..5400011 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.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 @@
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 @@
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 @@
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 c414c08..e53f6ad 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 @@
}, {"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 709ddf5..52312b8 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 @@
val iconContentDescription = getIconContentDescription(newState)
if (iconContentDescription != null) {
iconView.contentDescription = iconContentDescription
- iconViewOverlay.contentDescription = iconContentDescription
}
iconView.frame = 0
@@ -152,7 +151,7 @@
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 e698faf..08efd89 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.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 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 @@
}
/**
- * 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 fabc1c1..e16121d 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 @@
/**
* @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 @@
/**
* 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 ac6a22c..f7d87fc 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 @@
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 2d0d52e..231e7a4 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 @@
/**
* 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 28bc2b7..6854b50 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 @@
| 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 cef415c..98a3e4b 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 @@
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 a0b19dc..c0e1717 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 @@
/**
* 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 b3c18fb..3744344 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 @@
*
* 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 822190f..3555d0a 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 @@
)
/**
+ * 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 700f4f6..a171f43 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 @@
}
}
+ 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 @@
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 3e672f3..ae9c37a 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 @@
/** 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 f7e43a7..e51e832 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 @@
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 @@
// 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 bb2e2d7..06d4a08 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 @@
/**
* 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 @@
/**
* 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 3a3f9b4..bf0a692 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 @@
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 @@
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 0000000..d6cfb79
--- /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 9405c60..c61dad6 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.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.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.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 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 @@
private val controlsSettingsRepository: ControlsSettingsRepository,
private val authorizedPanelsRepository: AuthorizedPanelsRepository,
private val featureFlags: FeatureFlags,
+ private val dialogsFactory: ControlsDialogsFactory,
dumpManager: DumpManager
) : ControlsUiController, Dumpable {
@@ -122,6 +124,7 @@
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 @@
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 @@
}
}
+ @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 @@
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 @@
}
}
- 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 @@
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 @@
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 @@
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 @@
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 3b6ab20..78e87ca 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 @@
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 9921b1f..b86d419 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.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 @@
@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 84f83f1..45ff963 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 @@
*
* 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 0000000..1390b4d
--- /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 c3bd5d9..ca1cef3 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 @@
/**
* 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 f282aae..9374ad9 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 @@
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 @@
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 @@
mStateController = stateController;
mUiEventLogger = uiEventLogger;
mDreamOverlayCallbackController = dreamOverlayCallbackController;
+ mWindowTitle = windowTitle;
final ViewModelStore viewModelStore = new ViewModelStore();
final Complication.Host host =
@@ -277,7 +282,7 @@
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 e39073b..ff1f312 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 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 @@
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 @@
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 f598c36..f130026 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 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 @@
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 @@
@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 ab78b1b..febb45e 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 @@
// 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 @@
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 @@
@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 @@
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 @@
// 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 @@
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 0ca9115..57c4b36 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 @@
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 02bee3e..2ad1ab7 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.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 @@
private CentralSurfaces mCentralSurfaces;
- private boolean mUnocclusionTransitionFlagEnabled = false;
-
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -970,9 +966,6 @@
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 @@
@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 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- FeatureFlags featureFlags,
Lazy<ShadeController> shadeControllerLazy,
Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -1259,7 +1251,6 @@
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 98d3570..47ef0fa 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.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 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- FeatureFlags featureFlags,
Lazy<ShadeController> shadeController,
Lazy<NotificationShadeWindowController> notificationShadeWindowController,
Lazy<ActivityLaunchAnimator> activityLaunchAnimator,
@@ -144,7 +142,6 @@
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 be73f85..ef0c9a1 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 @@
.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 0066785..356a8fb 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 @@
// 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 baadc66..84abf57 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.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.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.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 @@
/** 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 @@
@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 0000000..adb1e01
--- /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 0e85347..86e5cd7 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 @@
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 @@
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 4a262f5..f27f899 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 @@
@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 0140529..eae40d6 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 @@
/**
* 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 @@
* 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 310f44d..e6568f2 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 @@
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 dfbe1c2..bc3c720 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.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 @@
* 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 @@
*
* @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 @@
)
}
- 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 0000000..fff7cfe
--- /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 0000000..a79513e
--- /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 ef3f242..8671753 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 @@
* @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 1e3b60c..ab9e6a4 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 @@
*
* @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 @@
previewMode.isInPreviewMode &&
previewMode.shouldHighlightSelectedAffordance &&
!isSelected,
+ forceInactive = previewMode.isInPreviewMode
)
}
.distinctUntilChanged()
@@ -198,6 +199,7 @@
isClickable: Boolean,
isSelected: Boolean,
isDimmed: Boolean,
+ forceInactive: Boolean,
): KeyguardQuickAffordanceViewModel {
return when (this) {
is KeyguardQuickAffordanceModel.Visible ->
@@ -213,7 +215,7 @@
)
},
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 d69ac7f..34a6740 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 @@
* 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 @@
* 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 f7349a2..647e3a1 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 @@
* 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 5acaa46..edc278d 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 @@
* 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 220993f..ca1ed1f 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 @@
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/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java
new file mode 100644
index 0000000..a2d381e
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/NotificationLockscreenLog.java
@@ -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.log.dagger;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+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 1712dab..29f273a 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 @@
*
* 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 @@
* - 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 @@
* 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 @@
* 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 @@
* 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 7ccc43c..06668d3 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 @@
*
* @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 a057c9f..2509f21 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 @@
/**
* 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 0b57175..ae03f27 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 @@
* 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 97717a6..207df6b 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 @@
* 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 af60e0e..72c4aab 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 @@
* 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 @@
/**
* 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 @@
* @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 @@
* [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 @@
}
/**
* 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 @@
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 @@
* 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 @@
* 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 @@
* 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 @@
* 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 6a512be..120704c 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 @@
* [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 878962d..a1d9214 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 @@
/**
* 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 @@
/**
* Callback representing that a media object [PlaybackState] has changed.
+ *
* @param key Media control unique identifier
* @param state The new [PlaybackState]
*/
@@ -77,6 +79,7 @@
/**
* 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 2d10b82..92e0c85 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.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 @@
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 @@
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 @@
/**
* 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 335ce1d..095cf09 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 @@
* 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 d2793bc..f5cc043 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 @@
* 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 4827a16..2b42604 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 @@
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 9f86cd8..3669493 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 @@
/**
* 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 6cf051a..680a8b6 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 @@
* @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 @@
*
* @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 @@
/**
* 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 66f12d6..7fc7bdb 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.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 @@
* 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 @@
*
* @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 @@
/**
* @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 @@
// 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 455b7de..be570b4 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 @@
* 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 b9b0459..0788e61 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 @@
* 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 @@
* 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 @@
*
* @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 c3fa76e..9bc66f6 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 @@
/** 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 720c44a..ee93c37 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 @@
*
* @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 7a77c47..01398cf 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 @@
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 d4991f9..9b9d561 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.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 @@
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 @@
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 88d5eaa..1c90154 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 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 @@
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 d5d7325..4db1da3 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.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 @@
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 3c17465..63d977e 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_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 @@
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 @@
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 @@
/** @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 @@
@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 @@
@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 f3d6014..342e0b00 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 @@
private boolean mLogGesture = false;
private boolean mInRejectedExclusion = false;
private boolean mIsOnLeftEdge;
+ private boolean mDeferSetIsOnLeftEdge;
private boolean mIsAttached;
private boolean mIsGesturalModeEnabled;
@@ -878,7 +879,9 @@
// 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 @@
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 @@
// 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 @@
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 be615d6..c65f0aa 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 @@
* 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 @@
* 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
+ }
- uiEvent?.let { uiEventLogger.log(it, noteTaskInfo.uid, noteTaskInfo.packageName) }
+ val info =
+ resolver.resolveInfo(
+ entryPoint = entryPoint,
+ isInMultiWindowMode = isInMultiWindowMode,
+ isKeyguardLocked = isKeyguardLocked,
+ )
+ ?: return
+
+ 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 @@
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 @@
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 e0bf1da..a256391 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 @@
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 0000000..acc537a
--- /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 0000000..16dd16e
--- /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 0000000..28d7647
--- /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 bd822d4..0f75f95 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 @@
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 d40bf2b..fb3c0cb 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 @@
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/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.kt
new file mode 100644
index 0000000..836e103f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskLaunchMode.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
+
+import android.content.Context
+import com.android.wm.shell.bubbles.Bubbles
+
+/**
+ * Supported ways for launching a note taking experience.
+ *
+ * @see [NoteTaskController.showNoteTask]
+ */
+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 b8800a2..ba8999c 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 @@
/** 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 @@
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 43869cc..30660c4 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.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 @@
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 7cb932a..2d63dbc 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.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 6ab0da6..8ced4646 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 @@
* 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 3ac5bfa..80fce6a 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.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 @@
super.onCreate(savedInstanceState)
noteTaskController.showNoteTask(
+ entryPoint = NoteTaskEntryPoint.WIDGET_PICKER_SHORTCUT,
isInMultiWindowMode = isInMultiWindowMode,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_SHORTCUT,
)
finish()
@@ -49,7 +49,7 @@
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 ff3ec72..d40112f 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 @@
@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 03bb7a0..8387c1d 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 @@
/**
* 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 fbf32b3..f170ac1 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 @@
* 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 645b125..346acf9 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 @@
.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 dd21be9..30509e2 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 @@
/**
* 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 310baaf..a8f99be 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 @@
/**
* @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 1b728b8..236213c 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 @@
/**
* @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 1946b8e..eda38e4 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.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 @@
// 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 b36f0d7..10e2afe 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.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 @@
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 @@
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 @@
return PixelFormat.TRANSLUCENT;
}
+ public void setShouldUseLargeScreenSize(boolean v) {
+ mShouldUseLargeScreenSize = v;
+ }
+
/**
* Corner radius used by either concave or convex corners.
*/
@@ -191,6 +202,10 @@
invalidateSelf();
}
+ public void setBottomEdgeRadius(float radius) {
+ mBottomEdgeRadius = radius;
+ }
+
@Override
public void draw(@NonNull Canvas canvas) {
mPaint.setColor(mMainColor);
@@ -198,9 +213,46 @@
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 f68e042..fc89a9e 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 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 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 @@
@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 @@
});
}
+ /**
+ * 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 287e810..33a3125 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 @@
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 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 3a5d0a7..0b2ae05 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 @@
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 @@
}
}
- 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 b502b4d..b1987c1 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 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.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 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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
NotificationStackSizeCalculator notificationStackSizeCalculator,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
ShadeTransitionController shadeTransitionController,
+ InteractionJankMonitor interactionJankMonitor,
SystemClock systemClock,
KeyguardBottomAreaViewModel keyguardBottomAreaViewModel,
KeyguardBottomAreaInteractor keyguardBottomAreaInteractor,
@@ -721,6 +752,7 @@
DumpManager dumpManager,
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor) {
+ mInteractionJankMonitor = interactionJankMonitor;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
public void onKeyguardFadingAwayChanged() {
@@ -816,6 +848,7 @@
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 @@
mNotificationPanelUnfoldAnimationController = unfoldComponent.map(
SysUIUnfoldComponent::getNotificationPanelUnfoldAnimationController);
- mUnocclusionTransitionFlagEnabled = featureFlags.isEnabled(Flags.UNOCCLUSION_TRANSITION);
updateUserSwitcherFlags();
mKeyguardBottomAreaViewModel = keyguardBottomAreaViewModel;
mKeyguardBottomAreaInteractor = keyguardBottomAreaInteractor;
@@ -1045,62 +1077,50 @@
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);
+ // 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);
+ // 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);
+ // 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);
+ // 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);
- }
+ // 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 @@
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 @@
// 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 @@
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 @@
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 @@
}
}
+ /**
+ * 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 @@
}
private void onExpandingFinished() {
- if (!mUnocclusionTransitionFlagEnabled) {
- mScrimController.onExpandingFinished();
- }
mNotificationStackScrollLayoutController.onExpansionStopped();
mHeadsUpManager.onExpandingFinished();
mConversationNotificationManager.onNotificationPanelExpandStateChanged(isFullyCollapsed());
@@ -4868,6 +4943,11 @@
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 @@
}
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 60fa865..87350b46 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.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 @@
NotificationInsetsController notificationInsetsController,
AmbientState ambientState,
PulsingGestureListener pulsingGestureListener,
- FeatureFlags featureFlags,
KeyguardBouncerViewModel keyguardBouncerViewModel,
KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
AlternateBouncerInteractor alternateBouncerInteractor,
@@ -165,10 +162,8 @@
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 f73dde6..7dff6ea 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.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 @@
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 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 099ad94..6857f4c 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.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.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.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.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 @@
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 @@
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 @@
|| 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 @@
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 @@
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 @@
}
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 a1767cc..f4b1cc5 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 @@
*
* @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 37773e9..b79f32a 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 @@
*
* [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 e9fac28..1cfb400 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 @@
@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 62c225b..df8c6ab 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 @@
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 42b874f..7297ae6 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 @@
/**
* @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 64b7ac9..5fa83ef 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 @@
* - 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 @@
* 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 @@
* 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 f395bea..82c5ee6 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 @@
}
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 0a5e986..11582d7 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.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.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 @@
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 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 @@
@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 @@
updateForegroundInstantApps();
}
- private final SynchronousUserSwitchObserver mUserSwitchListener =
- new SynchronousUserSwitchObserver() {
+ private final UserTracker.Callback mUserSwitchListener =
+ new UserTracker.Callback() {
@Override
- public void onUserSwitching(int newUserId) throws RemoteException {}
-
- @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 fc89be2..00d8c42 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 @@
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 @@
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 7e53d54..8874f59 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 @@
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 @@
// 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 4464531..88d9ffc 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 @@
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 @@
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 @@
}
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 a35617c..6deef2e 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 @@
/**
* 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 @@
* 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 1fccf82..0a9dddc 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 @@
/**
* 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 b483228..0000000
--- 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 6e5fcf4..0000000
--- 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 1a3927b..0000000
--- 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 1ca698b..0000000
--- 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 d9e3f8f..0000000
--- 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 034ab56..0000000
--- 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 ae19feb..9001470 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 @@
*/
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 0163dbe..9f45b9d 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 @@
@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 @@
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 2868116..92c5b63 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 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 aaf9300..c6f56d4 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 @@
|| (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 548d1a1..8b6d6a4f 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 @@
/**
* 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 9f38361..7855cdf 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 @@
}
@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 8dcfec7..9e62817 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 @@
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 17fb055..d6dc671 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.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.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 @@
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 @@
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 @@
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 @@
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 @@
WakefulnessLifecycle wakefulnessLifecycle,
SysuiStatusBarStateController statusBarStateController,
Optional<Bubbles> bubblesOptional,
+ Lazy<NoteTaskController> noteTaskControllerLazy,
DeviceProvisionedController deviceProvisionedController,
NavigationBarController navigationBarController,
AccessibilityFloatingMenuController accessibilityFloatingMenuController,
@@ -795,6 +828,7 @@
mWakefulnessLifecycle = wakefulnessLifecycle;
mStatusBarStateController = statusBarStateController;
mBubblesOptional = bubblesOptional;
+ mNoteTaskControllerLazy = noteTaskControllerLazy;
mDeviceProvisionedController = deviceProvisionedController;
mNavigationBarController = navigationBarController;
mAccessibilityFloatingMenuController = accessibilityFloatingMenuController;
@@ -852,9 +886,6 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
- * 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 @@
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 @@
} 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 @@
}
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 @@
}
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 6c532a5..e6b76ad 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.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 @@
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 @@
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 @@
@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 @@
mNextAlarmController = nextAlarmController;
mAlarmManager = alarmManager;
mUserInfoController = userInfoController;
- mIActivityManager = iActivityManager;
mUserManager = userManager;
mUserTracker = userTracker;
mDevicePolicyManager = devicePolicyManager;
@@ -208,6 +206,7 @@
mPrivacyItemController = privacyItemController;
mSensorPrivacyController = sensorPrivacyController;
mRecordingController = recordingController;
+ mMainExecutor = mainExecutor;
mUiBgExecutor = uiBgExecutor;
mTelecomManager = telecomManager;
mRingerModeTracker = ringerModeTracker;
@@ -256,11 +255,7 @@
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 @@
});
}
- 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 80093a3..fb8bf52 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 @@
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 @@
}
}
- /**
- * 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 @@
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 void onExpandingFinished() {
- setUnocclusionAnimationRunning(false);
- }
-
@VisibleForTesting
protected void onHideWallpaperTimeout() {
if (mState != ScrimState.AOD && mState != ScrimState.PULSING) {
@@ -875,13 +870,6 @@
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 a127139..66f5b65 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 @@
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 @@
.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 @@
// 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 3471a46..726b234 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 @@
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 8c82fba..f4e3eab 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 @@
* 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 b3d5b1e..53a208c 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 @@
* 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 7b0f952..4caf2b0 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 @@
* 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 24cd930..8e103f7 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 @@
* 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 e0e0ed7..b129617 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 @@
* 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 bdb656b..1e223b1 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 @@
*
* @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 @@
*
* @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 462504e..4e27ce6 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 @@
fun startListener() {
handler.post {
if (hasStarted) return@post
+ logDebug { "Listener has started." }
+
hasStarted = true
isInUsiSession =
inputManager.hasInputDevice {
@@ -116,6 +118,10 @@
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 @@
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 @@
if (!hasStarted) return
if (!inputDeviceAddressMap.contains(deviceId)) return
+ logDebug { "Stylus InputDevice removed: $deviceId" }
+
unregisterBatteryListener(deviceId)
val btAddress: String? = inputDeviceAddressMap[deviceId]
@@ -180,6 +189,11 @@
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 @@
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 @@
}
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 @@
}
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 @@
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 @@
// 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 @@
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 @@
}
}
- 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 @@
}
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 ec0a6e7..21b0efa 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.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 @@
inputDeviceId = deviceId
batteryCapacity = batteryState.capacity
+ logDebug {
+ "Updating notification battery state to $batteryCapacity " +
+ "for InputDevice $deviceId."
+ }
refresh()
}
}
@@ -125,12 +130,14 @@
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 @@
.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 @@
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 @@
}
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 @@
@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 60241a9..cf0184f 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 @@
* 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 01a81de..1612388 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 @@
* 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 @@
* 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 fe46318..125cc76 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 @@
* 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 2683971..981f429 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 @@
foldStateProvider.stop()
}
- override fun onHingeAngleUpdate(angle: Float) {}
-
override fun onFoldUpdate(@FoldUpdate update: Int) {
val now = clock.elapsedRealtime()
when (update) {
@@ -77,6 +75,10 @@
}
}
+ 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 5f89d5d..9304a46 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 @@
*
* @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 70523bb..a0b56aa 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 @@
@Background private val backgroundDispatcher: CoroutineDispatcher,
private val globalSettings: GlobalSettings,
private val tracker: UserTracker,
- private val activityManager: IActivityManager,
featureFlags: FeatureFlags,
) : UserRepository {
@@ -213,18 +209,18 @@
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 2f63f32..f026f0f 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 @@
* 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 082c8cc..13b3b1a 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 @@
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 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.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 @@
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 @@
}
@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 dfad15d..7144914 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 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 @@
@Mock
DozeParameters mDozeParameters;
@Mock
- FeatureFlags mFeatureFlags;
- @Mock
ScreenOffAnimationController mScreenOffAnimationController;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -83,7 +80,6 @@
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 09b738f..bd77c32 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 @@
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.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.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.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.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.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 @@
@Mock
private KeyguardUpdateMonitorLogger mKeyguardUpdateMonitorLogger;
@Mock
- private IActivityManager mActivityService;
- @Mock
private SessionTracker mSessionTracker;
@Mock
private UiEventLogger mUiEventLogger;
@@ -282,8 +279,6 @@
@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 @@
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 @@
@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 @@
strongAuthNotRequired();
// WHEN fingerprint is locked out
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
// THEN unlocking with face is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -697,7 +718,7 @@
strongAuthNotRequired();
// WHEN fingerprint is locked out
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
// THEN unlocking with fingerprint is not allowed
Assert.assertFalse(mKeyguardUpdateMonitor.isUnlockingWithBiometricAllowed(
@@ -721,7 +742,7 @@
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 @@
@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 @@
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 @@
}
@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 @@
@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 @@
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 @@
// 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 @@
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isTrue();
// Fingerprint is locked out.
- fingerprintErrorLockedOut();
+ fingerprintErrorTemporaryLockedOut();
assertThat(mKeyguardUpdateMonitor.shouldListenForFace()).isFalse();
}
@@ -2499,7 +2462,69 @@
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 @@
}
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 @@
mKeyguardUpdateMonitor.setSwitchingUser(true);
}
- private void fingerprintErrorLockedOut() {
+ private void fingerprintErrorTemporaryLockedOut() {
mKeyguardUpdateMonitor.mFingerprintAuthenticationCallback
.onAuthenticationError(FINGERPRINT_ERROR_LOCKOUT, "Fingerprint locked out");
}
@@ -2630,7 +2656,7 @@
any(),
mAuthenticationCallbackCaptor.capture(),
any(),
- anyInt());
+ any());
mAuthenticationCallbackCaptor.getValue()
.onAuthenticationSucceeded(
new FaceManager.AuthenticationResult(null, null, mCurrentUserId, false));
@@ -2741,6 +2767,30 @@
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 6333a68..3ec49b2 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 @@
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_0 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_90() =
@@ -362,7 +364,9 @@
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_90 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_180() =
@@ -370,7 +374,9 @@
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_180 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarCollapsedDownForXAlignedSensor_180() =
@@ -379,7 +385,9 @@
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForSmallNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_180() =
@@ -388,7 +396,9 @@
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForLargeNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_270() =
@@ -396,7 +406,9 @@
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 @@
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 @@
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 @@
isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_90 },
windowInsets = insetsForSmallNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun hidesSfpsIndicatorWhenOccludingTaskbarForXAlignedSensor_InReverseDefaultRotation_90() =
@@ -430,7 +448,9 @@
isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_90 },
windowInsets = insetsForLargeNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForXAlignedSensor_InReverseDefaultRotation_180() =
@@ -438,7 +458,9 @@
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 @@
deviceConfig = DeviceConfig.X_ALIGNED,
isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_270 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_0() =
@@ -454,7 +478,9 @@
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_0 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_90() =
@@ -462,7 +488,9 @@
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_90 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_180() =
@@ -480,7 +508,9 @@
deviceConfig = DeviceConfig.Y_ALIGNED,
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_270 }
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarCollapsedDownForYAlignedSensor_270() =
@@ -489,7 +519,9 @@
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_270 },
windowInsets = insetsForSmallNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_270() =
@@ -498,7 +530,9 @@
isReverseDefaultRotation = false,
{ rotation = Surface.ROTATION_270 },
windowInsets = insetsForLargeNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_0() =
@@ -506,7 +540,9 @@
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 @@
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 @@
isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForSmallNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = true)
+ }
@Test
fun hidesSfpsIndicatorWhenOccludingTaskbarForYAlignedSensor_InReverseDefaultRotation_180() =
@@ -542,7 +582,9 @@
isReverseDefaultRotation = true,
{ rotation = Surface.ROTATION_180 },
windowInsets = insetsForLargeNavbar()
- ) { verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false) }
+ ) {
+ verifySfpsIndicatorVisibilityOnTaskbarUpdate(sfpsViewVisible = false)
+ }
@Test
fun showsSfpsIndicatorWithTaskbarForYAlignedSensor_InReverseDefaultRotation_270() =
@@ -550,7 +592,9 @@
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 e35b2a3..28e80057 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.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.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.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 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(authorizedPanelsRepository.getAuthorizedPanels()).thenReturn(setOf())
`when`(userTracker.userHandle).thenReturn(UserHandle.of(user))
delayableExecutor = FakeExecutor(FakeSystemClock())
@@ -945,6 +947,28 @@
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 b91a3fd..7ac1953 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 @@
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 0c9986d..5a613aa 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 @@
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 0000000..1e8cd41
--- /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 aa90e2a..23faa99 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.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.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 @@
@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 @@
underTest =
ControlsUiControllerImpl(
- Lazy { controlsController },
+ { controlsController },
context,
packageManager,
uiExecutor,
bgExecutor,
- Lazy { controlsListingController },
+ { controlsListingController },
controlActionCoordinator,
activityStarter,
iconCache,
@@ -142,7 +144,8 @@
controlsSettingsRepository,
authorizedPanelsRepository,
featureFlags,
- dumpManager
+ ControlsDialogsFactory { fakeDialogController.dialog },
+ dumpManager,
)
`when`(
userFileManager.getSharedPreferences(
@@ -410,8 +413,45 @@
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 dbaf94f..483ab3b 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 @@
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 @@
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 0000000..8203291
--- /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 9e70c27..aa17d49 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 @@
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 @@
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 ef62abf..175da0b 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.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 @@
@Mock
private View mBcSmartspaceView;
+ @Mock
+ private FeatureFlags mFeatureFlags;
+
private Monitor mMonitor;
private final Set<Condition> mPreconditions = new HashSet<>();
@@ -73,6 +78,8 @@
public void setup() {
MockitoAnnotations.initMocks(this);
mMonitor = SelfExecutingMonitor.createInstance();
+
+ when(mFeatureFlags.isEnabled(Flags.HIDE_SMARTSPACE_ON_DREAM_OVERLAY)).thenReturn(false);
}
/**
@@ -85,12 +92,22 @@
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 @@
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 7c20e3c..c93e677 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.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.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 @@
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 @@
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 21ad5e2..5dc04f7 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 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.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.verify
import org.mockito.MockitoAnnotations
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
@@ -78,6 +83,7 @@
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 @@
testDispatcher = StandardTestDispatcher()
testScope = TestScope(testDispatcher)
userRepository = FakeUserRepository()
+ devicePostureRepository = FakeDevicePostureRepository()
}
private suspend fun createBiometricSettingsRepository() {
@@ -108,6 +115,7 @@
looper = testableLooper!!.looper,
dumpManager = dumpManager,
biometricManager = biometricManager,
+ devicePostureRepository = devicePostureRepository,
)
testScope.runCurrent()
}
@@ -299,6 +307,50 @@
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 0000000..bd6b7a8
--- /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 d2db910..f9493d1 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 @@
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 @@
)
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 8bd8be5..c727b3a 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 @@
TestConfig(
isVisible = true,
isClickable = false,
- isActivated = true,
+ isActivated = false,
icon = icon,
canShowWhileLocked = false,
intent = Intent("action"),
@@ -363,7 +363,7 @@
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 8c54da1..ab0669a 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 @@
@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 @@
}
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 @@
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 @@
@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 @@
}
@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 @@
)
// 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 @@
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 136ace1..4dfa626 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.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 @@
@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 @@
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 @@
tunerService,
resumeBrowserFactory,
dumpManager,
- clock
+ clock,
+ mediaFlags,
)
resumeListener.setManager(mediaDataManager)
mediaDataManager.addListener(resumeListener)
@@ -188,7 +192,8 @@
tunerService,
resumeBrowserFactory,
dumpManager,
- clock
+ clock,
+ mediaFlags,
)
listener.setManager(mediaDataManager)
verify(broadcastDispatcher, never())
@@ -244,6 +249,32 @@
}
@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 @@
tunerService,
resumeBrowserFactory,
dumpManager,
- clock
+ clock,
+ mediaFlags,
)
resumeListener.setManager(mediaDataManager)
mediaDataManager.addListener(resumeListener)
@@ -421,7 +453,8 @@
tunerService,
resumeBrowserFactory,
dumpManager,
- clock
+ clock,
+ mediaFlags,
)
resumeListener.setManager(mediaDataManager)
mediaDataManager.addListener(resumeListener)
@@ -463,7 +496,8 @@
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 4fc9ca7..85e8d07 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 @@
context,
appPackageName = null,
isReceiver = false,
- ) {
- }
+ ) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -86,8 +85,7 @@
context,
appPackageName = null,
isReceiver = true,
- ) {
- }
+ ) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -119,8 +117,7 @@
context,
appPackageName = "fakePackageName",
isReceiver = false,
- ) {
- }
+ ) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -135,8 +132,7 @@
context,
appPackageName = "fakePackageName",
isReceiver = true,
- ) {
- }
+ ) {}
assertThat(iconInfo.isAppIcon).isFalse()
assertThat(iconInfo.contentDescription.loadContentDescription(context))
@@ -154,7 +150,9 @@
context,
appPackageName = "fakePackageName",
isReceiver = false
- ) { exceptionTriggered = true }
+ ) {
+ exceptionTriggered = true
+ }
assertThat(exceptionTriggered).isTrue()
}
@@ -167,7 +165,9 @@
context,
appPackageName = "fakePackageName",
isReceiver = true
- ) { exceptionTriggered = true }
+ ) {
+ exceptionTriggered = true
+ }
assertThat(exceptionTriggered).isTrue()
}
@@ -179,8 +179,7 @@
context,
PACKAGE_NAME,
isReceiver = false,
- ) {
- }
+ ) {}
assertThat(iconInfo.isAppIcon).isTrue()
assertThat(iconInfo.icon).isEqualTo(MediaTttIcon.Loaded(appIconFromPackageName))
@@ -194,8 +193,7 @@
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 2212bbd..89405c1 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 @@
@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 @@
@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 @@
@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 39c4e06..376b7cc 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 @@
@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)
}
+ @Test
+ fun onBubbleExpandChanged_collapsing_logNoteTaskClosed() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = false, isInMultiWindowMode = false)
+
+ createNoteTaskController()
+ .apply { infoReference.set(expectedInfo) }
+ .onBubbleExpandChanged(
+ isExpanding = false,
+ key = Bubble.KEY_APP_BUBBLE,
+ )
+
+ 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 onBubbleExpandChanged_notExpandingAndKeyguardLocked_doNothing() {
+ val expectedInfo = noteTaskInfo.copy(isKeyguardLocked = true, isInMultiWindowMode = false)
+
+ createNoteTaskController()
+ .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() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(true)
+ val expectedInfo =
+ noteTaskInfo.copy(
+ entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
+ isInMultiWindowMode = false,
+ isKeyguardLocked = true,
+ )
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
+ whenever(resolver.resolveInfo(any(), any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
.showNoteTask(
- isInMultiWindowMode = false,
- uiEvent = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE,
+ 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_KEYGUARD_QUICK_AFFORDANCE,
- NOTES_UID,
- NOTES_PACKAGE_NAME
- )
}
@Test
- fun showNoteTask_keyguardIsUnlocked_shouldStartBubblesAndLogUiEvent() {
- 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 = ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
+ 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()
}
- verify(uiEventLogger)
- .log(
- ShowNoteTaskUiEvent.NOTE_OPENED_VIA_STYLUS_TAIL_BUTTON,
- NOTES_UID,
- NOTES_PACKAGE_NAME
- )
- }
-
- @Test
- fun showNoteTask_keyguardIsUnlocked_uiEventIsNull_shouldStartBubblesWithoutLoggingUiEvent() {
- whenever(keyguardManager.isKeyguardLocked).thenReturn(false)
-
- createNoteTaskController().showNoteTask(isInMultiWindowMode = false, uiEvent = null)
-
- 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.`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(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 @@
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 @@
}
// 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 0000000..a4df346
--- /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 d6495d8..7f64f8a 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 @@
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 @@
@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 @@
@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 0000000..7e975b6
--- /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 53720ff..46e0278 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 @@
@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 @@
@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 @@
// 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)
+ fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
- 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)
-
- 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 cdc683f..e57d0d9 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.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 @@
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 030c59f..5e0190b 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.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 @@
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 b4a66297..f1e3e8a 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.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 @@
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 95e7ad9f..a5c0004 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.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 @@
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 bf172f1..75fd000 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.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 @@
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 cfbb82f..4193854 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.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 @@
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 18f891c..64fd09d5 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.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 @@
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 fdb63ca..13c30e9 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.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 @@
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 60c1a33..ff27e02 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.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 @@
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 ce62f2d..b048643 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.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 @@
)
}
+ @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 e0b3125..b51c378 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.verify
import org.mockito.Mockito.verifyZeroInteractions
import java.util.Optional
+import org.junit.After
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -129,6 +130,12 @@
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 ce5edb1..6c0904e 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 @@
@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 71d8aa4..7d41aa6 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.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 @@
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 c7aba1a..692a644 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.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 @@
)
}
+ @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 9d62cfd..bd99cd4 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.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 @@
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 4a2ac96..959e750 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.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 @@
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 abd9094..adfd7f7 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.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 @@
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 08d10fd..33921c7 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.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 @@
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 1ab601c..e2f64b2 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.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 @@
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 9638a45..c7dae83 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.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 @@
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 188c3a3..04af69c 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.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 @@
@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 @@
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 3344a17..652c138 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.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 @@
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 24287ea..3125d45 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.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 @@
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 4722c8d..596df78 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.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 @@
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 99e5564..7913628 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.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 @@
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true)
@SmallTest
-@Ignore("b/269171747")
public class ReduceBrightColorsTileTest extends SysuiTestCase {
@Mock
private QSHost mHost;
@@ -99,6 +98,12 @@
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 c7eb2f1..5b94cfe 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.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 @@
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 21acc08..5aef758 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.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 @@
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 3d9f650..b556571 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.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 @@
)
}
+ @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 0000000..4f469f7
--- /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 8f6653f..d86ff67 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.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
- );
}
- @After
- public void tearDown() {
- mNotificationPanelViewController.cancelHeightAnimator();
- mMainHandler.removeCallbacksAndMessages(null);
+ /**
+ * 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);
+ }
+
+ /**
+ * 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 @@
.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 @@
}
@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 @@
}
@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 @@
}
@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 @@
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 0000000..0c046e9
--- /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 d229a08..0a401b0 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.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.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 @@
@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 @@
.thenReturn(keyguardBouncerComponent)
whenever(keyguardBouncerComponent.securityContainerController)
.thenReturn(keyguardSecurityContainerController)
+ whenever(keyguardTransitionInteractor.lockscreenToDreamingTransition)
+ .thenReturn(emptyFlow<TransitionStep>())
underTest = NotificationShadeWindowViewController(
lockscreenShadeTransitionController,
FalsingCollectorFake(),
@@ -143,7 +144,6 @@
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 5e9c219..5d71979 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.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.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 @@
@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 @@
when(mDockManager.isDocked()).thenReturn(false);
+ when(mKeyguardTransitionInteractor.getLockscreenToDreamingTransition())
+ .thenReturn(emptyFlow());
+
mController = new NotificationShadeWindowViewController(
mLockscreenShadeTransitionController,
new FalsingCollectorFake(),
@@ -144,7 +147,6 @@
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 7a74b12..56fc0c7 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 @@
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 @@
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 26eff61..1fdb364 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 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 @@
@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 @@
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 cd2efc0..7fa27f3 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 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.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 @@
@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 @@
@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 bea2cfb..bedb2b3 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 @@
.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 @@
.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 @@
.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 @@
.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 0000000..7a67796
--- /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 0000000..95591a4
--- /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 a6a9e51..0000000
--- 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 5cee9e3..0000000
--- 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 5b8305d..89c399b 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 @@
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 680a323..78da782 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 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 @@
}
@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)).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_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)).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 ec294b1..68d67ca 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 @@
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 38e8228..c4ee326 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.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.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.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.PrintWriter;
import java.util.Optional;
-import dagger.Lazy;
-
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper(setAsMainLooper = true)
@@ -259,6 +262,7 @@
@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 @@
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 @@
mWakefulnessLifecycle,
mStatusBarStateController,
Optional.of(mBubbles),
+ () -> mNoteTaskController,
mDeviceProvisionedController,
mNavigationBarController,
mAccessibilityFloatingMenuController,
@@ -852,6 +861,50 @@
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 @@
}
@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 305b9fe..6b18169 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 @@
@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 @@
commandQueue,
broadcastDispatcher,
executor,
+ executor,
testableLooper.looper,
context.resources,
castController,
@@ -190,7 +189,6 @@
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 c0537a6..dc5a047 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 @@
}
@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 @@
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 @@
@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 1aad83e..158e9ad 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 @@
}
@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 00ce412..b072dee 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 @@
* 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 5288608..0413d92 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_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 @@
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 @@
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 @@
{ 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 @@
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 @@
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 6086e16..8476d0d 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.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 @@
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 @@
override fun onFoldUpdate(update: Int) {
foldUpdates.add(update)
}
+
+ override fun onUnfoldedScreenAvailable() {
+ unfoldedScreenAvailabilityUpdates.add(Unit)
+ }
})
foldStateProvider.start()
@@ -156,8 +162,8 @@
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 @@
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 @@
fireScreenOnEvent()
- assertThat(foldUpdates).containsExactly(FOLD_UPDATE_UNFOLDED_SCREEN_AVAILABLE)
+ assertThat(unfoldedScreenAvailabilityUpdates).hasSize(1)
}
@Test
@@ -277,7 +284,7 @@
@Test
fun startClosingEvent_afterTimeout_finishHalfOpenEventEmitted() {
- sendHingeAngleEvent(90)
+ setInitialHingeAngle(90)
sendHingeAngleEvent(80)
simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS)
@@ -288,7 +295,7 @@
@Test
fun startClosingEvent_beforeTimeout_abortNotEmitted() {
- sendHingeAngleEvent(90)
+ setInitialHingeAngle(90)
sendHingeAngleEvent(80)
simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
@@ -298,7 +305,7 @@
@Test
fun startClosingEvent_eventBeforeTimeout_oneEventEmitted() {
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(90)
simulateTimeout(HALF_OPENED_TIMEOUT_MILLIS - 1)
@@ -309,7 +316,7 @@
@Test
fun startClosingEvent_timeoutAfterTimeoutRescheduled_finishHalfOpenStateEmitted() {
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(90)
// The timeout should not trigger here.
@@ -323,7 +330,7 @@
@Test
fun startClosingEvent_shortTimeBetween_emitsOnlyOneEvents() {
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(90)
sendHingeAngleEvent(80)
@@ -334,20 +341,19 @@
@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 @@
@Test
fun startClosingEvent_whileActivityTypeNotAvailable_triggerBeforeThreshold() {
setupForegroundActivityType(isHomeActivity = null)
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
@@ -367,7 +373,7 @@
@Test
fun startClosingEvent_whileOnLauncher_doesTriggerBeforeThreshold() {
setupForegroundActivityType(isHomeActivity = true)
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
@@ -377,9 +383,11 @@
@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 @@
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 @@
@Test
fun startClosingEvent_whileKeyguardStateNotAvailable_triggerBeforeThreshold() {
setKeyguardVisibility(visible = null)
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
@@ -408,7 +416,7 @@
@Test
fun startClosingEvent_whileonKeyguard_doesTriggerBeforeThreshold() {
setKeyguardVisibility(visible = true)
- sendHingeAngleEvent(180)
+ setInitialHingeAngle(180)
sendHingeAngleEvent(START_CLOSING_ON_APPS_THRESHOLD_DEGREES + 1)
@@ -418,9 +426,59 @@
@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 @@
}
}
- private fun simulateFolding(startAngle: Int) {
- sendHingeAngleEvent(startAngle)
- sendHingeAngleEvent(startAngle - 1)
- }
-
private fun setFoldState(folded: Boolean) {
foldProvider.notifyFolded(folded)
}
@@ -521,6 +574,17 @@
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 a064e8c..fbb0e5a 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 @@
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 9312643..e2f3cf7 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.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 @@
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 @@
}
@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 @@
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 9b4f496..c2947b4 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 @@
/**
* 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 01dac36..d4b1701 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.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.flowOf
class FakeBiometricSettingsRepository : BiometricSettingsRepository {
@@ -42,6 +43,9 @@
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 0000000..914c786
--- /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 251014f..4242c16 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.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 @@
_userId = _userInfo.id
_userHandle = UserHandle.of(_userId)
+ onUserChanging()
+ onUserChanged()
+ }
+
+ fun onUserChanging(userId: Int = _userId) {
val copy = callbacks.toList()
- copy.forEach {
- it.onUserChanging(_userId, userContext)
- it.onUserChanged(_userId, userContext)
- }
+ val latch = CountDownLatch(copy.size)
+ copy.forEach { it.onUserChanging(userId, userContext, latch) }
+ }
+
+ fun onUserChanged(userId: Int = _userId) {
+ val copy = callbacks.toList()
+ 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 0000000..0c9ce0f
--- /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 4622464..c437e5c 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 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 @@
}
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 @@
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 6ffbe5a..d19b414 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_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 @@
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 @@
}
}
+ 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 97c9ba9..82fd225 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 @@
@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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
/** 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 c7a8bf3..0af372f 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 @@
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 @@
[
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 @@
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 b7bab3e..f9751d9 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 @@
/**
* 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 1f8a779..7fba72b 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 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 @@
@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 @@
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 094053e..0e25a06 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 @@
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 945d43b..b19a502 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.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 @@
* 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 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 9d91d10..e258de1 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.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 com.android.server.wm.WindowManagerInternal;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
import java.util.List;
/**
@@ -42,6 +45,9 @@
* 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 @@
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 @@
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 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 @@
break;
}
}
+ if (DEBUG) {
+ Slog.v(LOG_TAG, "At least one proxy can retrieve windows: " + observingWindows);
+ }
return observingWindows;
}
@@ -205,6 +226,14 @@
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 @@
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 @@
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 21b51b1..607439b 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 @@
*/
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 @@
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 7804ebf..864fe0f 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.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.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 @@
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 @@
private final SensorManagerInternal mSensorManagerInternal;
private final VirtualDeviceManagerInternal mVdmInternal;
-
-
public SensorController(@NonNull Object lock, int virtualDeviceId,
@Nullable IVirtualSensorCallback virtualSensorCallback) {
mLock = lock;
@@ -97,7 +101,7 @@
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 @@
}
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 f650560..ee1b1fd 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 @@
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 @@
mDisplayManager = displayManager;
if (inputController == null) {
mInputController = new InputController(
- mVirtualDeviceLock,
context.getMainThreadHandler(),
context.getSystemService(WindowManager.class));
} else {
@@ -281,7 +288,7 @@
* 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 92e322f..e9a7f20 100644
--- a/services/core/java/com/android/server/BatteryService.java
+++ b/services/core/java/com/android/server/BatteryService.java
@@ -192,20 +192,21 @@
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 @@
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mPowerConnectedOptions);
+ mPowerOptions);
}
});
}
@@ -648,7 +649,7 @@
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mPowerDisconnectedOptions);
+ mPowerOptions);
}
});
}
@@ -662,7 +663,7 @@
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mBatteryLowOptions);
+ mBatteryOptions);
}
});
} else if (mSentLowBatteryBroadcast &&
@@ -675,7 +676,7 @@
@Override
public void run() {
mContext.sendBroadcastAsUser(statusIntent, UserHandle.ALL, null,
- mBatteryOkayOptions);
+ mBatteryOptions);
}
});
}
@@ -1210,6 +1211,11 @@
}
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 @@
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 @@
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 @@
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 725ea5c..19e5cb1 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.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 @@
// 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 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 3f1ad3a..2a46d86 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.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 @@
* 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 @@
}
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 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
/**
* 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 @@
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 c16314b..225afea 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.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 @@
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 @@
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 70304c5..c1850bd 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 @@
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 @@
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 4a134ee..a8054de 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 @@
}
@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 @@
// 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 @@
}
/**
- * 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 @@
(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 @@
}
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 @@
}
} else {
registeredReceivers = mReceiverResolver.queryIntent(snapshot, intent,
- resolvedType, false /*defaultOnly*/, callingUid, userId);
+ resolvedType, false /*defaultOnly*/, userId);
}
}
BroadcastQueue.traceEnd(cookie);
@@ -18209,8 +18158,9 @@
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 72d6ca9..4c1835e 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.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 @@
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 @@
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 @@
}
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 01466b8..9be553c 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 @@
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 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 463a2f8..16219cd 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 @@
* 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 b942f4b..841b61e 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 @@
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 @@
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 f721d69..48df1494 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 @@
}
/**
- * 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 1534ff5..50841ae 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 @@
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 0c36626..d05301a 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 @@
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 c6a8bcd..d4bcd9e 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 88c0c7f..0d0e576 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 @@
// persistent data
initVolumeGroupStates();
- mSoundDoseHelper.initSafeUsbMediaVolumeIndex();
+ mSoundDoseHelper.initSafeMediaVolumeIndex();
// Link VGS on VSS
initVolumeStreamStates();
@@ -8358,6 +8358,7 @@
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 @@
} 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 @@
} 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 @@
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 @@
/*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 4e8e704..cf81dbe 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 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 @@
// 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 @@
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 @@
// 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;
+ // For now using the same value for CSD supported devices
+ private float mSafeMediaVolumeDbfs;
- // 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));
+ 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
}
/*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 @@
/*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 @@
@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 @@
/*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 @@
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 @@
/*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 @@
/*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 @@
}
/*package*/ boolean safeDevicesContains(int device) {
- return mSafeMediaVolumeDevices.contains(device);
+ return mSafeMediaVolumeDevices.containsKey(device);
}
/*package*/ void invalidatPendingVolumeCommand() {
@@ -551,6 +631,15 @@
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 @@
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 @@
/*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,36 +686,71 @@
}
}
- private void initCsd() {
- if (mEnableCsd) {
- Log.v(TAG, "Initializing sound dose");
+ private void updateDoseAttenuation(int newIndex, int device, int streamType,
+ boolean isAbsoluteVolume) {
+ if (!mEnableCsd) {
+ return;
+ }
- synchronized (mCsdStateLock) {
- if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
- mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
- }
+ final ISoundDose soundDose = mSoundDose.get();
+ if (soundDose == null) {
+ Log.w(TAG, "Can not apply attenuation. ISoundDose itf is null.");
+ return;
+ }
- 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);
- }
- }
+ try {
+ if (!isAbsoluteVolume) {
+ // remove any possible previous attenuation
+ soundDose.updateAttenuation(/* attenuationDB= */0.f, device);
+
+ return;
}
- reset();
+ 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) {
+ return;
+ }
+
+ Log.v(TAG, "Initializing sound dose");
+
+ synchronized (mCsdStateLock) {
+ if (mGlobalTimeOffsetInSecs == GLOBAL_TIME_OFFSET_UNINITIALIZED) {
+ mGlobalTimeOffsetInSecs = System.currentTimeMillis() / 1000L;
+ }
+
+ 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) {
synchronized (mSafeMediaVolumeStateLock) {
int mcc = mContext.getResources().getConfiguration().mcc;
@@ -629,7 +758,7 @@
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 @@
// 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 @@
/*obj=*/null), /*delay=*/0);
}
}
-
- if (mEnableCsd) {
- initCsd();
- }
}
private int getTimeoutMsForWarning(@AudioManager.CsdWarning int csdWarning) {
@@ -719,25 +844,32 @@
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 cdf22aa..69ad152 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 @@
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 @@
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 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 b0b23fa..f09d192 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 @@
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 cf8ea86..1a53fec 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 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 0f22296..1e33c96 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 @@
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 d21a750..89a17c6 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 @@
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 8a33f22..f6c1375 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 @@
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 c315ccf..ff9127f 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 @@
@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 a833278..23b6f84 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 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 @@
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 99c491a..9e6f4e4 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 @@
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 5e7cf35..8b61f59 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 @@
@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 3a4aaa7..1f82961 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.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.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 @@
// 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 @@
@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 @@
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 6e1640d..22b6a53 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.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 @@
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 @@
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 @@
/**
* 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 @@
* 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 @@
}
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 @@
}
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 @@
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 @@
if (!mInjector.isBrightnessModeAutomatic(mContentResolver)
|| !mInjector.isInteractive(mContext)
|| mColorSamplingEnabled
- || mBrightnessConfiguration == null
- || !mBrightnessConfiguration.shouldCollectColorSamples()) {
+ || !mShouldCollectColorSample) {
return;
}
@@ -997,7 +995,7 @@
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 @@
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 @@
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 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 99e709e..7b560ce 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.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 d9b3501..75f8acc 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 @@
return mAmbientDarkeningPercentagesIdle;
}
- SensorData getAmbientLightSensor() {
+ public SensorData getAmbientLightSensor() {
return mAmbientLightSensor;
}
@@ -2821,7 +2821,7 @@
/**
* 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 6eb465e..214c591 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.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 9917bfc..da8e6a6 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 @@
// 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 @@
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
if (mBrightnessTracker != null) {
- mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null
+ && mBrightnessConfiguration.shouldCollectColorSamples());
}
boolean updateScreenBrightnessSetting = false;
@@ -1823,7 +1824,7 @@
userInitiatedChange = false;
}
notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
- hadUserBrightnessPoint);
+ wasShortTermModelActive);
}
// We save the brightness info *after* the brightness setting has been changed and
@@ -1865,7 +1866,7 @@
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 @@
}
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 @@
? 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 f49419cf..a2a53e3 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 @@
// 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 @@
: AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED);
if (mBrightnessTracker != null) {
- mBrightnessTracker.setBrightnessConfiguration(mBrightnessConfiguration);
+ mBrightnessTracker.setShouldCollectColorSample(mBrightnessConfiguration != null
+ && mBrightnessConfiguration.shouldCollectColorSamples());
}
boolean updateScreenBrightnessSetting = false;
@@ -1537,7 +1538,7 @@
userInitiatedChange = false;
}
notifyBrightnessTrackerChanged(brightnessState, userInitiatedChange,
- hadUserBrightnessPoint);
+ wasShortTermModelActive);
}
// We save the brightness info *after* the brightness setting has been changed and
@@ -1579,7 +1580,7 @@
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 @@
}
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 @@
? 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 8f52c97..58c5034 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.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 fc90db6..78c597e 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.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 3e67f0a..2ce7690 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 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
similarity index 98%
rename from services/core/java/com/android/server/display/DisplayModeDirector.java
rename to services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 31f5ab7..24d5ca4 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.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 @@
mUdfpsObserver.observe();
}
+ /**
+ * Enables or disables component logging
+ */
public void setLoggingEnabled(boolean loggingEnabled) {
if (mLoggingEnabled == loggingEnabled) {
return;
@@ -1574,7 +1578,10 @@
}
}
- 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 @@
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 @@
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 @@
*/
@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 @@
return mLowAmbientBrightnessThresholds;
}
- public void observe(SensorManager sensorManager) {
+ private void observe(SensorManager sensorManager) {
mSensorManager = sensorManager;
mBrightness = getBrightness(Display.DEFAULT_DISPLAY);
@@ -2064,11 +2074,11 @@
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 @@
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 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 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 void dumpLocked(PrintWriter pw) {
+ void dumpLocked(PrintWriter pw) {
pw.println(" BrightnessObserver");
pw.println(" mAmbientLux: " + mAmbientLux);
pw.println(" mBrightness: " + mBrightness);
@@ -2400,7 +2411,7 @@
}
@VisibleForTesting
- public void setDefaultDisplayState(int state) {
+ void setDefaultDisplayState(int state) {
if (mLoggingEnabled) {
Slog.d(TAG, "setDefaultDisplayState: mDefaultDisplayState = "
+ mDefaultDisplayState + ", state = " + state);
@@ -2475,7 +2486,7 @@
}
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 @@
}
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 @@
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 @@
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 @@
}
}
}
- } 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 @@
}
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 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 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 3bc4b54..3e2efdd 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 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 3563938..c0deb3f 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 @@
}
if (mCecController == null) {
Slog.i(TAG, "Device does not support HDMI-CEC.");
+ return;
}
if (mMhlController == null) {
mMhlController = HdmiMhlControllerStub.create(this);
@@ -713,9 +714,6 @@
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 0ea64ab..8c7658e 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 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 @@
@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 f142293..d397a0c 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 @@
}
@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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 559eb53..17536fc 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 @@
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 0000000..de429f0
--- /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 99d30f5..e5f5897 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 @@
// 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 @@
Binder.restoreCallingIdentity(token);
}
} finally {
- logMetric(atomRecordForMetrics);
+ logAppLocalesMetric(atomRecordForMetrics);
}
}
@@ -355,32 +355,30 @@
}
/**
- * 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 @@
// 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 @@
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 @@
return;
}
- 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);
-
- // 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();
- }
-
- final long token = Binder.clearCallingIdentity();
+ AppSupportedLocalesChangedAtomRecord atomRecord = new AppSupportedLocalesChangedAtomRecord(
+ Binder.getCallingUid());
try {
- setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig);
- } finally {
- Binder.restoreCallingIdentity(token);
- }
+ requireNonNull(appPackageName);
- //TODO: Add metrics to monitor the usage by applications
+ //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, null, atomRecord)) {
+ enforceSetAppSpecificLocaleConfigPermission(atomRecord);
+ }
+
+ final long token = Binder.clearCallingIdentity();
+ try {
+ setOverrideLocaleConfigUnchecked(appPackageName, userId, localeConfig, atomRecord);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ } finally {
+ logAppSupportedLocalesChangedMetric(atomRecord);
+ }
}
+
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 @@
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 @@
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 @@
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 @@
}
}
- 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 @@
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 4013468..5a832b7 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 @@
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 @@
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 @@
@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 @@
}
@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 @@
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 @@
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 @@
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 e592a22..d070b41 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 @@
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 0000000..58fdb57
--- /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 08691d0..d4a1184 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 @@
*/
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 @@
}
@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 0000000..8bd6416
--- /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 7979d2a..e31a7fc 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.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 @@
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 76ff19f..638e81a 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.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 @@
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 @@
@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 @@
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 @@
}
});
- 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 @@
// 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 @@
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 @@
}
}
- 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 @@
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 @@
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 faa06f7..9bca9f0 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 @@
"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 @@
@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 fac7748..c232b36 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 @@
@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 21f661b..5984360 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
{@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 @@
|| !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 @@
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 @@
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 @@
| 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 @@
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 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 ee8f560..90d89c6 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.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 @@
}
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 0fd81ac..a9d4115 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 @@
@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 @@
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 @@
@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 e53e756..3923890 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 @@
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 f338137..0a90e7a3 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 @@
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 @@
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 a42e78b..de5f0c4 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 @@
@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 9036d4c..928ffa7 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.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 @@
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 @@
? 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 @@
}
// 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 @@
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 41592bd..586e112 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 @@
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 8bca4a9..a13c568 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 @@
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 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 @@
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 @@
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 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 6d5e2b0..67639fb 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 @@
@NonNull List<ShortcutInfo> changedShortcuts) {
Preconditions.checkArgument(newShortcut.isEnabled(),
"pushDynamicShortcuts() cannot publish disabled shortcuts");
+ ensureShortcutCountBeforePush();
newShortcut.addFlags(ShortcutInfo.FLAG_DYNAMIC);
@@ -437,7 +438,7 @@
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 @@
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 @@
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 @@
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 @@
};
/**
+ * 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 b7c3b97..372b3bb 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 @@
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 @@
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 @@
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 @@
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 @@
}
/**
+ * 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 @@
@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 07d36b3..18eebe4 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.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.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.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 @@
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 @@
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 @@
+ 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 977fab1..fac681a 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 @@
}
}
- 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 @@
}
// 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 @@
}
@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 @@
}
@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 @@
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 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 @@
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 @@
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 @@
}
@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 static class ActivityIntentResolver
- extends MimeGroupsAwareIntentResolver<ParsedActivity> {
+ extends MimeGroupsAwareIntentResolver<Pair<ParsedActivity, ParsedIntentInfo>, ResolveInfo> {
@NonNull
private UserNeedsBadgingCache mUserNeedsBadging;
@@ -1049,6 +969,53 @@
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 @@
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 @@
}
@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 @@
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 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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
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 @@
}
@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 @@
}
@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 @@
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 @@
}
@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 7f88660..b8e4c8d 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 @@
@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 @@
@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 @@
@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 6899924..9115775 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 @@
@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 @@
@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 @@
@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 5bfb135..0c84f4c 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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 @@
@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 f308931..45dc49d 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 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.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 @@
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 @@
ActivityTaskManagerInternal mActivityTaskManagerInternal;
AutofillManagerInternal mAutofillManagerInternal;
InputManagerInternal mInputManagerInternal;
- InputMethodManagerInternal mInputMethodManagerInternal;
DreamManagerInternal mDreamManagerInternal;
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
@@ -539,6 +544,8 @@
boolean mWakeOnAssistKeyPress;
boolean mWakeOnBackKeyPress;
long mWakeUpToLastStateTimeout;
+ int mSearchKeyBehavior;
+ ComponentName mSearchKeyTargetActivity;
private boolean mHandleVolumeKeysInWM;
@@ -659,6 +666,7 @@
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 @@
case MSG_SCREENSHOT_CHORD:
handleScreenShot(msg.arg1);
break;
+ case MSG_SWITCH_KEYBOARD_LAYOUT:
+ handleSwitchKeyboardLayout(msg.arg1, msg.arg2);
+ break;
}
}
}
@@ -1025,14 +1036,8 @@
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 @@
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 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
+ 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 41c2fbf..6c32ec2 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 @@
* @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 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 9790659..1baa0a6 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 @@
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 @@
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 91c4a2f..1f7af41 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 @@
// 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 32dac49..ac07248 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 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.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_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 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 @@
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 @@
// 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 @@
/* 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 @@
.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 @@
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 @@
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 @@
? "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 37634f8..df471c5 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 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 @@
// 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 @@
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 @@
// 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 @@
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 587138d..8fc3797 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 @@
/** 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 25ce569..210a7d9 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 @@
}
}
+ 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 97e0b1e..ce9bff8 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 @@
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 cc81d52..0a833f4 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 @@
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 b131365..3a30e4b 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.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 @@
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 @@
}
}
- /**
- * 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 e2bdcdd..2ce86ad 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -179,6 +179,7 @@
"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 ed79352..7e0bb68 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 @@
static void getThermalHalLocked();
static std::mutex gThermalHalMutex;
-static sp<IThermal> gThermalHal = nullptr;
+static sp<hardware::thermal::V1_0::IThermal> gThermalHidlHal = nullptr;
+static std::shared_ptr<IThermal> gThermalAidlHal = 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();
- }
+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 @@
// 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 @@
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 @@
});
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 @@
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 @@
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 @@
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 @@
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 356e9a9..a916b64 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 @@
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 @@
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 @@
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 @@
}
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 @@
.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 @@
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 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 @@
{"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 @@
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 8c94b0a..6498b6a 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 @@
/** 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 @@
/**
* 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 3976a70..be7feb5 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 05a8b11..07ddda3 100644
--- a/services/tests/InputMethodSystemServerTests/Android.bp
+++ b/services/tests/InputMethodSystemServerTests/Android.bp
@@ -52,6 +52,10 @@
"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 92be780..1371934 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 0000000..7cbfc52
--- /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 dbdffd0..9501b96 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 @@
mockitoSession()
.initMocks(this)
.strictness(Strictness.LENIENT)
- .mockStatic(LocalServices.class)
+ .spyStatic(LocalServices.class)
.mockStatic(ServiceManager.class)
.mockStatic(SystemServerInitThreadPool.class)
.startMocking();
@@ -212,6 +212,7 @@
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 @@
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 0000000..111cabd
--- /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 95c2ed2..99da415 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 @@
}
/**
- * 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 95a5884..5f82ec1 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.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 3f5d113..06ba5dd 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 @@
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 @@
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 503e579..96eca71 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 @@
@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 @@
@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 @@
@SmallTest
@Test
- public void testUnRegisterProxyWithoutPermission() throws Exception {
+ public void testUnRegisterProxyWithoutA11yPermission() {
doThrow(SecurityException.class).when(mMockSecurityPolicy)
.enforceCallingOrSelfPermission(Manifest.permission.MANAGE_ACCESSIBILITY);
@@ -339,6 +351,17 @@
@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 1298e7b..24b003c 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.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.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 @@
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 @@
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 @@
@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 cf650d2..e9d8269 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.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.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.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.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 @@
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 @@
}
+ @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 @@
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 f0d8616..5806443 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 @@
}
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 760ed9b..7642e7b 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 @@
// 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 6431e88..1259d71 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 @@
@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 @@
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 9910a80..09a84da 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 @@
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 @@
.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 800f60b..ffe2fec 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.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 6def7b1..8981160 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 @@
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 @@
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
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
rename to services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
index 7e6eeee..f256c8a 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_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.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 @@
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 @@
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 @@
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 9686c38..1c33d0d 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 @@
}
@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 0000000..24ed42c
--- /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 5cdaf0d..b693974 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.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.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 @@
@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 @@
() -> 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 @@
void tearDown() {
mHandlerThread.quitSafely();
+ LocalServices.removeServiceForTest(InputMethodManagerInternal.class);
mMockitoSession.finishMocking();
}
@@ -417,7 +423,13 @@
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 d7e4c55..169586e 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 @@
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 fafe90a..323894ca 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 @@
}
@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 @@
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 1c0c71b..a3fe6f2 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 0dcf8ce..b1cd994 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 @@
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.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 libcore.io.IoUtils;
+import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -56,7 +59,7 @@
*/
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
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");
+ 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);
+ }
+
+ if (DEBUG) {
+ logByteArray("Input " + cableNumber
+ + " after conversion ", convertedArray, 0,
+ convertedArray.length);
+ }
+
+ if (convertedArray.length == 0) {
+ continue;
+ }
+
+ if ((outputReceivers == null)
+ || (outputReceivers[portStartFinal + cableNumber]
+ == null)) {
+ Log.w(TAG, "outputReceivers is null");
+ keepGoing = false;
break;
}
- convertedArray =
- mUsbMidiPacketConverter.usbMidiToRawMidi(
- inputBuffer, bytesRead);
- }
+ outputReceivers[portStartFinal + cableNumber].send(
+ convertedArray, 0, convertedArray.length,
+ timestamp);
- if (DEBUG) {
- logByteArray("Input 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();
+ // 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 @@
};
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];
+ 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 thread " + portFinal) {
+ 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;
- }
-
- if (DEBUG) {
- logByteArray("Output before conversion ", event.data, 0,
- event.count);
- }
-
- byte[] convertedArray;
- 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);
- } else {
- if (mUsbMidiPacketConverter == null) {
- Log.w(TAG, "mUsbMidiPacketConverter 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 (Thread.currentThread().interrupted()) {
+ Log.d(TAG, "output thread interrupted");
+ break;
+ }
+
+ byte[] convertedArray = new byte[0];
+ if (mIsUniversalMidiDevice) {
+ convertedArray = midi2ByteStream.toByteArray();
+ midi2ByteStream.reset();
+ } else {
convertedArray =
- mUsbMidiPacketConverter.rawMidiToUsbMidi(
- event.data, event.count, portFinal);
+ packetConverter.pullEncodedMidiPackets();
}
if (DEBUG) {
@@ -520,7 +610,6 @@
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 @@
}
}
}
- 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 @@
};
newThread.start();
mThreads.add(newThread);
- portNumber++;
+ portStartNumber += cableCountFinal;
}
}
@@ -667,11 +754,21 @@
}
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 @@
mUsbDeviceConnections = null;
mInputUsbEndpoints = null;
mOutputUsbEndpoints = null;
-
- mUsbMidiPacketConverter = null;
+ mInputUsbEndpointCableCounts = null;
+ mOutputUsbEndpointCableCounts = null;
mIsOpen = false;
}
@@ -788,4 +885,19 @@
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 56bb236..65d7a41 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
} 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 @@
}
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 1f448ac..117a3d9 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 @@
mClassSpecificEndpointDescriptor = descriptor;
}
- UsbDescriptor getClassSpecificEndpointDescriptor() {
+ public UsbDescriptor getClassSpecificEndpointDescriptor() {
return mClassSpecificEndpointDescriptor;
}
diff --git a/services/voiceinteraction/TEST_MAPPING b/services/voiceinteraction/TEST_MAPPING
index d6c6964..5fe1c8d 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 ce1157e..7d5750e 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_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 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 @@
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 @@
}
}
+ 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 de99ebf..9170117 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -2073,6 +2073,14 @@
* {@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 c352f2b..abcce5f 100644
--- a/telephony/java/android/telephony/DomainSelectionService.java
+++ b/telephony/java/android/telephony/DomainSelectionService.java
@@ -537,9 +537,9 @@
}
@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 @@
}
@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 b9008c4..788d0e8 100644
--- a/telephony/java/android/telephony/PhoneNumberUtils.java
+++ b/telephony/java/android/telephony/PhoneNumberUtils.java
@@ -1526,7 +1526,7 @@
* 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 @@
* 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 d396790..04752e4 100644
--- a/telephony/java/android/telephony/TransportSelectorCallback.java
+++ b/telephony/java/android/telephony/TransportSelectorCallback.java
@@ -35,8 +35,10 @@
/**
* 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 b3682ca..f9c2620 100644
--- a/telephony/java/android/telephony/WwanSelectorCallback.java
+++ b/telephony/java/android/telephony/WwanSelectorCallback.java
@@ -46,6 +46,7 @@
* 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 c352266..64454cc 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -166,8 +166,8 @@
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 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 @@
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 5dc1a65..d93ee21 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -31,7 +31,6 @@
* 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 @@
* 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 @@
*
* @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 @@
* 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 e24e892e..d966868 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatelliteListener.aidl
@@ -36,10 +36,10 @@
/**
* 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 df51432..711dcbe 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -63,19 +63,19 @@
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 @@
* 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 @@
* 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 @@
*
* @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 @@
* 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 5ee7f9a..e4f9413 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteModemState.aidl
@@ -42,6 +42,10 @@
*/
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 aca83f4..6777256d 100644
--- a/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/ITransportSelectorCallback.aidl
@@ -22,7 +22,7 @@
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 339fbee..94e7c87 100644
--- a/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl
+++ b/telephony/java/com/android/internal/telephony/IWwanSelectorCallback.aidl
@@ -21,6 +21,6 @@
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 0000000..39550a3
--- /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 855d3c1..fef5211 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -42,6 +42,8 @@
"androidx.test.ext.junit",
"flickertestapplib",
"flickerlib",
+ "flickerlib-apphelpers",
+ "flickerlib-helpers",
"truth-prebuilt",
"launcher-helper-lib",
"launcher-aosp-tapl",
@@ -65,6 +67,7 @@
],
static_libs: [
"flickerlib",
+ "flickerlib-helpers",
"truth-prebuilt",
"app-helpers-core",
],
@@ -80,8 +83,10 @@
"**/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 707b522..f2ffc19 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 87bfdeb..4e3ae0c 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 @@
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 e3dc699..9dc4bf0 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 @@
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 @@
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 @@
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 b7bdeeb7..7ef4d93 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 7f2e057f..ed17059 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 20259a7..c3cbb84 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 @@
@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 dba588a..5dc2dd7 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 86f52d5..9fa84019 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 4d2b86a..b042a14 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 4707642..136995a 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 4d72729..c4628aa 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 65d0fa9..e531bc0 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.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 73018a0..afb9fbf 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 18563ff..7aea05d 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 @@
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 @@
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 cdf7ae5..47dd4e9 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 2ae8e1d..9227e07 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 f5aed41..79c048a 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 a433b15..73effbd 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 fb0242e..a6e57d5 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 fb04b32..d61a500 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 @@
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 8a25e36..fb5e1d2 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 ba8dabd..b95d86b 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 d6ed24a..ab91685 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 ae42232..e93e9c8 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 @@
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 5c1eca3..c547ad0 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 58da2d8..20ee3b9 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 d7f0830..78f8bcf 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 0c8589d..0e852b6 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 @@
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 @@
// 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 @@
// 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 @@
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 8f54000..06e668e 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 61dabfc..94c90da 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 9ed9d28e..64af811 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 8f7049a..316766a 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 092a4fd..c23cf34 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 @@
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 @@
@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 0870cec..823328a 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 @@
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 @@
@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 96b23bc..0fe52df 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 48dbf25..a4e4b6f 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 @@
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 ed5d309..5aacb30 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 7b935ff..e85da1f 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 @@
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 0a89991..eb81aed 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 1a0c959..1b4d6ad 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 @@
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 37e8c6b..db1440b 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 0b7b165..e2d6dbf 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 @@
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 @@
@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 116bc1b..405ab6b 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 dcffa47..579c10f 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 defb437..3a8db45 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 @@
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 89d37db..1fee20d 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 @@
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 @@
}
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 @@
@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 307821f..3aca2a0 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 c72e4e4..6179fc2 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 @@
@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 167689c..954f589 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 @@
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 @@
@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 82c38a3..0f57467 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 6c6003f..a927102 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 e7cfb9e..7514c9b 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 @@
@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 e7ecb87..194c86b 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 851651e..d133529 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 @@
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 @@
@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 0c53155..f5b2294 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 6058212..99b9bd2bfc 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 @@
@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 5d96346..9ea12a9 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 @@
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 9308fbb..fc39713 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 7979cf9b..e8f9aa3 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 549b929..8b89a8b 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 0942287..549183f 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 9d86f8c8..19ecf6a 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 @@
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 @@
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 9fbec97..8fdbb64 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 2f0e56f..1a1d403 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 fb6fb22..63ffee6 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.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 32276d6..a221ef6 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.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 ff39611..4efee55 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 @@
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 @@
@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 aa054a9..730f78f 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 @@
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 0ed3bba..9c16b79 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 @@
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 af6c81d..4a9507a 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 @@
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 2b16ef0..00d7544 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 @@
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 @@
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 1c979e8..ff24190 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 55e7a99..9ab6156 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 @@
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 @@
@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 618fb8a..e0db96f 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 93bf099..cdd2d45 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 @@
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 8d2af38..6005a81 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 c78d0e9..da98523 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.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 @@
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 @@
)
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 94afd81..dd9e4cf 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.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 b7faf83..78cee3c 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 @@
@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 e6cdd1e..f970a79 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 @@
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 25d9753..2b69e9b 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 6294761..cd7d6fa 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 @@
@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 aa9adf0..9f48cda 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 @@
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 b40ecac..b0d4e27 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 c03cd29..63299cb 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 @@
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 @@
@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 8b21603..af671df 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 @@
@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 e7e39c6..4a4180b 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 @@
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 6420f79..0e6b20f 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 74ecdde..3c0bbd6 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 1a69344..17b3b2b 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 @@
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 @@
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 @@
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 0ebbf4e..b236d87 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 9b1262d..75e35ee 100644
--- a/tests/FlickerTests/test-apps/flickerapp/Android.bp
+++ b/tests/FlickerTests/test-apps/flickerapp/Android.bp
@@ -41,6 +41,7 @@
"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 c7e5a5e..e59071b 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.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.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 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 0000000..254770d
--- /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 0000000..0ee1b449
--- /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 0000000..9320f0a
--- /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 0000000..af273a6
--- /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 0000000..60416a8
--- /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 0000000..1659cc0
--- /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 7a8d949..97398dc 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 @@
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 da53387..0cc18d6 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 @@
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 @@
// 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 @@
// 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 @@
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 2d6c664..6f4d11c 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.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 @@
// 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 @@
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 cf4186d..e722ba5 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 @@
// 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 61d4095..be3ed71 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SharedBufferModeTests.kt
@@ -17,7 +17,7 @@
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 @@
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 6383da5..cf4cb8c 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.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 @@
}
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 @@
}
fun withTrace(predicate: () -> Unit): LayersTrace {
- return withSFTracing(TRACE_FLAGS,
- outputDir = instrumentation.targetContext.dataDir.toPath()) {
+ return withSFTracing(TRACE_FLAGS) {
predicate()
}
}
@@ -84,8 +84,7 @@
}
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 @@
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 093c312..bba9678 100644
--- a/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
+++ b/tests/SurfaceViewBufferTests/src/com/android/test/SurfaceViewBufferTestBase.kt
@@ -18,6 +18,8 @@
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 @@
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 @@
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 722e671..6f4f7b1 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 @@
@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 @@
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 @@
// 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 @@
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 0000000..ad701e5
--- /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 161c83c..1fb1c63 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.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 @@
@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 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 @@
}
/**
- * 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 @@
}
}
- 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 @@
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 @@
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 @@
/**
* 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 @@
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 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 @@
*/
@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 @@
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 af4fd4a..7b591d3 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.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 @@
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 @@
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 @@
}
/**
- * 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 @@
@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 @@
mNetworkName = networkName;
mHotspotSsid = hotspotSsid;
mHotspotBssid = hotspotBssid;
- mHotspotSecurityTypes = hotspotSecurityTypes;
+ mHotspotSecurityTypes = new ArraySet<>(hotspotSecurityTypes);
}
/**
@@ -293,11 +291,11 @@
/**
* 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 @@
&& 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 @@
dest.writeString(mNetworkName);
dest.writeString(mHotspotSsid);
dest.writeString(mHotspotBssid);
- dest.writeIntArray(mHotspotSecurityTypes);
+ dest.writeArraySet(mHotspotSecurityTypes);
}
/**
@@ -345,7 +343,7 @@
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 @@
.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 f8f0700..e6595eb 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 @@
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 org.junit.Test;
/**
- * Unit tests for {@link android.app.sharedconnectivity.DeviceInfo}.
+ * Unit tests for {@link DeviceInfo}.
*/
@SmallTest
public class DeviceInfoTest {
@@ -63,8 +62,8 @@
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 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 @@
@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 37dca8d..8a0f21e 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.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 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 @@
.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 @@
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 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 @@
@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 @@
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 266afcc..872dd2e 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.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 @@
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 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 @@
@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 cdb438f..7c0a8b6 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.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.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executor;
@@ -110,7 +108,7 @@
public void resourcesNotDefined() {
when(mResources.getString(anyInt())).thenThrow(new Resources.NotFoundException());
- assertNull(SharedConnectivityManager.create(mContext));
+ assertThat(SharedConnectivityManager.create(mContext)).isNull();
}
/**
@@ -183,7 +181,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
@@ -191,7 +189,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(mService);
- assertFalse(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
@@ -201,7 +199,7 @@
manager.registerCallback(mExecutor, mClientCallback);
- assertTrue(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isTrue();
verify(mService).unregisterCallback(any());
}
@@ -213,7 +211,7 @@
manager.registerCallback(mExecutor, mClientCallback);
manager.unregisterCallback(mClientCallback);
- assertFalse(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
@@ -224,7 +222,7 @@
manager.registerCallback(mExecutor, mClientCallback);
manager.unregisterCallback(mClientCallback);
- assertFalse(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
@Test
@@ -234,7 +232,7 @@
doThrow(new RemoteException()).when(mService).unregisterCallback(any());
- assertFalse(manager.unregisterCallback(mClientCallback));
+ assertThat(manager.unregisterCallback(mClientCallback)).isFalse();
}
/**
@@ -294,7 +292,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.connectTetherNetwork(network));
+ assertThat(manager.connectTetherNetwork(network)).isFalse();
}
@Test
@@ -315,7 +313,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).connectTetherNetwork(network);
- assertFalse(manager.connectTetherNetwork(network));
+ assertThat(manager.connectTetherNetwork(network)).isFalse();
}
/**
@@ -327,7 +325,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.disconnectTetherNetwork(network));
+ assertThat(manager.disconnectTetherNetwork(network)).isFalse();
}
@Test
@@ -348,7 +346,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).disconnectTetherNetwork(any());
- assertFalse(manager.disconnectTetherNetwork(network));
+ assertThat(manager.disconnectTetherNetwork(network)).isFalse();
}
/**
@@ -360,7 +358,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.connectKnownNetwork(network));
+ assertThat(manager.connectKnownNetwork(network)).isFalse();
}
@Test
@@ -381,7 +379,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).connectKnownNetwork(network);
- assertFalse(manager.connectKnownNetwork(network));
+ assertThat(manager.connectKnownNetwork(network)).isFalse();
}
/**
@@ -393,7 +391,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertFalse(manager.forgetKnownNetwork(network));
+ assertThat(manager.forgetKnownNetwork(network)).isFalse();
}
@Test
@@ -414,7 +412,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).forgetKnownNetwork(network);
- assertFalse(manager.forgetKnownNetwork(network));
+ assertThat(manager.forgetKnownNetwork(network)).isFalse();
}
/**
@@ -425,7 +423,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertArrayEquals(List.of().toArray(), manager.getTetherNetworks().toArray());
+ assertThat(manager.getKnownNetworks()).isEmpty();
}
@Test
@@ -434,18 +432,17 @@
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 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertArrayEquals(List.of().toArray(), manager.getKnownNetworks().toArray());
+ assertThat(manager.getKnownNetworks()).isEmpty();
}
@Test
@@ -463,18 +460,17 @@
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 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertNull(manager.getSettingsState());
+ assertThat(manager.getSettingsState()).isNull();
}
@Test
@@ -491,7 +487,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).getSettingsState();
- assertNull(manager.getSettingsState());
+ assertThat(manager.getSettingsState()).isNull();
}
@Test
@@ -502,7 +498,7 @@
manager.setService(mService);
when(mService.getSettingsState()).thenReturn(state);
- assertEquals(state, manager.getSettingsState());
+ assertThat(manager.getSettingsState()).isEqualTo(state);
}
@Test
@@ -511,7 +507,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertNull(manager.getTetherNetworkConnectionStatus());
+ assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
}
@Test
@@ -521,7 +517,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).getTetherNetworkConnectionStatus();
- assertNull(manager.getTetherNetworkConnectionStatus());
+ assertThat(manager.getTetherNetworkConnectionStatus()).isNull();
}
@Test
@@ -534,7 +530,7 @@
manager.setService(mService);
when(mService.getTetherNetworkConnectionStatus()).thenReturn(status);
- assertEquals(status, manager.getTetherNetworkConnectionStatus());
+ assertThat(manager.getTetherNetworkConnectionStatus()).isEqualTo(status);
}
@Test
@@ -543,7 +539,7 @@
SharedConnectivityManager manager = SharedConnectivityManager.create(mContext);
manager.setService(null);
- assertNull(manager.getKnownNetworkConnectionStatus());
+ assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
}
@Test
@@ -553,7 +549,7 @@
manager.setService(mService);
doThrow(new RemoteException()).when(mService).getKnownNetworkConnectionStatus();
- assertNull(manager.getKnownNetworkConnectionStatus());
+ assertThat(manager.getKnownNetworkConnectionStatus()).isNull();
}
@Test
@@ -566,7 +562,7 @@
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 @@
}
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 3137c72..752b749 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 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 @@
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 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 @@
@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 1d9c2e6..0844364 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.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 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 @@
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 @@
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 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 @@
@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);
+ }
+
+ @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 @@
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 b01aec4..a50d767 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.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 @@
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 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().setHotspotSecurityTypes(HOTSPOT_SECURITY_TYPES_1);
- assertNotEquals(network1, builder.build());
+ builder = buildTetherNetworkBuilder();
+ TetherNetwork.Builder builder1 = buildTetherNetworkBuilder();
+ Arrays.stream(HOTSPOT_SECURITY_TYPES_1).forEach(builder1::addHotspotSecurityType);
+
+ assertThat(builder1.build()).isNotEqualTo(builder.build());
}
/**
@@ -117,23 +121,35 @@
@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 a04526a..81efa79 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.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 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 @@
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 @@
@Test
public void onBind_isNotNull() {
SharedConnectivityService service = createService();
- assertNotNull(service.onBind(new Intent()));
+
+ assertThat(service.onBind(new Intent())).isNotNull();
}
@Test
@@ -121,7 +121,9 @@
(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 @@
(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 @@
(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 @@
(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 @@
(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() {