Merge "Add resident shared memory to the ANR Memory headers" into udc-dev
diff --git a/apct-tests/perftests/multiuser/Android.bp b/apct-tests/perftests/multiuser/Android.bp
index c967e51..45c6b8c 100644
--- a/apct-tests/perftests/multiuser/Android.bp
+++ b/apct-tests/perftests/multiuser/Android.bp
@@ -31,6 +31,9 @@
],
platform_apis: true,
test_suites: ["device-tests"],
- data: ["trace_configs/*"],
+ data: [
+ ":MultiUserPerfDummyApp",
+ "trace_configs/*",
+ ],
certificate: "platform",
}
diff --git a/apct-tests/perftests/packagemanager/Android.bp b/apct-tests/perftests/packagemanager/Android.bp
index 81cec91..e84aea1 100644
--- a/apct-tests/perftests/packagemanager/Android.bp
+++ b/apct-tests/perftests/packagemanager/Android.bp
@@ -33,7 +33,10 @@
test_suites: ["device-tests"],
- data: [":perfetto_artifacts"],
+ data: [
+ ":QueriesAll0",
+ ":perfetto_artifacts",
+ ],
certificate: "platform",
diff --git a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
index 3fc87d3..ce381b6 100644
--- a/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
+++ b/apex/jobscheduler/service/java/com/android/server/AppStateTrackerImpl.java
@@ -22,7 +22,7 @@
import android.app.AppOpsManager;
import android.app.AppOpsManager.PackageOps;
import android.app.IActivityManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
import android.app.usage.UsageStatsManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -716,11 +716,7 @@
return true;
}
- private final class UidObserver extends IUidObserver.Stub {
- @Override
- public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
- }
-
+ private final class UidObserver extends android.app.UidObserver {
@Override
public void onUidActive(int uid) {
mHandler.onUidActive(uid);
@@ -740,10 +736,6 @@
public void onUidCachedChanged(int uid, boolean cached) {
mHandler.onUidCachedChanged(uid, cached);
}
-
- @Override
- public void onUidProcAdjChanged(int uid) {
- }
}
private final class AppOpsWatcher extends IAppOpsCallback.Stub {
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 70b06cb..887ee5f 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1551,16 +1551,21 @@
jobStatus.getNumPreviousAttempts(),
jobStatus.getJob().getMaxExecutionDelayMillis(),
/* isDeadlineConstraintSatisfied */ false,
- /* isCharging */ false,
- /* batteryNotLow */ false,
- /* storageNotLow */false,
+ /* isChargingSatisfied */ false,
+ /* batteryNotLowSatisfied */ false,
+ /* storageNotLowSatisfied */false,
/* timingDelayConstraintSatisfied */ false,
- /* isDeviceIdle */ false,
+ /* isDeviceIdleSatisfied */ false,
/* hasConnectivityConstraintSatisfied */ false,
/* hasContentTriggerConstraintSatisfied */ false,
- 0,
+ /* jobStartLatencyMs */ 0,
jobStatus.getJob().isUserInitiated(),
- /* isRunningAsUserInitiatedJob */ false);
+ /* isRunningAsUserInitiatedJob */ false,
+ jobStatus.getJob().isPeriodic(),
+ jobStatus.getJob().getMinLatencyMillis(),
+ jobStatus.getEstimatedNetworkDownloadBytes(),
+ jobStatus.getEstimatedNetworkUploadBytes(),
+ jobStatus.getWorkCount());
// If the job is immediately ready to run, then we can just immediately
// put it in the pending list and try to schedule it. This is especially
@@ -1981,9 +1986,14 @@
cancelled.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
cancelled.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
- 0,
+ /* jobStartLatencyMs */ 0,
cancelled.getJob().isUserInitiated(),
- /* isRunningAsUserInitiatedJob */ false);
+ /* isRunningAsUserInitiatedJob */ false,
+ cancelled.getJob().isPeriodic(),
+ cancelled.getJob().getMinLatencyMillis(),
+ cancelled.getEstimatedNetworkDownloadBytes(),
+ cancelled.getEstimatedNetworkUploadBytes(),
+ cancelled.getWorkCount());
}
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
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 8355e9c..44700c8 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -471,7 +471,12 @@
job.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
mExecutionStartTimeElapsed - job.enqueueTime,
job.getJob().isUserInitiated(),
- job.shouldTreatAsUserInitiatedJob());
+ job.shouldTreatAsUserInitiatedJob(),
+ job.getJob().isPeriodic(),
+ job.getJob().getMinLatencyMillis(),
+ job.getEstimatedNetworkDownloadBytes(),
+ job.getEstimatedNetworkUploadBytes(),
+ job.getWorkCount());
final String sourcePackage = job.getSourcePackageName();
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
final String componentPackage = job.getServiceComponent().getPackageName();
@@ -1435,9 +1440,14 @@
completedJob.isConstraintSatisfied(JobInfo.CONSTRAINT_FLAG_DEVICE_IDLE),
completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONNECTIVITY),
completedJob.isConstraintSatisfied(JobStatus.CONSTRAINT_CONTENT_TRIGGER),
- 0,
+ mExecutionStartTimeElapsed - completedJob.enqueueTime,
completedJob.getJob().isUserInitiated(),
- completedJob.startedAsUserInitiatedJob);
+ completedJob.startedAsUserInitiatedJob,
+ completedJob.getJob().isPeriodic(),
+ completedJob.getJob().getMinLatencyMillis(),
+ completedJob.getEstimatedNetworkDownloadBytes(),
+ completedJob.getEstimatedNetworkUploadBytes(),
+ completedJob.getWorkCount());
if (Trace.isTagEnabled(Trace.TRACE_TAG_SYSTEM_SERVER)) {
Trace.asyncTraceForTrackEnd(Trace.TRACE_TAG_SYSTEM_SERVER, "JobScheduler",
getId());
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
index 5d2c926..b9e3b76 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/FlexibilityController.java
@@ -682,7 +682,7 @@
static final String KEY_RESCHEDULED_JOB_DEADLINE_MS =
FC_CONFIG_PREFIX + "rescheduled_job_deadline_ms";
- private static final boolean DEFAULT_FLEXIBILITY_ENABLED = true;
+ private static final boolean DEFAULT_FLEXIBILITY_ENABLED = false;
@VisibleForTesting
static final long DEFAULT_DEADLINE_PROXIMITY_LIMIT_MS = 15 * MINUTE_IN_MILLIS;
@VisibleForTesting
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 7cc2f28..6445c3b 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
@@ -590,9 +590,10 @@
this.sourceTag = tag;
}
+ final String bnNamespace = namespace == null ? "" : "@" + namespace + "@";
this.batteryName = this.sourceTag != null
- ? this.sourceTag + ":" + job.getService().getPackageName()
- : job.getService().flattenToShortString();
+ ? bnNamespace + this.sourceTag + ":" + job.getService().getPackageName()
+ : bnNamespace + job.getService().flattenToShortString();
this.tag = "*job*/" + this.batteryName + "#" + job.getId();
this.earliestRunTimeElapsedMillis = earliestRunTimeElapsedMillis;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index aca0a6e..175c8d1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -35,7 +35,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.AlarmManager;
-import android.app.IUidObserver;
+import android.app.UidObserver;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -382,31 +382,11 @@
}
};
- private class QcUidObserver extends IUidObserver.Stub {
+ private class QcUidObserver extends UidObserver {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
mHandler.obtainMessage(MSG_UID_PROCESS_STATE_CHANGED, uid, procState).sendToTarget();
}
-
- @Override
- public void onUidGone(int uid, boolean disabled) {
- }
-
- @Override
- public void onUidActive(int uid) {
- }
-
- @Override
- public void onUidIdle(int uid, boolean disabled) {
- }
-
- @Override
- public void onUidCachedChanged(int uid, boolean cached) {
- }
-
- @Override
- public void onUidProcAdjChanged(int uid) {
- }
}
/**
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
index 3578c8a..58536675 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/ProcessStateModifier.java
@@ -20,6 +20,7 @@
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.IUidObserver;
+import android.app.UidObserver;
import android.os.RemoteException;
import android.util.IndentingPrintWriter;
import android.util.Slog;
@@ -61,7 +62,7 @@
@GuardedBy("mLock")
private final SparseIntArray mUidProcStateBucketCache = new SparseIntArray();
- private final IUidObserver mUidObserver = new IUidObserver.Stub() {
+ private final IUidObserver mUidObserver = new UidObserver() {
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
final int newBucket = getProcStateBucket(procState);
@@ -85,22 +86,6 @@
notifyStateChangedLocked(uid);
}
}
-
- @Override
- public void onUidActive(int uid) {
- }
-
- @Override
- public void onUidIdle(int uid, boolean disabled) {
- }
-
- @Override
- public void onUidCachedChanged(int uid, boolean cached) {
- }
-
- @Override
- public void onUidProcAdjChanged(int uid) {
- }
};
ProcessStateModifier(@NonNull InternalResourceService irs) {
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 1346d07..873234a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -427,7 +427,7 @@
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void collapsePanels();
method public void expandNotificationsPanel();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getLastSystemKey();
- method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(int);
+ method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void handleSystemKey(@NonNull android.view.KeyEvent);
method public void sendNotificationFeedback(@Nullable String, @Nullable android.os.Bundle);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void togglePanel();
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 29f774c..a6313db 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -46,6 +46,7 @@
import android.os.UserHandle;
import android.util.Pair;
import android.util.Slog;
+import android.view.KeyEvent;
import android.view.View;
import com.android.internal.statusbar.AppClipsServiceConnector;
@@ -740,7 +741,7 @@
*/
@RequiresPermission(android.Manifest.permission.STATUS_BAR)
@TestApi
- public void handleSystemKey(int key) {
+ public void handleSystemKey(@NonNull KeyEvent key) {
try {
final IStatusBarService svc = getService();
if (svc != null) {
diff --git a/core/java/android/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 12882a2..9efdf28 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -39,21 +39,22 @@
import android.os.ResultReceiver;
/**
- * Interface for a virtual device.
+ * Interface for a virtual device for communication between the system server and the process of
+ * the owner of the virtual device.
*
* @hide
*/
interface IVirtualDevice {
/**
- * Returns the association ID for this virtual device.
+ * Returns the CDM association ID of this virtual device.
*
* @see AssociationInfo#getId()
*/
int getAssociationId();
/**
- * Returns the unique device ID for this virtual device.
+ * Returns the unique ID of this virtual device.
*/
int getDeviceId();
@@ -64,55 +65,99 @@
void close();
/**
- * Notifies of an audio session being started.
+ * Notifies that an audio session being started.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void onAudioSessionStarting(
- int displayId,
- IAudioRoutingCallback routingCallback,
+ void onAudioSessionStarting(int displayId, IAudioRoutingCallback routingCallback,
IAudioConfigChangedCallback configChangedCallback);
+ /**
+ * Notifies that an audio session has ended.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void onAudioSessionEnded();
+ /**
+ * Creates a new dpad and registers it with the input framework with the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void createVirtualDpad(
- in VirtualDpadConfig config,
- IBinder token);
+ void createVirtualDpad(in VirtualDpadConfig config, IBinder token);
+
+ /**
+ * Creates a new keyboard and registers it with the input framework with the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void createVirtualKeyboard(
- in VirtualKeyboardConfig config,
- IBinder token);
+ void createVirtualKeyboard(in VirtualKeyboardConfig config, IBinder token);
+
+ /**
+ * Creates a new mouse and registers it with the input framework with the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void createVirtualMouse(
- in VirtualMouseConfig config,
- IBinder token);
+ void createVirtualMouse(in VirtualMouseConfig config, IBinder token);
+
+ /**
+ * Creates a new touchscreen and registers it with the input framework with the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void createVirtualTouchscreen(
- in VirtualTouchscreenConfig config,
- IBinder token);
+ void createVirtualTouchscreen(in VirtualTouchscreenConfig config, IBinder token);
+
+ /**
+ * Creates a new navigation touchpad and registers it with the input framework with the given
+ * token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void createVirtualNavigationTouchpad(
- in VirtualNavigationTouchpadConfig config,
- IBinder token);
+ void createVirtualNavigationTouchpad(in VirtualNavigationTouchpadConfig config, IBinder token);
+
+ /**
+ * Removes the input device corresponding to the given token from the framework.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterInputDevice(IBinder token);
+
+ /**
+ * Returns the ID of the device corresponding to the given token, as registered with the input
+ * framework.
+ */
int getInputDeviceId(IBinder token);
+
+ /**
+ * Injects a key event to the virtual dpad corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendDpadKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+ /**
+ * Injects a key event to the virtual keyboard corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendKeyEvent(IBinder token, in VirtualKeyEvent event);
+
+ /**
+ * Injects a button event to the virtual mouse corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendButtonEvent(IBinder token, in VirtualMouseButtonEvent event);
+
+ /**
+ * Injects a relative event to the virtual mouse corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendRelativeEvent(IBinder token, in VirtualMouseRelativeEvent event);
+
+ /**
+ * Injects a scroll event to the virtual mouse corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendScrollEvent(IBinder token, in VirtualMouseScrollEvent event);
+
+ /**
+ * Injects a touch event to the virtual touch input device corresponding to the given token.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
boolean sendTouchEvent(IBinder token, in VirtualTouchEvent event);
/**
- * Returns all virtual sensors for this device.
+ * Returns all virtual sensors created for this device.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
List<VirtualSensor> getVirtualSensorList();
@@ -126,8 +171,13 @@
/**
* Launches a pending intent on the given display that is owned by this virtual device.
*/
- void launchPendingIntent(
- int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
+ void launchPendingIntent(int displayId, in PendingIntent pendingIntent,
+ in ResultReceiver resultReceiver);
+
+ /**
+ * Returns the current cursor position of the mouse corresponding to the given token, in x and y
+ * coordinates.
+ */
PointF getCursorPosition(IBinder token);
/** Sets whether to show or hide the cursor while this virtual device is active. */
@@ -140,8 +190,12 @@
* intent.
*/
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
- void registerIntentInterceptor(
- in IVirtualDeviceIntentInterceptor intentInterceptor, in IntentFilter filter);
+ void registerIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor,
+ in IntentFilter filter);
+
+ /**
+ * Unregisters a previously registered intent interceptor.
+ */
@EnforcePermission("CREATE_VIRTUAL_DEVICE")
void unregisterIntentInterceptor(in IVirtualDeviceIntentInterceptor intentInterceptor);
}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
index 4f49b8d..07743cef5 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceManager.aidl
@@ -101,7 +101,7 @@
*
* @param deviceId id of the virtual device.
* @param sound effect type corresponding to
- * {@code android.media.AudioManager.SystemSoundEffect}
+ * {@code android.media.AudioManager.SystemSoundEffect}
*/
void playSoundEffect(int deviceId, int effectType);
}
diff --git a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
index 91c209f..f284554 100644
--- a/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
+++ b/core/java/android/companion/virtual/IVirtualDeviceSoundEffectListener.aidl
@@ -28,7 +28,7 @@
* Called when there's sound effect to be played on Virtual Device.
*
* @param sound effect type corresponding to
- * {@code android.media.AudioManager.SystemSoundEffect}
+ * {@code android.media.AudioManager.SystemSoundEffect}
*/
void onPlaySoundEffect(int effectType);
}
diff --git a/core/java/android/companion/virtual/VirtualDevice.java b/core/java/android/companion/virtual/VirtualDevice.java
index 4a09186..4ee65e0 100644
--- a/core/java/android/companion/virtual/VirtualDevice.java
+++ b/core/java/android/companion/virtual/VirtualDevice.java
@@ -26,6 +26,11 @@
/**
* Details of a particular virtual device.
+ *
+ * <p>Read-only device representation exposing the properties of an existing virtual device.
+ *
+ * <p class="note">Not to be confused with {@link VirtualDeviceManager.VirtualDevice}, which is used
+ * by the virtual device creator and allows them to manage the device.
*/
public final class VirtualDevice implements Parcelable {
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index d3d635e..da6784b 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -68,7 +68,13 @@
import java.util.function.IntConsumer;
/**
- * System level service for managing virtual devices.
+ * System level service for creation and management of virtual devices.
+ *
+ * <p>VirtualDeviceManager enables interactive sharing of capabilities between the host Android
+ * device and a remote device.
+ *
+ * <p class="note">Not to be confused with the Android Studio's Virtual Device Manager, which allows
+ * for device emulation.
*/
@SystemService(Context.VIRTUAL_DEVICE_SERVICE)
public final class VirtualDeviceManager {
@@ -174,6 +180,9 @@
/**
* Returns the details of all available virtual devices.
+ *
+ * <p>The returned objects are read-only representations that expose the properties of all
+ * existing virtual devices.
*/
@NonNull
public List<android.companion.virtual.VirtualDevice> getVirtualDevices() {
@@ -252,11 +261,12 @@
*
* @param deviceId - id of the virtual audio device
* @return Device specific session id to be used for audio playback (see
- * {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
- * {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
- * {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
- * is configured in context-aware mode.
- * Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+ * {@link AudioManager#generateAudioSessionId}) if virtual device has
+ * {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+ * {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+ * is configured in context-aware mode. Otherwise
+ * {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+ *
* @hide
*/
public int getAudioPlaybackSessionId(int deviceId) {
@@ -275,11 +285,12 @@
*
* @param deviceId - id of the virtual audio device
* @return Device specific session id to be used for audio recording (see
- * {@link android.media.AudioManager.generateAudioSessionId}) if virtual device has
- * {@link VirtualDeviceParams.POLICY_TYPE_AUDIO} set to
- * {@link VirtualDeviceParams.DEVICE_POLICY_CUSTOM} and Virtual Audio Device
- * is configured in context-aware mode.
- * Otherwise {@link AUDIO_SESSION_ID_GENERATE} constant is returned.
+ * {@link AudioManager#generateAudioSessionId}) if virtual device has
+ * {@link VirtualDeviceParams#POLICY_TYPE_AUDIO} set to
+ * {@link VirtualDeviceParams#DEVICE_POLICY_CUSTOM} and Virtual Audio Device
+ * is configured in context-aware mode. Otherwise
+ * {@link AudioManager#AUDIO_SESSION_ID_GENERATE} constant is returned.
+ *
* @hide
*/
public int getAudioRecordingSessionId(int deviceId) {
@@ -296,10 +307,11 @@
/**
* Requests sound effect to be played on virtual device.
*
- * @see android.media.AudioManager#playSoundEffect(int)
+ * @see AudioManager#playSoundEffect(int)
*
* @param deviceId - id of the virtual audio device
* @param effectType the type of sound effect
+ *
* @hide
*/
public void playSoundEffect(int deviceId, @AudioManager.SystemSoundEffect int effectType) {
@@ -315,11 +327,18 @@
}
/**
- * A virtual device has its own virtual display, audio output, microphone, sensors, etc. The
- * creator of a virtual device can take the output from the virtual display and stream it over
- * to another device, and inject input events that are received from the remote device.
+ * A representation of a virtual device.
*
- * TODO(b/204081582): Consider using a builder pattern for the input APIs.
+ * <p>A virtual device can have its own virtual displays, audio input/output, sensors, etc.
+ * The creator of a virtual device can take the output from the virtual display and stream it
+ * over to another device, and inject input and sensor events that are received from the remote
+ * device.
+ *
+ * <p>This object is only used by the virtual device creator and allows them to manage the
+ * device's behavior, peripherals, and the user interaction with that device.
+ *
+ * <p class="note">Not to be confused with {@link android.companion.virtual.VirtualDevice},
+ * which is a read-only representation exposing the properties of an existing virtual device.
*
* @hide
*/
@@ -346,8 +365,10 @@
}
/**
- * @return A new Context bound to this device. This is a convenience method equivalent to
- * calling {@link Context#createDeviceContext(int)} with the device id of this device.
+ * Returns a new context bound to this device.
+ *
+ * <p>This is a convenience method equivalent to calling
+ * {@link Context#createDeviceContext(int)} with the id of this device.
*/
public @NonNull Context createContext() {
return mVirtualDeviceInternal.createContext();
@@ -400,20 +421,19 @@
* @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 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)}.
+ * 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}.
+ * {@link DisplayManager#createVirtualDisplay}. In addition, the following flags are
+ * automatically set for all virtual devices:
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_PUBLIC} and
+ * {@link DisplayManager#VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}.
* @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.
+ * if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+ * must not be null.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
* @return The newly created virtual display, or {@code null} if the application could
- * not create the virtual display.
+ * not create the virtual display.
*
* @see DisplayManager#createVirtualDisplay
*
@@ -450,11 +470,11 @@
*
* @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.
+ * if {@code callback} is {@code null}. If {@code callback} is specified, this executor
+ * must not be null.
* @param callback Callback to call when the state of the {@link VirtualDisplay} changes
* @return The newly created virtual display, or {@code null} if the application could
- * not create the virtual display.
+ * not create the virtual display.
*
* @see DisplayManager#createVirtualDisplay
*/
@@ -478,7 +498,7 @@
/**
* Creates a virtual dpad.
*
- * @param config the configurations of the virtual Dpad.
+ * @param config the configurations of the virtual dpad.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -500,11 +520,10 @@
/**
* Creates a virtual keyboard.
*
- * @param display the display that the events inputted through this device should
- * target
- * @param inputDeviceName the name to call this input device
- * @param vendorId the PCI vendor id
- * @param productId the product id, as defined by the vendor
+ * @param display the display that the events inputted through this device should target.
+ * @param inputDeviceName the name of this keyboard device.
+ * @param vendorId the PCI vendor id.
+ * @param productId the product id, as defined by the vendor.
* @see #createVirtualKeyboard(VirtualKeyboardConfig config)
* @deprecated Use {@link #createVirtualKeyboard(VirtualKeyboardConfig config)} instead
*/
@@ -537,14 +556,12 @@
/**
* Creates a virtual mouse.
*
- * @param display the display that the events inputted through this device should
- * target
- * @param inputDeviceName the name to call this input device
- * @param vendorId the PCI vendor id
- * @param productId the product id, as defined by the vendor
+ * @param display the display that the events inputted through this device should target.
+ * @param inputDeviceName the name of this mouse.
+ * @param vendorId the PCI vendor id.
+ * @param productId the product id, as defined by the vendor.
* @see #createVirtualMouse(VirtualMouseConfig config)
* @deprecated Use {@link #createVirtualMouse(VirtualMouseConfig config)} instead
- * *
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -576,11 +593,10 @@
/**
* Creates a virtual touchscreen.
*
- * @param display the display that the events inputted through this device should
- * target
- * @param inputDeviceName the name to call this input device
- * @param vendorId the PCI vendor id
- * @param productId the product id, as defined by the vendor
+ * @param display the display that the events inputted through this device should target.
+ * @param inputDeviceName the name of this touchscreen device.
+ * @param vendorId the PCI vendor id.
+ * @param productId the product id, as defined by the vendor.
* @see #createVirtualTouchscreen(VirtualTouchscreenConfig config)
* @deprecated Use {@link #createVirtualTouchscreen(VirtualTouchscreenConfig config)}
* instead
@@ -605,11 +621,13 @@
/**
* Creates a virtual touchpad in navigation mode.
*
- * A touchpad in navigation mode means that its events are interpreted as navigation events
- * (up, down, etc) instead of using them to update a cursor's absolute position. If the
- * events are not consumed they are converted to DPAD events.
+ * <p>A touchpad in navigation mode means that its events are interpreted as navigation
+ * events (up, down, etc) instead of using them to update a cursor's absolute position. If
+ * the events are not consumed they are converted to DPAD events and delivered to the target
+ * again.
*
* @param config the configurations of the virtual navigation touchpad.
+ * @see android.view.InputDevice#SOURCE_TOUCH_NAVIGATION
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -629,10 +647,10 @@
*
* @param display The target virtual display to capture from and inject into.
* @param executor The {@link Executor} object for the thread on which to execute
- * the callback. If <code>null</code>, the {@link Executor} associated with
- * the main {@link Looper} will be used.
+ * the callback. If <code>null</code>, the {@link Executor} associated with the main
+ * {@link Looper} will be used.
* @param callback Interface to be notified when playback or recording configuration of
- * applications running on virtual display is changed.
+ * applications running on virtual display is changed.
* @return A {@link VirtualAudioDevice} instance.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -648,7 +666,7 @@
* Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
*
* @param showPointerIcon True if the pointer should be shown; false otherwise. The default
- * visibility is true.
+ * visibility is true.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@NonNull
@@ -670,8 +688,7 @@
}
/**
- * Removes an activity listener previously added with
- * {@link #addActivityListener}.
+ * Removes an activity listener previously added with {@link #addActivityListener}.
*
* @param listener The listener to remove.
* @see #addActivityListener(Executor, ActivityListener)
@@ -693,10 +710,10 @@
}
/**
- * Removes a sound effect listener previously added with {@link #addActivityListener}.
+ * Removes a sound effect listener previously added with {@link #addSoundEffectListener}.
*
* @param soundEffectListener The listener to remove.
- * @see #addActivityListener(Executor, ActivityListener)
+ * @see #addSoundEffectListener(Executor, SoundEffectListener)
*/
public void removeSoundEffectListener(@NonNull SoundEffectListener soundEffectListener) {
mVirtualDeviceInternal.removeSoundEffectListener(soundEffectListener);
@@ -723,7 +740,7 @@
}
/**
- * Unregisters the intent interceptorCallback previously registered with
+ * Unregisters the intent interceptor previously registered with
* {@link #registerIntentInterceptor}.
*/
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
@@ -761,9 +778,9 @@
* {@link #onDisplayEmpty(int)} will be called. If the value topActivity is cached, it
* should be cleared when {@link #onDisplayEmpty(int)} is called.
*
- * @param displayId The display ID on which the activity change happened.
+ * @param displayId The display ID on which the activity change happened.
* @param topActivity The component name of the top activity.
- * @param userId The user ID associated with the top activity.
+ * @param userId The user ID associated with the top activity.
*/
default void onTopActivityChanged(int displayId, @NonNull ComponentName topActivity,
@UserIdInt int userId) {}
@@ -800,6 +817,7 @@
/**
* Listener for system sound effect playback on virtual device.
+ *
* @hide
*/
@SystemApi
@@ -808,8 +826,8 @@
/**
* Called when there's a system sound effect to be played on virtual device.
*
- * @param effectType - system sound effect type, see
- * {@code android.media.AudioManager.SystemSoundEffect}
+ * @param effectType - system sound effect type
+ * @see android.media.AudioManager.SystemSoundEffect
*/
void onPlaySoundEffect(@AudioManager.SystemSoundEffect int effectType);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceParams.java b/core/java/android/companion/virtual/VirtualDeviceParams.java
index 9a34dbe..45d6dc6 100644
--- a/core/java/android/companion/virtual/VirtualDeviceParams.java
+++ b/core/java/android/companion/virtual/VirtualDeviceParams.java
@@ -34,6 +34,7 @@
import android.companion.virtual.sensor.VirtualSensorConfig;
import android.companion.virtual.sensor.VirtualSensorDirectChannelCallback;
import android.content.ComponentName;
+import android.content.Context;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SharedMemory;
@@ -680,7 +681,7 @@
* {@link #NAVIGATION_POLICY_DEFAULT_ALLOWED}, meaning activities are allowed to launch
* unless they are in {@code blockedCrossTaskNavigations}.
*
- * <p> This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
+ * <p>This method must not be called if {@link #setAllowedCrossTaskNavigations(Set)} has
* been called.
*
* @throws IllegalArgumentException if {@link #setAllowedCrossTaskNavigations(Set)} has
@@ -847,11 +848,11 @@
* <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
* otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
* the playback session id is set to value other than
- * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+ * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
*
* @param playbackSessionId requested device-specific audio session id for playback
- * @see android.media.AudioManager.generateAudioSessionId()
- * @see android.media.AudioTrack.Builder.setContext(Context)
+ * @see android.media.AudioManager#generateAudioSessionId()
+ * @see android.media.AudioTrack.Builder#setContext(Context)
*/
@NonNull
public Builder setAudioPlaybackSessionId(int playbackSessionId) {
@@ -871,11 +872,11 @@
* <p>Requires {@link #DEVICE_POLICY_CUSTOM} to be set for {@link #POLICY_TYPE_AUDIO},
* otherwise {@link #build()} method will throw {@link IllegalArgumentException} if
* the recording session id is set to value other than
- * {@link android.media.AudioManager.AUDIO_SESSION_ID_GENERATE}.
+ * {@link android.media.AudioManager#AUDIO_SESSION_ID_GENERATE}.
*
* @param recordingSessionId requested device-specific audio session id for playback
- * @see android.media.AudioManager.generateAudioSessionId()
- * @see android.media.AudioRecord.Builder.setContext(Context)
+ * @see android.media.AudioManager#generateAudioSessionId()
+ * @see android.media.AudioRecord.Builder#setContext(Context)
*/
@NonNull
public Builder setAudioRecordingSessionId(int recordingSessionId) {
diff --git a/core/java/android/companion/virtual/audio/AudioCapture.java b/core/java/android/companion/virtual/audio/AudioCapture.java
index d6d0d2b..dd5e660 100644
--- a/core/java/android/companion/virtual/audio/AudioCapture.java
+++ b/core/java/android/companion/virtual/audio/AudioCapture.java
@@ -56,12 +56,12 @@
/**
* Sets the {@link AudioRecord} to handle audio capturing.
- * Callers may call this multiple times with different audio records to change
- * the underlying {@link AudioRecord} without stopping and re-starting recording.
*
- * @param audioRecord The underlying {@link AudioRecord} to use for capture,
- * or null if no audio (i.e. silence) should be captured while still keeping the
- * record in a recording state.
+ * <p>Callers may call this multiple times with different audio records to change the underlying
+ * {@link AudioRecord} without stopping and re-starting recording.
+ *
+ * @param audioRecord The underlying {@link AudioRecord} to use for capture, or null if no audio
+ * (i.e. silence) should be captured while still keeping the record in a recording state.
*/
void setAudioRecord(@Nullable AudioRecord audioRecord) {
Log.d(TAG, "set AudioRecord with " + audioRecord);
diff --git a/core/java/android/companion/virtual/audio/AudioInjection.java b/core/java/android/companion/virtual/audio/AudioInjection.java
index 9d6a3eb..5de5f7e 100644
--- a/core/java/android/companion/virtual/audio/AudioInjection.java
+++ b/core/java/android/companion/virtual/audio/AudioInjection.java
@@ -65,12 +65,12 @@
/**
* Sets the {@link AudioTrack} to handle audio injection.
- * Callers may call this multiple times with different audio tracks to change
- * the underlying {@link AudioTrack} without stopping and re-starting injection.
*
- * @param audioTrack The underlying {@link AudioTrack} to use for injection,
- * or null if no audio (i.e. silence) should be injected while still keeping the
- * record in a playing state.
+ * <p>Callers may call this multiple times with different audio tracks to change the underlying
+ * {@link AudioTrack} without stopping and re-starting injection.
+ *
+ * @param audioTrack The underlying {@link AudioTrack} to use for injection, or null if no audio
+ * (i.e. silence) should be injected while still keeping the record in a playing state.
*/
void setAudioTrack(@Nullable AudioTrack audioTrack) {
Log.d(TAG, "set AudioTrack with " + audioTrack);
diff --git a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
index 3cb0572..dcdb6c6 100644
--- a/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
+++ b/core/java/android/companion/virtual/sensor/IVirtualSensorCallback.aidl
@@ -33,7 +33,7 @@
* @param enabled Whether the sensor is enabled.
* @param samplingPeriodMicros The requested sensor's sampling period in microseconds.
* @param batchReportingLatencyMicros The requested maximum time interval in microseconds
- * between the delivery of two batches of sensor events.
+ * between the delivery of two batches of sensor events.
*/
void onConfigurationChanged(in VirtualSensor sensor, boolean enabled, int samplingPeriodMicros,
int batchReportLatencyMicros);
@@ -60,7 +60,7 @@
* @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.
+ * 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/VirtualSensor.java b/core/java/android/companion/virtual/sensor/VirtualSensor.java
index bda44d4..eaa1792 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensor.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensor.java
@@ -30,7 +30,7 @@
* Representation of a sensor on a remote device, capable of sending events, such as an
* accelerometer or a gyroscope.
*
- * This registers the sensor device with the sensor framework as a runtime sensor.
+ * <p>A virtual sensor device is registered with the sensor framework as a runtime sensor.
*
* @hide
*/
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
index e6bd6da..4d586f6 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorCallback.java
@@ -45,10 +45,10 @@
*
* @param sensor The sensor whose requested injection parameters have changed.
* @param enabled Whether the sensor is enabled. True if any listeners are currently registered,
- * and false otherwise.
+ * and false otherwise.
* @param samplingPeriod The requested sampling period of the sensor.
* @param batchReportLatency The requested maximum time interval between the delivery of two
- * batches of sensor events.
+ * batches of sensor events.
*/
void onConfigurationChanged(@NonNull VirtualSensor sensor, boolean enabled,
@NonNull Duration samplingPeriod, @NonNull Duration batchReportLatency);
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
index ef55ca9..3bdf9aa 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorConfig.java
@@ -31,7 +31,9 @@
/**
* Configuration for creation of a virtual sensor.
+ *
* @see VirtualSensor
+ *
* @hide
*/
@SystemApi
@@ -122,6 +124,7 @@
/**
* Returns the vendor string of the sensor.
+ *
* @see Builder#setVendor
*/
@Nullable
@@ -130,7 +133,8 @@
}
/**
- * Returns maximum range of the sensor in the sensor's unit.
+ * Returns the maximum range of the sensor in the sensor's unit.
+ *
* @see Sensor#getMaximumRange
*/
public float getMaximumRange() {
@@ -138,7 +142,8 @@
}
/**
- * Returns The resolution of the sensor in the sensor's unit.
+ * Returns the resolution of the sensor in the sensor's unit.
+ *
* @see Sensor#getResolution
*/
public float getResolution() {
@@ -146,7 +151,8 @@
}
/**
- * Returns The power in mA used by this sensor while in use.
+ * Returns the power in mA used by this sensor while in use.
+ *
* @see Sensor#getPower
*/
public float getPower() {
@@ -154,8 +160,9 @@
}
/**
- * Returns The minimum delay allowed between two events in microseconds, or zero depending on
+ * Returns the minimum delay allowed between two events in microseconds, or zero depending on
* the sensor type.
+ *
* @see Sensor#getMinDelay
*/
public int getMinDelay() {
@@ -163,7 +170,8 @@
}
/**
- * Returns The maximum delay between two sensor events in microseconds.
+ * Returns the maximum delay between two sensor events in microseconds.
+ *
* @see Sensor#getMaxDelay
*/
public int getMaxDelay() {
@@ -201,6 +209,7 @@
/**
* Returns the sensor flags.
+ *
* @hide
*/
public int getFlags() {
@@ -233,7 +242,7 @@
*
* @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(@IntRange(from = 1) int type, @NonNull String name) {
if (type <= 0) {
@@ -275,6 +284,7 @@
/**
* Sets the maximum range of the sensor in the sensor's unit.
+ *
* @see Sensor#getMaximumRange
*/
@NonNull
@@ -285,6 +295,7 @@
/**
* Sets the resolution of the sensor in the sensor's unit.
+ *
* @see Sensor#getResolution
*/
@NonNull
@@ -295,6 +306,7 @@
/**
* Sets the power in mA used by this sensor while in use.
+ *
* @see Sensor#getPower
*/
@NonNull
@@ -305,6 +317,7 @@
/**
* Sets the minimum delay allowed between two events in microseconds.
+ *
* @see Sensor#getMinDelay
*/
@NonNull
@@ -315,6 +328,7 @@
/**
* Sets the maximum delay between two sensor events in microseconds.
+ *
* @see Sensor#getMaxDelay
*/
@NonNull
@@ -339,11 +353,11 @@
* 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.
+ * 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.
+ * set to be supported.
*/
@NonNull
public VirtualSensorConfig.Builder setDirectChannelTypesSupported(
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
index d352f94f..f10e9d0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelCallback.java
@@ -45,6 +45,8 @@
* <p>The callback is tied to the VirtualDevice's lifetime as the virtual sensors are created when
* the device is created and destroyed when the device is destroyed.
*
+ * @see VirtualSensorDirectChannelWriter
+ *
* @hide
*/
@SystemApi
@@ -94,7 +96,7 @@
* @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.
+ * different sensors within the same channel.
*
* @see VirtualSensorConfig.Builder#setHighestDirectReportRateLevel(int)
* @see VirtualSensorConfig.Builder#setDirectChannelTypesSupported(int)
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
index 6aed96f..bf78dd0 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorDirectChannelWriter.java
@@ -41,6 +41,41 @@
* write the events from the relevant sensors directly to the shared memory regions of the
* corresponding {@link SensorDirectChannel} instances.
*
+ * <p>Example:
+ * <p>During sensor and virtual device creation:
+ * <pre>
+ * VirtualSensorDirectChannelWriter writer = new VirtualSensorDirectChannelWriter();
+ * VirtualSensorDirectChannelCallback callback = new VirtualSensorDirectChannelCallback() {
+ * @Override
+ * public void onDirectChannelCreated(int channelHandle, SharedMemory sharedMemory) {
+ * writer.addChannel(channelHandle, sharedMemory);
+ * }
+ * @Override
+ * public void onDirectChannelDestroyed(int channelHandle);
+ * writer.removeChannel(channelHandle);
+ * }
+ * @Override
+ * public void onDirectChannelConfigured(int channelHandle, VirtualSensor sensor, int rateLevel,
+ * int reportToken)
+ * if (!writer.configureChannel(channelHandle, sensor, rateLevel, reportToken)) {
+ * // handle error
+ * }
+ * }
+ * }
+ * </pre>
+ * <p>During the virtual device lifetime:
+ * <pre>
+ * VirtualSensor sensor = ...
+ * while (shouldInjectEvents(sensor)) {
+ * if (!writer.writeSensorEvent(sensor, event)) {
+ * // handle error
+ * }
+ * }
+ * writer.close();
+ * </pre>
+ * <p>Note that the virtual device owner should take the currently configured rate level into
+ * account when deciding whether and how often to inject events for a particular sensor.
+ *
* @see android.hardware.SensorDirectChannel#configure
* @see VirtualSensorDirectChannelCallback
*
diff --git a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
index 01b4975..a368467e 100644
--- a/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
+++ b/core/java/android/companion/virtual/sensor/VirtualSensorEvent.java
@@ -121,7 +121,7 @@
* monotonically increasing using the same time base as
* {@link android.os.SystemClock#elapsedRealtimeNanos()}.
*
- * If not explicitly set, the current timestamp is used for the sensor event.
+ * <p>If not explicitly set, the current timestamp is used for the sensor event.
*
* @see android.hardware.SensorEvent#timestamp
*/
diff --git a/core/java/android/util/TeeWriter.java b/core/java/android/util/TeeWriter.java
new file mode 100644
index 0000000..439a0c2
--- /dev/null
+++ b/core/java/android/util/TeeWriter.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.util;
+
+import android.annotation.NonNull;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.util.Objects;
+
+/**
+ * Writer that offers to "tee" identical output to multiple underlying
+ * {@link Writer} instances.
+ *
+ * @see https://man7.org/linux/man-pages/man1/tee.1.html
+ * @hide
+ */
+public class TeeWriter extends Writer {
+ private final @NonNull Writer[] mWriters;
+
+ public TeeWriter(@NonNull Writer... writers) {
+ for (Writer writer : writers) {
+ Objects.requireNonNull(writer);
+ }
+ mWriters = writers;
+ }
+
+ @Override
+ public void write(char[] cbuf, int off, int len) throws IOException {
+ for (Writer writer : mWriters) {
+ writer.write(cbuf, off, len);
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ for (Writer writer : mWriters) {
+ writer.flush();
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ for (Writer writer : mWriters) {
+ writer.close();
+ }
+ }
+}
diff --git a/core/java/android/view/InputWindowHandle.java b/core/java/android/view/InputWindowHandle.java
index 24a0355..d35aff9 100644
--- a/core/java/android/view/InputWindowHandle.java
+++ b/core/java/android/view/InputWindowHandle.java
@@ -158,6 +158,14 @@
*/
public Matrix transform;
+ /**
+ * The input token for the window to which focus should be transferred when this input window
+ * can be successfully focused. If null, this input window will not transfer its focus to
+ * any other window.
+ */
+ @Nullable
+ public IBinder focusTransferTarget;
+
private native void nativeDispose();
public InputWindowHandle(InputApplicationHandle inputApplicationHandle, int displayId) {
@@ -195,6 +203,7 @@
transform = new Matrix();
transform.set(other.transform);
}
+ focusTransferTarget = other.focusTransferTarget;
}
@Override
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 0db52aa..bc6a3b5 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -265,7 +265,7 @@
int transformHint);
private static native void nativeRemoveCurrentInputFocus(long nativeObject, int displayId);
private static native void nativeSetFocusedWindow(long transactionObj, IBinder toToken,
- String windowName, IBinder focusedToken, String focusedWindowName, int displayId);
+ String windowName, int displayId);
private static native void nativeSetFrameTimelineVsync(long transactionObj,
long frameTimelineVsyncId);
private static native void nativeAddJankDataListener(long nativeListener,
@@ -3604,28 +3604,7 @@
*/
public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
int displayId) {
- nativeSetFocusedWindow(mNativeObject, token, windowName,
- null /* focusedToken */, null /* focusedWindowName */, displayId);
- return this;
- }
-
- /**
- * Set focus on the window identified by the input {@code token} if the window identified by
- * the input {@code focusedToken} is currently focused. If the {@code focusedToken} does not
- * have focus, the request is dropped.
- *
- * This is used by forward focus transfer requests from clients that host embedded windows,
- * and want to transfer focus to/from them.
- *
- * @hide
- */
- public Transaction requestFocusTransfer(@NonNull IBinder token,
- String windowName,
- @NonNull IBinder focusedToken,
- String focusedWindowName,
- int displayId) {
- nativeSetFocusedWindow(mNativeObject, token, windowName, focusedToken,
- focusedWindowName, displayId);
+ nativeSetFocusedWindow(mNativeObject, token, windowName, displayId);
return this;
}
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 0560caf..9868144 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -57,18 +57,16 @@
SurfaceControl mLeash;
Rect mFrame;
Rect mAttachedFrame;
+ IBinder mFocusGrantToken;
- State(SurfaceControl sc, WindowManager.LayoutParams p,
- int displayId, IBinder inputChannelToken, IWindow client, SurfaceControl leash,
- Rect frame, Rect attachedFrame) {
+ State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IWindow client,
+ SurfaceControl leash, Rect frame) {
mSurfaceControl = sc;
mParams.copyFrom(p);
mDisplayId = displayId;
- mInputChannelToken = inputChannelToken;
mClient = client;
mLeash = leash;
mFrame = frame;
- mAttachedFrame = attachedFrame;
}
};
@@ -182,45 +180,53 @@
.setParent(leash)
.build();
+ final State state = new State(sc, attrs, displayId, window, leash, /* frame= */ new Rect());
+ synchronized (this) {
+ State parentState = mStateForWindow.get(attrs.token);
+ if (parentState != null) {
+ state.mAttachedFrame = parentState.mFrame;
+ }
+
+ // Give the first window the mFocusGrantToken since that's the token the host can use
+ // to give focus to the embedded.
+ if (mStateForWindow.isEmpty()) {
+ state.mFocusGrantToken = mFocusGrantToken;
+ } else {
+ state.mFocusGrantToken = new Binder();
+ }
+
+ mStateForWindow.put(window.asBinder(), state);
+ }
+
+ if (state.mAttachedFrame == null) {
+ outAttachedFrame.set(0, 0, -1, -1);
+ } else {
+ outAttachedFrame.set(state.mAttachedFrame);
+ }
+ outSizeCompatScale[0] = 1f;
+
if (((attrs.inputFeatures &
WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
try {
if (mRealWm instanceof IWindowSession.Stub) {
mRealWm.grantInputChannel(displayId,
new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
- window, mHostInputToken,
- attrs.flags, attrs.privateFlags, attrs.inputFeatures, attrs.type,
- attrs.token, mFocusGrantToken, attrs.getTitle().toString(),
+ window, mHostInputToken, attrs.flags, attrs.privateFlags,
+ attrs.inputFeatures, attrs.type,
+ attrs.token, state.mFocusGrantToken, attrs.getTitle().toString(),
outInputChannel);
} else {
mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
attrs.privateFlags, attrs.inputFeatures, attrs.type, attrs.token,
- mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
+ state.mFocusGrantToken, attrs.getTitle().toString(), outInputChannel);
}
+ state.mInputChannelToken =
+ outInputChannel != null ? outInputChannel.getToken() : null;
} catch (RemoteException e) {
Log.e(TAG, "Failed to grant input to surface: ", e);
}
}
- final State state = new State(sc, attrs, displayId,
- outInputChannel != null ? outInputChannel.getToken() : null, window,
- leash, /* frame= */ new Rect(), /* attachedFrame= */ null);
- Rect parentFrame = null;
- synchronized (this) {
- State parentState = mStateForWindow.get(attrs.token);
- if (parentState != null) {
- parentFrame = parentState.mFrame;
- }
- mStateForWindow.put(window.asBinder(), state);
- }
- state.mAttachedFrame = parentFrame;
- if (parentFrame == null) {
- outAttachedFrame.set(0, 0, -1, -1);
- } else {
- outAttachedFrame.set(parentFrame);
- }
- outSizeCompatScale[0] = 1f;
-
final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
WindowManagerGlobal.ADD_FLAG_USE_BLAST;
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
index 6872536..1840bcb 100644
--- a/core/java/android/view/inputmethod/InputConnection.java
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -1217,9 +1217,11 @@
* notify cursor/anchor locations.
*
* @param cursorUpdateMode any combination of update modes and filters:
- * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and date filters:
+ * {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}, and data filters:
* {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
- * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+ * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+ * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+ * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
* Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
* rejected and method will return {@code false}.
* @return {@code true} if the request is scheduled. {@code false} to indicate that when the
@@ -1240,7 +1242,9 @@
* {@link #CURSOR_UPDATE_IMMEDIATE}, {@link #CURSOR_UPDATE_MONITOR}
* @param cursorUpdateFilter any combination of data filters:
* {@link #CURSOR_UPDATE_FILTER_CHARACTER_BOUNDS}, {@link #CURSOR_UPDATE_FILTER_EDITOR_BOUNDS},
- * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER}.
+ * {@link #CURSOR_UPDATE_FILTER_INSERTION_MARKER},
+ * {@link #CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS},
+ * {@link #CURSOR_UPDATE_FILTER_TEXT_APPEARANCE}.
*
* <p>Pass {@code 0} to disable them. However, if an unknown flag is provided, request will be
* rejected and method will return {@code false}.</p>
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 515b95c..82cf073 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -1648,6 +1648,7 @@
*
* @param userId user ID to query
* @return {@link List} of {@link InputMethodInfo}.
+ * @see #getEnabledInputMethodSubtypeListAsUser(String, boolean, int)
* @hide
*/
@RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -1676,6 +1677,27 @@
}
/**
+ * Returns a list of enabled input method subtypes for the specified input method info for the
+ * specified user.
+ *
+ * @param imeId IME ID to be queried about.
+ * @param allowsImplicitlyEnabledSubtypes {@code true} to include implicitly enabled subtypes.
+ * @param userId user ID to be queried about.
+ * {@link Manifest.permission#INTERACT_ACROSS_USERS_FULL} is required if this is
+ * different from the calling process user ID.
+ * @return {@link List} of {@link InputMethodSubtype}.
+ * @see #getEnabledInputMethodListAsUser(int)
+ * @hide
+ */
+ @NonNull
+ @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+ public List<InputMethodSubtype> getEnabledInputMethodSubtypeListAsUser(
+ @NonNull String imeId, boolean allowsImplicitlyEnabledSubtypes, @UserIdInt int userId) {
+ return IInputMethodManagerGlobalInvoker.getEnabledInputMethodSubtypeList(
+ Objects.requireNonNull(imeId), allowsImplicitlyEnabledSubtypes, userId);
+ }
+
+ /**
* @deprecated Use {@link InputMethodService#showStatusIcon(int)} instead. This method was
* intended for IME developers who should be accessing APIs through the service. APIs in this
* class are intended for app developers interacting with the IME.
diff --git a/core/java/android/window/RemoteTransition.java b/core/java/android/window/RemoteTransition.java
index 4bd15f2..4cc7ec5 100644
--- a/core/java/android/window/RemoteTransition.java
+++ b/core/java/android/window/RemoteTransition.java
@@ -38,9 +38,18 @@
/** The application thread that will be running the remote transition. */
private @Nullable IApplicationThread mAppThread;
+ /** A name for this that can be used for debugging. */
+ private @Nullable String mDebugName;
+
/** Constructs with no app thread (animation runs in shell). */
public RemoteTransition(@NonNull IRemoteTransition remoteTransition) {
- this(remoteTransition, null /* appThread */);
+ this(remoteTransition, null /* appThread */, null /* debugName */);
+ }
+
+ /** Constructs with no app thread (animation runs in shell). */
+ public RemoteTransition(@NonNull IRemoteTransition remoteTransition,
+ @Nullable String debugName) {
+ this(remoteTransition, null /* appThread */, debugName);
}
/** Get the IBinder associated with the underlying IRemoteTransition. */
@@ -70,15 +79,19 @@
* The actual remote-transition interface used to run the transition animation.
* @param appThread
* The application thread that will be running the remote transition.
+ * @param debugName
+ * A name for this that can be used for debugging.
*/
@DataClass.Generated.Member
public RemoteTransition(
@NonNull IRemoteTransition remoteTransition,
- @Nullable IApplicationThread appThread) {
+ @Nullable IApplicationThread appThread,
+ @Nullable String debugName) {
this.mRemoteTransition = remoteTransition;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mRemoteTransition);
this.mAppThread = appThread;
+ this.mDebugName = debugName;
// onConstructed(); // You can define this method to get a callback
}
@@ -100,6 +113,14 @@
}
/**
+ * A name for this that can be used for debugging.
+ */
+ @DataClass.Generated.Member
+ public @Nullable String getDebugName() {
+ return mDebugName;
+ }
+
+ /**
* The actual remote-transition interface used to run the transition animation.
*/
@DataClass.Generated.Member
@@ -119,6 +140,15 @@
return this;
}
+ /**
+ * A name for this that can be used for debugging.
+ */
+ @DataClass.Generated.Member
+ public @NonNull RemoteTransition setDebugName(@NonNull String value) {
+ mDebugName = value;
+ return this;
+ }
+
@Override
@DataClass.Generated.Member
public String toString() {
@@ -127,7 +157,8 @@
return "RemoteTransition { " +
"remoteTransition = " + mRemoteTransition + ", " +
- "appThread = " + mAppThread +
+ "appThread = " + mAppThread + ", " +
+ "debugName = " + mDebugName +
" }";
}
@@ -139,9 +170,11 @@
byte flg = 0;
if (mAppThread != null) flg |= 0x2;
+ if (mDebugName != null) flg |= 0x4;
dest.writeByte(flg);
dest.writeStrongInterface(mRemoteTransition);
if (mAppThread != null) dest.writeStrongInterface(mAppThread);
+ if (mDebugName != null) dest.writeString(mDebugName);
}
@Override
@@ -158,11 +191,13 @@
byte flg = in.readByte();
IRemoteTransition remoteTransition = IRemoteTransition.Stub.asInterface(in.readStrongBinder());
IApplicationThread appThread = (flg & 0x2) == 0 ? null : IApplicationThread.Stub.asInterface(in.readStrongBinder());
+ String debugName = (flg & 0x4) == 0 ? null : in.readString();
this.mRemoteTransition = remoteTransition;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, mRemoteTransition);
this.mAppThread = appThread;
+ this.mDebugName = debugName;
// onConstructed(); // You can define this method to get a callback
}
@@ -182,10 +217,10 @@
};
@DataClass.Generated(
- time = 1630690027011L,
+ time = 1678926409863L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/window/RemoteTransition.java",
- inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
+ inputSignatures = "private @android.annotation.NonNull android.window.IRemoteTransition mRemoteTransition\nprivate @android.annotation.Nullable android.app.IApplicationThread mAppThread\nprivate @android.annotation.Nullable java.lang.String mDebugName\npublic @android.annotation.Nullable android.os.IBinder asBinder()\nclass RemoteTransition extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true, genSetters=true, genAidl=true)")
@Deprecated
private void __metadata() {}
diff --git a/core/java/android/window/TransitionInfo.java b/core/java/android/window/TransitionInfo.java
index 4c482460..0f3eef7 100644
--- a/core/java/android/window/TransitionInfo.java
+++ b/core/java/android/window/TransitionInfo.java
@@ -149,8 +149,11 @@
/** The task is launching behind home. */
public static final int FLAG_TASK_LAUNCHING_BEHIND = 1 << 19;
+ /** The task became the top-most task even if it didn't change visibility. */
+ public static final int FLAG_MOVED_TO_TOP = 1 << 20;
+
/** The first unused bit. This can be used by remotes to attach custom flags to this change. */
- public static final int FLAG_FIRST_CUSTOM = 1 << 20;
+ public static final int FLAG_FIRST_CUSTOM = 1 << 21;
/** The change belongs to a window that won't contain activities. */
public static final int FLAGS_IS_NON_APP_WINDOW =
@@ -179,6 +182,7 @@
FLAG_BACK_GESTURE_ANIMATED,
FLAG_NO_ANIMATION,
FLAG_TASK_LAUNCHING_BEHIND,
+ FLAG_MOVED_TO_TOP,
FLAG_FIRST_CUSTOM
})
public @interface ChangeFlags {}
@@ -190,6 +194,9 @@
private AnimationOptions mOptions;
+ /** This is only a BEST-EFFORT id used for log correlation. DO NOT USE for any real work! */
+ private int mDebugId = -1;
+
/** @hide */
public TransitionInfo(@TransitionType int type, @TransitionFlags int flags) {
mType = type;
@@ -202,6 +209,7 @@
in.readTypedList(mChanges, Change.CREATOR);
in.readTypedList(mRoots, Root.CREATOR);
mOptions = in.readTypedObject(AnimationOptions.CREATOR);
+ mDebugId = in.readInt();
}
@Override
@@ -212,6 +220,7 @@
dest.writeTypedList(mChanges);
dest.writeTypedList(mRoots, flags);
dest.writeTypedObject(mOptions, flags);
+ dest.writeInt(mDebugId);
}
@NonNull
@@ -347,11 +356,24 @@
return (mFlags & TRANSIT_FLAG_KEYGUARD_GOING_AWAY) != 0;
}
+ /**
+ * Set an arbitrary "debug" id for this info. This id will not be used for any "real work",
+ * it is just for debugging and logging.
+ */
+ public void setDebugId(int id) {
+ mDebugId = id;
+ }
+
+ /** Get the "debug" id of this info. Do NOT use this for real work, only use for debugging. */
+ public int getDebugId() {
+ return mDebugId;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
- sb.append("{t=").append(transitTypeToString(mType)).append(" f=0x")
- .append(Integer.toHexString(mFlags)).append(" r=[");
+ sb.append("{id=").append(mDebugId).append(" t=").append(transitTypeToString(mType))
+ .append(" f=0x").append(Integer.toHexString(mFlags)).append(" r=[");
for (int i = 0; i < mRoots.size(); ++i) {
if (i > 0) {
sb.append(',');
@@ -510,6 +532,7 @@
*/
public TransitionInfo localRemoteCopy() {
final TransitionInfo out = new TransitionInfo(mType, mFlags);
+ out.mDebugId = mDebugId;
for (int i = 0; i < mChanges.size(); ++i) {
out.mChanges.add(mChanges.get(i).localRemoteCopy());
}
diff --git a/core/java/android/window/WindowContainerTransaction.java b/core/java/android/window/WindowContainerTransaction.java
index ad20432..9f804b1 100644
--- a/core/java/android/window/WindowContainerTransaction.java
+++ b/core/java/android/window/WindowContainerTransaction.java
@@ -1586,61 +1586,109 @@
return mShortcutInfo;
}
+ /** Gets a string representation of a hierarchy-op type. */
+ public static String hopToString(int type) {
+ switch (type) {
+ case HIERARCHY_OP_TYPE_REPARENT: return "reparent";
+ case HIERARCHY_OP_TYPE_REORDER: return "reorder";
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT: return "ChildrenTasksReparent";
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: return "SetLaunchRoot";
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS: return "SetAdjacentRoot";
+ case HIERARCHY_OP_TYPE_LAUNCH_TASK: return "LaunchTask";
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT: return "SetAdjacentFlagRoot";
+ case HIERARCHY_OP_TYPE_PENDING_INTENT: return "PendingIntent";
+ case HIERARCHY_OP_TYPE_START_SHORTCUT: return "StartShortcut";
+ case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER: return "addInsetsFrameProvider";
+ case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
+ return "removeInsetsFrameProvider";
+ case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP: return "setAlwaysOnTop";
+ case HIERARCHY_OP_TYPE_REMOVE_TASK: return "RemoveTask";
+ case HIERARCHY_OP_TYPE_FINISH_ACTIVITY: return "finishActivity";
+ case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS: return "ClearAdjacentRoot";
+ case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
+ return "setReparentLeafTaskIfRelaunch";
+ case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
+ return "addTaskFragmentOperation";
+ default: return "HOP(" + type + ")";
+ }
+ }
+
@Override
public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("{").append(hopToString(mType)).append(": ");
switch (mType) {
case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
- return "{ChildrenTasksReparent: from=" + mContainer + " to=" + mReparent
- + " mToTop=" + mToTop + " mReparentTopOnly=" + mReparentTopOnly
- + " mWindowingMode=" + Arrays.toString(mWindowingModes)
- + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+ sb.append("from=").append(mContainer).append(" to=").append(mReparent)
+ .append(" mToTop=").append(mToTop)
+ .append(" mReparentTopOnly=").append(mReparentTopOnly)
+ .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+ .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+ break;
case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT:
- return "{SetLaunchRoot: container=" + mContainer
- + " mWindowingMode=" + Arrays.toString(mWindowingModes)
- + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+ sb.append("container=").append(mContainer)
+ .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+ .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
+ break;
case HIERARCHY_OP_TYPE_REPARENT:
- return "{reparent: " + mContainer + " to " + (mToTop ? "top of " : "bottom of ")
- + mReparent + "}";
+ sb.append(mContainer).append(" to ").append(mToTop ? "top of " : "bottom of ")
+ .append(mReparent);
+ break;
case HIERARCHY_OP_TYPE_REORDER:
- return "{reorder: " + mContainer + " to " + (mToTop ? "top" : "bottom") + "}";
+ sb.append(mContainer).append(" to ").append(mToTop ? "top" : "bottom");
+ break;
case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
- return "{SetAdjacentRoot: container=" + mContainer
- + " adjacentRoot=" + mReparent + "}";
+ sb.append("container=").append(mContainer)
+ .append(" adjacentRoot=").append(mReparent);
+ break;
case HIERARCHY_OP_TYPE_LAUNCH_TASK:
- return "{LaunchTask: " + mLaunchOptions + "}";
+ sb.append(mLaunchOptions);
+ break;
case HIERARCHY_OP_TYPE_SET_LAUNCH_ADJACENT_FLAG_ROOT:
- return "{SetAdjacentFlagRoot: container=" + mContainer + " clearRoot=" + mToTop
- + "}";
+ sb.append("container=").append(mContainer).append(" clearRoot=").append(mToTop);
+ break;
case HIERARCHY_OP_TYPE_START_SHORTCUT:
- return "{StartShortcut: options=" + mLaunchOptions + " info=" + mShortcutInfo
- + "}";
+ sb.append("options=").append(mLaunchOptions)
+ .append(" info=").append(mShortcutInfo);
+ break;
+ case HIERARCHY_OP_TYPE_PENDING_INTENT:
+ sb.append("options=").append(mLaunchOptions);
+ break;
case HIERARCHY_OP_TYPE_ADD_INSETS_FRAME_PROVIDER:
- return "{addRectInsetsProvider: container=" + mContainer
- + " provider=" + mInsetsFrameProvider + "}";
case HIERARCHY_OP_TYPE_REMOVE_INSETS_FRAME_PROVIDER:
- return "{removeLocalInsetsProvider: container=" + mContainer
- + " provider=" + mInsetsFrameProvider + "}";
+ sb.append("container=").append(mContainer)
+ .append(" provider=").append(mInsetsFrameProvider);
+ break;
case HIERARCHY_OP_TYPE_SET_ALWAYS_ON_TOP:
- return "{setAlwaysOnTop: container=" + mContainer
- + " alwaysOnTop=" + mAlwaysOnTop + "}";
+ sb.append("container=").append(mContainer)
+ .append(" alwaysOnTop=").append(mAlwaysOnTop);
+ break;
case HIERARCHY_OP_TYPE_REMOVE_TASK:
- return "{RemoveTask: task=" + mContainer + "}";
+ sb.append("task=").append(mContainer);
+ break;
case HIERARCHY_OP_TYPE_FINISH_ACTIVITY:
- return "{finishActivity: activity=" + mContainer + "}";
+ sb.append("activity=").append(mContainer);
+ break;
case HIERARCHY_OP_TYPE_CLEAR_ADJACENT_ROOTS:
- return "{ClearAdjacentRoot: container=" + mContainer + "}";
+ sb.append("container=").append(mContainer);
+ break;
case HIERARCHY_OP_TYPE_SET_REPARENT_LEAF_TASK_IF_RELAUNCH:
- return "{setReparentLeafTaskIfRelaunch: container= " + mContainer
- + " reparentLeafTaskIfRelaunch= " + mReparentLeafTaskIfRelaunch + "}";
+ sb.append("container= ").append(mContainer)
+ .append(" reparentLeafTaskIfRelaunch= ")
+ .append(mReparentLeafTaskIfRelaunch);
+ break;
case HIERARCHY_OP_TYPE_ADD_TASK_FRAGMENT_OPERATION:
- return "{addTaskFragmentOperation: fragmentToken= " + mContainer
- + " operation= " + mTaskFragmentOperation + "}";
+ sb.append("fragmentToken= ").append(mContainer)
+ .append(" operation= ").append(mTaskFragmentOperation);
+ break;
default:
- return "{mType=" + mType + " container=" + mContainer + " reparent=" + mReparent
- + " mToTop=" + mToTop
- + " mWindowingMode=" + Arrays.toString(mWindowingModes)
- + " mActivityType=" + Arrays.toString(mActivityTypes) + "}";
+ sb.append("container=").append(mContainer)
+ .append(" reparent=").append(mReparent)
+ .append(" mToTop=").append(mToTop)
+ .append(" mWindowingMode=").append(Arrays.toString(mWindowingModes))
+ .append(" mActivityType=").append(Arrays.toString(mActivityTypes));
}
+ return sb.append("}").toString();
}
@Override
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 0cb87fe..7ad2a68 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -524,11 +524,6 @@
public static final String DEFAULT_QR_CODE_SCANNER = "default_qr_code_scanner";
/**
- * (boolean) Whether the task manager entrypoint is enabled.
- */
- public static final String TASK_MANAGER_ENABLED = "task_manager_enabled";
-
- /**
* (boolean) Whether the task manager should show an attention grabbing dot when tasks changed.
*/
public static final String TASK_MANAGER_SHOW_FOOTER_DOT = "task_manager_show_footer_dot";
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index f7c03cd..ae58626 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -28,6 +28,7 @@
import android.media.MediaRoute2Info;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
+import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
import com.android.internal.statusbar.IAddTileResultCallback;
@@ -141,7 +142,7 @@
void addQsTile(in ComponentName tile);
void remQsTile(in ComponentName tile);
void clickQsTile(in ComponentName tile);
- void handleSystemKey(in int key);
+ void handleSystemKey(in KeyEvent key);
/**
* Methods to show toast messages for screen pinning
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index c1dbc87..3708859 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -29,6 +29,7 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
+import android.view.KeyEvent;
import android.service.notification.StatusBarNotification;
import com.android.internal.logging.InstanceId;
@@ -110,7 +111,7 @@
void remTile(in ComponentName tile);
void clickTile(in ComponentName tile);
@UnsupportedAppUsage
- void handleSystemKey(in int key);
+ void handleSystemKey(in KeyEvent key);
int getLastSystemKey();
/**
diff --git a/core/jni/android_hardware_input_InputWindowHandle.cpp b/core/jni/android_hardware_input_InputWindowHandle.cpp
index 241320f..416d991 100644
--- a/core/jni/android_hardware_input_InputWindowHandle.cpp
+++ b/core/jni/android_hardware_input_InputWindowHandle.cpp
@@ -74,6 +74,7 @@
WeakRefHandleField touchableRegionSurfaceControl;
jfieldID transform;
jfieldID windowToken;
+ jfieldID focusTransferTarget;
} gInputWindowHandleClassInfo;
static struct {
@@ -216,6 +217,17 @@
mInfo.windowToken.clear();
}
+ ScopedLocalRef<jobject>
+ focusTransferTargetObj(env,
+ env->GetObjectField(obj,
+ gInputWindowHandleClassInfo
+ .focusTransferTarget));
+ if (focusTransferTargetObj.get()) {
+ mInfo.focusTransferTarget = ibinderForJavaObject(env, focusTransferTargetObj.get());
+ } else {
+ mInfo.focusTransferTarget.clear();
+ }
+
env->DeleteLocalRef(obj);
return true;
}
@@ -433,6 +445,9 @@
GET_FIELD_ID(gInputWindowHandleClassInfo.windowToken, clazz, "windowToken",
"Landroid/os/IBinder;");
+ GET_FIELD_ID(gInputWindowHandleClassInfo.focusTransferTarget, clazz, "focusTransferTarget",
+ "Landroid/os/IBinder;");
+
jclass weakRefClazz;
FIND_CLASS(weakRefClazz, "java/lang/ref/Reference");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 03d6eec..e42c6f1 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -1820,17 +1820,11 @@
}
static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
- jobject toTokenObj, jstring windowNameJstr,
- jobject focusedTokenObj, jstring focusedWindowNameJstr,
- jint displayId) {
+ jobject toTokenObj, jstring windowNameJstr, jint displayId) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
if (toTokenObj == NULL) return;
sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
- sp<IBinder> focusedToken;
- if (focusedTokenObj != NULL) {
- focusedToken = ibinderForJavaObject(env, focusedTokenObj);
- }
FocusRequest request;
request.token = toToken;
@@ -1839,11 +1833,6 @@
request.windowName = windowName.c_str();
}
- request.focusedToken = focusedToken;
- if (focusedWindowNameJstr != NULL) {
- ScopedUtfChars focusedWindowName(env, focusedWindowNameJstr);
- request.focusedWindowName = focusedWindowName.c_str();
- }
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
request.displayId = displayId;
transaction->setFocusedWindow(request);
@@ -2236,7 +2225,7 @@
(void*)nativeGetHandle },
{"nativeSetFixedTransformHint", "(JJI)V",
(void*)nativeSetFixedTransformHint},
- {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;Landroid/os/IBinder;Ljava/lang/String;I)V",
+ {"nativeSetFocusedWindow", "(JLandroid/os/IBinder;Ljava/lang/String;I)V",
(void*)nativeSetFocusedWindow},
{"nativeRemoveCurrentInputFocus", "(JI)V",
(void*)nativeRemoveCurrentInputFocus},
diff --git a/core/proto/android/server/windowmanagerservice.proto b/core/proto/android/server/windowmanagerservice.proto
index e6c8557..bb3089b 100644
--- a/core/proto/android/server/windowmanagerservice.proto
+++ b/core/proto/android/server/windowmanagerservice.proto
@@ -391,6 +391,9 @@
optional int32 last_drop_input_mode = 36;
optional int32 override_orientation = 37 [(.android.typedef) = "android.content.pm.ActivityInfo.ScreenOrientation"];
optional bool should_send_compat_fake_focus = 38;
+ optional bool should_force_rotate_for_camera_compat = 39;
+ optional bool should_refresh_activity_for_camera_compat = 40;
+ optional bool should_refresh_activity_via_pause_for_camera_compat = 41;
}
/* represents WindowToken */
diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 2e3d145..a64bb21 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -250,16 +250,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -282,7 +282,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault" parent="Theme.DeviceDefaultBase" />
@@ -345,16 +344,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -377,7 +376,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar. This theme
@@ -439,16 +437,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -471,7 +469,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with no action bar and no status bar and
@@ -535,16 +532,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -567,7 +564,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault} that has no title bar and translucent
@@ -630,16 +626,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -662,7 +658,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for dialog windows and activities. This changes the window to be
@@ -733,16 +728,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -765,7 +760,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} that has a nice minimum width for a
@@ -827,16 +821,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -859,7 +853,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog} without an action bar -->
@@ -920,16 +913,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -952,7 +945,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Dialog_NoActionBar} that has a nice minimum width
@@ -1014,16 +1006,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1046,7 +1038,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -1124,16 +1115,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1156,7 +1147,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for a window without an action bar that will be displayed either
@@ -1219,16 +1209,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1251,7 +1241,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for a presentation window on a secondary display. -->
@@ -1312,16 +1301,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1344,7 +1333,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for panel windows. This removes all extraneous window
@@ -1407,16 +1395,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1439,7 +1427,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1501,16 +1488,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1533,7 +1520,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault theme for windows that want to have the user's selected wallpaper appear
@@ -1595,16 +1581,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1627,7 +1613,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -1689,16 +1674,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1721,7 +1706,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -1783,16 +1767,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1815,7 +1799,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -1877,16 +1860,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -1909,7 +1892,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Theme for the dialog shown when an app crashes or ANRs. -->
@@ -1976,16 +1958,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2008,7 +1990,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Dialog.NoFrame" parent="Theme.Material.Dialog.NoFrame">
@@ -2068,16 +2049,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2100,7 +2081,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault} with a light-colored style -->
@@ -2298,16 +2278,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2330,8 +2310,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of the DeviceDefault (light) theme that has a solid (opaque) action bar with an
@@ -2393,16 +2371,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2425,8 +2403,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar -->
@@ -2487,16 +2463,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2519,8 +2495,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar.
@@ -2582,16 +2556,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2614,8 +2588,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} with no action bar and no status bar
@@ -2679,16 +2651,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2711,8 +2683,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light} that has no title bar and translucent
@@ -2775,16 +2745,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2807,8 +2777,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be
@@ -2877,16 +2845,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -2909,8 +2877,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} that has a nice minimum width for a
@@ -2975,16 +2941,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3007,11 +2973,9 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
- <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
+ <!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog} without an action bar -->
<style name="Theme.DeviceDefault.Light.Dialog.NoActionBar" parent="Theme.Material.Light.Dialog.NoActionBar">
<item name="windowTitleStyle">@style/DialogWindowTitle.DeviceDefault.Light</item>
<item name="windowAnimationStyle">@style/Animation.DeviceDefault.Dialog</item>
@@ -3072,16 +3036,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3104,8 +3068,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of {@link #Theme_DeviceDefault_Light_Dialog_NoActionBar} that has a nice minimum
@@ -3170,16 +3132,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3202,8 +3164,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. -->
@@ -3249,16 +3209,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3281,8 +3241,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. -->
@@ -3328,16 +3286,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3360,8 +3318,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault light theme for a window that will be displayed either full-screen on smaller
@@ -3426,16 +3382,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3458,8 +3414,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault light theme for a window without an action bar that will be displayed either
@@ -3525,16 +3479,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3557,8 +3511,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault light theme for a presentation window on a secondary display. -->
@@ -3622,16 +3574,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3654,8 +3606,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault light theme for panel windows. This removes all extraneous window
@@ -3718,16 +3668,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3750,8 +3700,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert" parent="Theme.Material.Light.Dialog.Alert">
@@ -3813,16 +3761,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3845,8 +3793,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<style name="Theme.DeviceDefault.Dialog.Alert.DayNight" parent="Theme.DeviceDefault.Light.Dialog.Alert" />
@@ -3908,16 +3854,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -3940,8 +3886,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<style name="Theme.DeviceDefault.Light.Voice" parent="Theme.Material.Light.Voice">
@@ -4001,16 +3945,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4033,8 +3977,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- DeviceDefault theme for a window that should look like the Settings app. -->
@@ -4347,16 +4289,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4379,7 +4321,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Settings.DialogBase" parent="Theme.Material.Light.BaseDialog">
@@ -4543,16 +4484,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4575,7 +4516,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4639,16 +4579,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4671,7 +4611,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4813,16 +4752,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4845,8 +4784,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<!-- Theme overlay that replaces colorAccent with the colorAccent from {@link #Theme_DeviceDefault_DayNight}. -->
@@ -4870,16 +4807,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4902,7 +4839,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.Light.Dialog.Alert.UserSwitchingDialog" parent="Theme.DeviceDefault.NoActionBar.Fullscreen">
@@ -4922,16 +4858,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4954,8 +4890,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_light</item>
<item name="materialColorOnSurface">@color/system_on_surface_light</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
-
</style>
<style name="Theme.DeviceDefault.Notification" parent="@style/Theme.Material.Notification">
@@ -4986,16 +4920,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -5018,7 +4952,6 @@
<item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
<item name="materialColorOnSurface">@color/system_on_surface_dark</item>
<item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
-
</style>
<style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
<item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml
index bf8ca8b..4cccf8e 100644
--- a/core/tests/coretests/AndroidManifest.xml
+++ b/core/tests/coretests/AndroidManifest.xml
@@ -76,7 +76,8 @@
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
- <uses-permission android:name="android.permission.WRITE_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_WRITE_SYNC_DISABLED_MODE_CONFIG" />
+ <uses-permission android:name="android.permission.WRITE_ALLOWLISTED_DEVICE_CONFIG" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java
index 361dc59..e04f13c 100644
--- a/graphics/java/android/graphics/HardwareBufferRenderer.java
+++ b/graphics/java/android/graphics/HardwareBufferRenderer.java
@@ -275,11 +275,22 @@
Consumer<RenderResult> wrapped = consumable -> executor.execute(
() -> renderCallback.accept(consumable));
if (!isClosed()) {
+ int renderWidth;
+ int renderHeight;
+ if (mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_90
+ || mTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_270) {
+ renderWidth = mHardwareBuffer.getHeight();
+ renderHeight = mHardwareBuffer.getWidth();
+ } else {
+ renderWidth = mHardwareBuffer.getWidth();
+ renderHeight = mHardwareBuffer.getHeight();
+ }
+
nRender(
mProxy,
mTransform,
- mHardwareBuffer.getWidth(),
- mHardwareBuffer.getHeight(),
+ renderWidth,
+ renderHeight,
mColorSpace.getNativeInstance(),
wrapped);
} else {
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 e2cd7a0..d8e2f5c 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
@@ -54,6 +54,7 @@
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
+import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.freeform.FreeformComponents;
import com.android.wm.shell.freeform.FreeformTaskListener;
@@ -677,13 +678,15 @@
SyncTransactionQueue syncQueue,
RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
Transitions transitions,
- EnterDesktopTaskTransitionHandler transitionHandler,
+ EnterDesktopTaskTransitionHandler enterDesktopTransitionHandler,
+ ExitDesktopTaskTransitionHandler exitDesktopTransitionHandler,
@DynamicOverride DesktopModeTaskRepository desktopModeTaskRepository,
@ShellMainThread ShellExecutor mainExecutor
) {
return new DesktopTasksController(context, shellInit, shellController, displayController,
shellTaskOrganizer, syncQueue, rootTaskDisplayAreaOrganizer, transitions,
- transitionHandler, desktopModeTaskRepository, mainExecutor);
+ enterDesktopTransitionHandler, exitDesktopTransitionHandler,
+ desktopModeTaskRepository, mainExecutor);
}
@WMSingleton
@@ -695,6 +698,15 @@
@WMSingleton
@Provides
+ static ExitDesktopTaskTransitionHandler provideExitDesktopTaskTransitionHandler(
+ Transitions transitions,
+ Context context
+ ) {
+ return new ExitDesktopTaskTransitionHandler(transitions, context);
+ }
+
+ @WMSingleton
+ @Provides
@DynamicOverride
static DesktopModeTaskRepository provideDesktopModeTaskRepository() {
return new DesktopModeTaskRepository();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
index cb04a43..c35cd5a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt
@@ -68,7 +68,8 @@
private val syncQueue: SyncTransactionQueue,
private val rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
private val transitions: Transitions,
- private val animationTransitionHandler: EnterDesktopTaskTransitionHandler,
+ private val enterDesktopTaskTransitionHandler: EnterDesktopTaskTransitionHandler,
+ private val exitDesktopTaskTransitionHandler: ExitDesktopTaskTransitionHandler,
private val desktopModeTaskRepository: DesktopModeTaskRepository,
@ShellMainThread private val mainExecutor: ShellExecutor
) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {
@@ -149,7 +150,7 @@
wct.setBounds(taskInfo.token, startBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- animationTransitionHandler.startTransition(
+ enterDesktopTaskTransitionHandler.startTransition(
Transitions.TRANSIT_ENTER_FREEFORM, wct)
} else {
shellTaskOrganizer.applyTransaction(wct)
@@ -167,7 +168,8 @@
wct.setBounds(taskInfo.token, freeformBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
- animationTransitionHandler.startTransition(Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
+ enterDesktopTaskTransitionHandler.startTransition(
+ Transitions.TRANSIT_ENTER_DESKTOP_MODE, wct)
} else {
shellTaskOrganizer.applyTransaction(wct)
}
@@ -191,6 +193,18 @@
}
}
+ fun moveToFullscreenWithAnimation(task: ActivityManager.RunningTaskInfo) {
+ val wct = WindowContainerTransaction()
+ addMoveToFullscreenChanges(wct, task.token)
+
+ if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ exitDesktopTaskTransitionHandler.startTransition(
+ Transitions.TRANSIT_EXIT_DESKTOP_MODE, wct)
+ } else {
+ shellTaskOrganizer.applyTransaction(wct)
+ }
+ }
+
/** Move a task to the front **/
fun moveTaskToFront(taskInfo: ActivityManager.RunningTaskInfo) {
val wct = WindowContainerTransaction()
@@ -396,9 +410,9 @@
val statusBarHeight = displayController
.getDisplayLayout(taskInfo.displayId)?.stableInsets()?.top ?: 0
if (y <= statusBarHeight && taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
- moveToFullscreen(taskInfo.taskId)
visualIndicator?.releaseFullscreenIndicator()
visualIndicator = null
+ moveToFullscreenWithAnimation(taskInfo)
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
new file mode 100644
index 0000000..d18e98a
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandler.java
@@ -0,0 +1,162 @@
+/*
+ * 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.desktopmode;
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import android.animation.ValueAnimator;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.TransitionInfo;
+import android.window.TransitionRequestInfo;
+import android.window.WindowContainerTransaction;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.transition.Transitions;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Supplier;
+
+
+/**
+ * The {@link Transitions.TransitionHandler} that handles transitions for desktop mode tasks
+ * entering and exiting freeform.
+ */
+public class ExitDesktopTaskTransitionHandler implements Transitions.TransitionHandler {
+ private static final int FULLSCREEN_ANIMATION_DURATION = 336;
+ private final Context mContext;
+ private final Transitions mTransitions;
+ private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
+
+ private Supplier<SurfaceControl.Transaction> mTransactionSupplier;
+
+ public ExitDesktopTaskTransitionHandler(
+ Transitions transitions,
+ Context context) {
+ this(transitions, SurfaceControl.Transaction::new, context);
+ }
+
+ private ExitDesktopTaskTransitionHandler(
+ Transitions transitions,
+ Supplier<SurfaceControl.Transaction> supplier,
+ Context context) {
+ mTransitions = transitions;
+ mTransactionSupplier = supplier;
+ mContext = context;
+ }
+
+ /**
+ * Starts Transition of a given type
+ * @param type Transition type
+ * @param wct WindowContainerTransaction for transition
+ */
+ public void startTransition(@WindowManager.TransitionType int type,
+ @NonNull WindowContainerTransaction wct) {
+ final IBinder token = mTransitions.startTransition(type, wct, this);
+ mPendingTransitionTokens.add(token);
+ }
+
+ @Override
+ public boolean startAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull SurfaceControl.Transaction finishT,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ boolean transitionHandled = false;
+ for (TransitionInfo.Change change : info.getChanges()) {
+ if ((change.getFlags() & TransitionInfo.FLAG_IS_WALLPAPER) != 0) {
+ continue;
+ }
+
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null || taskInfo.taskId == -1) {
+ continue;
+ }
+
+ if (change.getMode() == WindowManager.TRANSIT_CHANGE) {
+ transitionHandled |= startChangeTransition(
+ transition, info.getType(), change, startT, finishCallback);
+ }
+ }
+
+ mPendingTransitionTokens.remove(transition);
+
+ return transitionHandled;
+ }
+
+ @VisibleForTesting
+ boolean startChangeTransition(
+ @NonNull IBinder transition,
+ @WindowManager.TransitionType int type,
+ @NonNull TransitionInfo.Change change,
+ @NonNull SurfaceControl.Transaction startT,
+ @NonNull Transitions.TransitionFinishCallback finishCallback) {
+ if (!mPendingTransitionTokens.contains(transition)) {
+ return false;
+ }
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (type == Transitions.TRANSIT_EXIT_DESKTOP_MODE
+ && taskInfo.getWindowingMode() == WINDOWING_MODE_FULLSCREEN) {
+ // This Transition animates a task to fullscreen after being dragged to status bar
+ final Resources resources = mContext.getResources();
+ final DisplayMetrics metrics = resources.getDisplayMetrics();
+ final int screenWidth = metrics.widthPixels;
+ final int screenHeight = metrics.heightPixels;
+ final SurfaceControl sc = change.getLeash();
+ startT.setCrop(sc, null);
+ startT.apply();
+ final ValueAnimator animator = new ValueAnimator();
+ animator.setFloatValues(0f, 1f);
+ animator.setDuration(FULLSCREEN_ANIMATION_DURATION);
+ final Rect startBounds = change.getStartAbsBounds();
+ final float scaleX = (float) startBounds.width() / screenWidth;
+ final float scaleY = (float) startBounds.height() / screenHeight;
+ final SurfaceControl.Transaction t = mTransactionSupplier.get();
+ Point startPos = new Point(startBounds.left,
+ startBounds.top);
+ animator.addUpdateListener(animation -> {
+ float fraction = animation.getAnimatedFraction();
+ float currentScaleX = scaleX + ((1 - scaleX) * fraction);
+ float currentScaleY = scaleY + ((1 - scaleY) * fraction);
+ t.setPosition(sc, startPos.x * (1 - fraction), startPos.y * (1 - fraction));
+ t.setScale(sc, currentScaleX, currentScaleY);
+ t.apply();
+ });
+ animator.start();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Nullable
+ @Override
+ public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
+ @NonNull TransitionRequestInfo request) {
+ return null;
+ }
+}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
index afddfab..ec09827 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/OWNERS
@@ -1,2 +1,3 @@
# WM shell sub-module pip owner
hwwang@google.com
+mateuszc@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f2f30ea..984c3c1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -73,6 +73,7 @@
import android.window.TaskSnapshot;
import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
+import android.window.WindowContainerTransactionCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
@@ -142,6 +143,23 @@
protected final ShellTaskOrganizer mTaskOrganizer;
protected final ShellExecutor mMainExecutor;
+ // the runnable to execute after WindowContainerTransactions is applied to finish resizing pip
+ private Runnable mPipFinishResizeWCTRunnable;
+
+ private final WindowContainerTransactionCallback mPipFinishResizeWCTCallback =
+ new WindowContainerTransactionCallback() {
+ @Override
+ public void onTransactionReady(int id, SurfaceControl.Transaction t) {
+ t.apply();
+
+ // execute the runnable if non-null after WCT is applied to finish resizing pip
+ if (mPipFinishResizeWCTRunnable != null) {
+ mPipFinishResizeWCTRunnable.run();
+ mPipFinishResizeWCTRunnable = null;
+ }
+ }
+ };
+
// These callbacks are called on the update thread
private final PipAnimationController.PipAnimationCallback mPipAnimationCallback =
new PipAnimationController.PipAnimationCallback() {
@@ -1249,8 +1267,23 @@
/**
* Animates resizing of the pinned stack given the duration and start bounds.
* This is used when the starting bounds is not the current PiP bounds.
+ *
+ * @param pipFinishResizeWCTRunnable callback to run after window updates are complete
*/
public void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
+ float startingAngle, Consumer<Rect> updateBoundsCallback,
+ Runnable pipFinishResizeWCTRunnable) {
+ mPipFinishResizeWCTRunnable = pipFinishResizeWCTRunnable;
+ if (mPipFinishResizeWCTRunnable != null) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "mPipFinishResizeWCTRunnable is set to be called once window updates");
+ }
+
+ scheduleAnimateResizePip(fromBounds, toBounds, duration, startingAngle,
+ updateBoundsCallback);
+ }
+
+ private void scheduleAnimateResizePip(Rect fromBounds, Rect toBounds, int duration,
float startingAngle, Consumer<Rect> updateBoundsCallback) {
if (mWaitForFixedRotation) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
@@ -1555,7 +1588,7 @@
mSplitScreenOptional.ifPresent(splitScreenController ->
splitScreenController.enterSplitScreen(mTaskInfo.taskId, wasPipTopLeft, wct));
} else {
- mTaskOrganizer.applyTransaction(wct);
+ mTaskOrganizer.applySyncTransaction(wct, mPipFinishResizeWCTCallback);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index fee9140..956af70 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -580,8 +580,16 @@
final float snapFraction = mPipBoundsAlgorithm.getSnapFraction(
mLastResizeBounds, movementBounds);
mPipBoundsAlgorithm.applySnapFraction(mLastResizeBounds, snapFraction);
+
+ // disable the pinch resizing until the final bounds are updated
+ final boolean prevEnablePinchResize = mEnablePinchResize;
+ mEnablePinchResize = false;
+
mPipTaskOrganizer.scheduleAnimateResizePip(startBounds, mLastResizeBounds,
- PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback);
+ PINCH_RESIZE_SNAP_DURATION, mAngle, mUpdateResizeBoundsCallback, () -> {
+ // reset the pinch resizing to its default state
+ mEnablePinchResize = prevEnablePinchResize;
+ });
} else {
mPipTaskOrganizer.scheduleFinishResizePip(mLastResizeBounds,
PipAnimationController.TRANSITION_DIRECTION_USER_RESIZE,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
index 5c64177..c8d6a5e8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/recents/RecentsTransitionHandler.java
@@ -456,7 +456,10 @@
cancel(mWillFinishToHome);
return;
}
- hasChangingApp = true;
+ // Don't consider order-only changes as changing apps.
+ if (!TransitionUtil.isOrderOnly(change)) {
+ hasChangingApp = true;
+ }
}
}
if (hasChangingApp && foundRecentsClosing) {
@@ -484,13 +487,14 @@
}
boolean didMergeThings = false;
if (closingTasks != null) {
- // Cancelling a task-switch. Move the tasks back to mPausing from mOpening
+ // Potentially cancelling a task-switch. Move the tasks back to mPausing if they
+ // are in mOpening.
for (int i = 0; i < closingTasks.size(); ++i) {
final TransitionInfo.Change change = closingTasks.get(i);
int openingIdx = TaskState.indexOf(mOpeningTasks, change);
if (openingIdx < 0) {
- Slog.e(TAG, "Back to existing recents animation from an unrecognized "
- + "task: " + change.getTaskInfo().taskId);
+ Slog.w(TAG, "Closing a task that wasn't opening, this may be split or"
+ + " something unexpected: " + change.getTaskInfo().taskId);
continue;
}
mPausingTasks.add(mOpeningTasks.remove(openingIdx));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
index d52abf7..5a92f78 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultMixedHandler.java
@@ -235,6 +235,7 @@
private TransitionInfo subCopy(@NonNull TransitionInfo info,
@WindowManager.TransitionType int newType, boolean withChanges) {
final TransitionInfo out = new TransitionInfo(newType, withChanges ? info.getFlags() : 0);
+ out.setDebugId(info.getDebugId());
if (withChanges) {
for (int i = 0; i < info.getChanges().size(); ++i) {
out.getChanges().add(info.getChanges().get(i));
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 63c7969..3dd10a0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -301,8 +301,8 @@
return true;
}
- // check if no-animation and skip animation if so.
- if (Transitions.isAllNoAnimation(info)) {
+ // Early check if the transition doesn't warrant an animation.
+ if (Transitions.isAllNoAnimation(info) || Transitions.isAllOrderOnly(info)) {
startTransaction.apply();
finishTransaction.apply();
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
index 485b400..4e3d220 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/OneShotRemoteHandler.java
@@ -63,7 +63,7 @@
@NonNull Transitions.TransitionFinishCallback finishCallback) {
if (mTransition != transition) return false;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
- + " transition %s for %s.", mRemote, transition);
+ + " transition %s for #%d.", mRemote, info.getDebugId());
final IBinder.DeathRecipient remoteDied = () -> {
Log.e(Transitions.TAG, "Remote transition died, finishing");
@@ -113,9 +113,6 @@
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Using registered One-shot remote"
- + " transition %s for %s.", mRemote, transition);
-
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
@Override
public void onTransitionFinished(WindowContainerTransaction wct,
@@ -154,4 +151,10 @@
+ " for %s: %s", transition, remote);
return new WindowContainerTransaction();
}
+
+ @Override
+ public String toString() {
+ return "OneShotRemoteHandler:" + mRemote.getDebugName() + ":"
+ + mRemote.getRemoteTransition();
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
index 3c4e889..5b7231c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/RemoteTransitionHandler.java
@@ -101,8 +101,8 @@
}
RemoteTransition pendingRemote = mRequestedRemotes.get(transition);
if (pendingRemote == null) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s doesn't have "
- + "explicit remote, search filters for match for %s", transition, info);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition doesn't have "
+ + "explicit remote, search filters for match for %s", info);
// If no explicit remote, search filters until one matches
for (int i = mFilters.size() - 1; i >= 0; --i) {
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Checking filter %s",
@@ -116,8 +116,8 @@
}
}
}
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for %s to %s",
- transition, pendingRemote);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Delegate animation for #%d to %s",
+ info.getDebugId(), pendingRemote);
if (pendingRemote == null) return false;
@@ -184,9 +184,10 @@
public void mergeAnimation(@NonNull IBinder transition, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull IBinder mergeTarget,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
- final IRemoteTransition remote = mRequestedRemotes.get(mergeTarget).getRemoteTransition();
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt merge %s into %s",
- transition, remote);
+ final RemoteTransition remoteTransition = mRequestedRemotes.get(mergeTarget);
+ final IRemoteTransition remote = remoteTransition.getRemoteTransition();
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Merge into remote: %s",
+ remoteTransition);
if (remote == null) return;
IRemoteTransitionFinishedCallback cb = new IRemoteTransitionFinishedCallback.Stub() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
index 0386ec3..1879bf7 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/SleepHandler.java
@@ -19,7 +19,6 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.IBinder;
-import android.util.Log;
import android.view.SurfaceControl;
import android.window.TransitionInfo;
import android.window.TransitionRequestInfo;
@@ -59,7 +58,6 @@
@Override
public void onTransitionConsumed(@NonNull IBinder transition, boolean aborted,
@Nullable SurfaceControl.Transaction finishTransaction) {
- Log.e(Transitions.TAG, "Sleep transition was consumed. This doesn't make sense");
mSleepTransitions.remove(transition);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
index 4284993..681fa51 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/Transitions.java
@@ -140,6 +140,9 @@
/** Transition type to freeform in desktop mode. */
public static final int TRANSIT_ENTER_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 11;
+ /** Transition type to fullscreen from desktop mode. */
+ public static final int TRANSIT_EXIT_DESKTOP_MODE = WindowManager.TRANSIT_FIRST_CUSTOM + 12;
+
private final WindowOrganizer mOrganizer;
private final Context mContext;
private final ShellExecutor mMainExecutor;
@@ -182,6 +185,14 @@
/** Ordered list of transitions which have been merged into this one. */
private ArrayList<ActiveTransition> mMerged;
+
+ @Override
+ public String toString() {
+ if (mInfo != null && mInfo.getDebugId() >= 0) {
+ return "(#" + mInfo.getDebugId() + ")" + mToken;
+ }
+ return mToken.toString();
+ }
}
/** Keeps track of transitions which have been started, but aren't ready yet. */
@@ -516,6 +527,16 @@
return hasNoAnimation;
}
+ /**
+ * Check if all changes in this transition are only ordering changes. If so, we won't animate.
+ */
+ static boolean isAllOrderOnly(TransitionInfo info) {
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ if (!TransitionUtil.isOrderOnly(info.getChanges().get(i))) return false;
+ }
+ return true;
+ }
+
@VisibleForTesting
void onTransitionReady(@NonNull IBinder transitionToken, @NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl.Transaction finishT) {
@@ -529,8 +550,8 @@
activeTransition -> activeTransition.mToken).toArray()));
}
if (activeIdx > 0) {
- Log.e(TAG, "Transition became ready out-of-order " + transitionToken + ". Expected"
- + " order: " + Arrays.toString(mPendingTransitions.stream().map(
+ Log.e(TAG, "Transition became ready out-of-order " + mPendingTransitions.get(activeIdx)
+ + ". Expected order: " + Arrays.toString(mPendingTransitions.stream().map(
activeTransition -> activeTransition.mToken).toArray()));
}
// Move from pending to ready
@@ -547,6 +568,7 @@
if (info.getType() == TRANSIT_SLEEP) {
if (activeIdx > 0 || !mActiveTransitions.isEmpty() || mReadyTransitions.size() > 1) {
// Sleep starts a process of forcing all prior transitions to finish immediately
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Start finish-for-sleep");
finishForSleep(null /* forceFinish */);
return;
}
@@ -555,8 +577,8 @@
if (info.getRootCount() == 0 && !alwaysReportToKeyguard(info)) {
// No root-leashes implies that the transition is empty/no-op, so just do
// housekeeping and return.
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots (%s): %s",
- transitionToken, info);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "No transition roots in %s so"
+ + " abort", active);
onAbort(active);
return;
}
@@ -585,6 +607,8 @@
&& allOccluded)) {
// Treat this as an abort since we are bypassing any merge logic and effectively
// finishing immediately.
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Non-visible anim so abort: %s", active);
onAbort(active);
return;
}
@@ -652,21 +676,21 @@
return;
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition %s ready while"
- + " another transition %s is still animating. Notify the animating transition"
- + " in case they can be merged", ready.mToken, playing.mToken);
+ + " %s is still animating. Notify the animating transition"
+ + " in case they can be merged", ready, playing);
playing.mHandler.mergeAnimation(ready.mToken, ready.mInfo, ready.mStartT,
playing.mToken, (wct, cb) -> onMerged(playing, ready));
}
private void onMerged(@NonNull ActiveTransition playing, @NonNull ActiveTransition merged) {
- ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged %s",
- merged.mToken);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition was merged: %s into %s",
+ merged, playing);
int readyIdx = 0;
if (mReadyTransitions.isEmpty() || mReadyTransitions.get(0) != merged) {
- Log.e(TAG, "Merged transition out-of-order?");
+ Log.e(TAG, "Merged transition out-of-order? " + merged);
readyIdx = mReadyTransitions.indexOf(merged);
if (readyIdx < 0) {
- Log.e(TAG, "Merged a transition that is no-longer queued?");
+ Log.e(TAG, "Merged a transition that is no-longer queued? " + merged);
return;
}
}
@@ -687,6 +711,7 @@
}
private void playTransition(@NonNull ActiveTransition active) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Playing animation for %s", active);
for (int i = 0; i < mObservers.size(); ++i) {
mObservers.get(i).onTransitionStarting(active.mToken);
}
@@ -788,12 +813,12 @@
int activeIdx = mActiveTransitions.indexOf(active);
if (activeIdx < 0) {
Log.e(TAG, "Trying to finish a non-running transition. Either remote crashed or "
- + " a handler didn't properly deal with a merge. " + active.mToken,
+ + " a handler didn't properly deal with a merge. " + active,
new RuntimeException());
return;
} else if (activeIdx != 0) {
// Relevant right now since we only allow 1 active transition at a time.
- Log.e(TAG, "Finishing a transition out of order. " + active.mToken);
+ Log.e(TAG, "Finishing a transition out of order. " + active);
}
mActiveTransitions.remove(activeIdx);
@@ -801,7 +826,7 @@
mObservers.get(i).onTransitionFinished(active.mToken, active.mAborted);
}
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Transition animation finished "
- + "(aborted=%b), notifying core %s", active.mAborted, active.mToken);
+ + "(aborted=%b), notifying core %s", active.mAborted, active);
if (active.mStartT != null) {
// Applied by now, so clear immediately to remove any references. Do not set to null
// yet, though, since nullness is used later to disambiguate malformed transitions.
@@ -917,6 +942,8 @@
/** Start a new transition directly. */
public IBinder startTransition(@WindowManager.TransitionType int type,
@NonNull WindowContainerTransaction wct, @Nullable TransitionHandler handler) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, "Directly starting a new transition "
+ + "type=%d wct=%s handler=%s", type, wct, handler);
final ActiveTransition active = new ActiveTransition();
active.mHandler = handler;
active.mToken = mOrganizer.startNewTransition(type, wct);
@@ -948,8 +975,7 @@
return;
}
if (forceFinish != null && mActiveTransitions.contains(forceFinish)) {
- Log.e(TAG, "Forcing transition to finish due to sleep timeout: "
- + forceFinish.mToken);
+ Log.e(TAG, "Forcing transition to finish due to sleep timeout: " + forceFinish);
forceFinish.mAborted = true;
// Last notify of it being consumed. Note: mHandler should never be null,
// but check just to be safe.
@@ -967,6 +993,8 @@
// Try to signal that we are sleeping by attempting to merge the sleep transition
// into the playing one.
final ActiveTransition nextSleep = mReadyTransitions.get(sleepIdx);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_TRANSITIONS, " Attempt to merge SLEEP %s"
+ + " into %s", nextSleep, playing);
playing.mHandler.mergeAnimation(nextSleep.mToken, nextSleep.mInfo, dummyT,
playing.mToken, (wct, cb) -> {});
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
index 7595c96..ce10291 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/util/TransitionUtil.java
@@ -31,6 +31,7 @@
import static android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY;
import static android.window.TransitionInfo.FLAG_IS_DISPLAY;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT;
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
@@ -90,6 +91,15 @@
&& !change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY);
}
+ /** Returns `true` if `change` is only re-ordering. */
+ public static boolean isOrderOnly(TransitionInfo.Change change) {
+ return change.getMode() == TRANSIT_CHANGE
+ && (change.getFlags() & FLAG_MOVED_TO_TOP) != 0
+ && change.getStartAbsBounds().equals(change.getEndAbsBounds())
+ && (change.getLastParent() == null
+ || change.getLastParent().equals(change.getParent()));
+ }
+
/**
* Filter that selects leaf-tasks only. THIS IS ORDER-DEPENDENT! For it to work properly, you
* MUST call `test` in the same order that the changes appear in the TransitionInfo.
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 91846fa..e986ee1 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
@@ -218,36 +218,36 @@
assertLayers {
if (landscapePosLeft) {
splitAppLayerBoundsSnapToDivider(
- component,
- landscapePosLeft,
- portraitPosTop,
- scenario.endRotation
- )
- .then()
- .isInvisible(component)
- .then()
- .splitAppLayerBoundsSnapToDivider(
- component,
- landscapePosLeft,
- portraitPosTop,
- scenario.endRotation
- )
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ .then()
+ .isInvisible(component)
+ .then()
+ .splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
} else {
splitAppLayerBoundsSnapToDivider(
- component,
- landscapePosLeft,
- portraitPosTop,
- scenario.endRotation
- )
- .then()
- .isInvisible(component)
- .then()
- .splitAppLayerBoundsSnapToDivider(
- component,
- landscapePosLeft,
- portraitPosTop,
- scenario.endRotation
- )
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
+ .then()
+ .isInvisible(component)
+ .then()
+ .splitAppLayerBoundsSnapToDivider(
+ component,
+ landscapePosLeft,
+ portraitPosTop,
+ scenario.endRotation
+ )
}
}
}
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 1045a5a..93ee699 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
@@ -40,6 +40,7 @@
* Select "Auto-enter PiP" 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]
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 2d2588e..59918fb 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
@@ -38,6 +38,7 @@
* Launch an app in pip mode [pipApp],
* Swipe the pip window to the bottom-center of the screen and wait it disappear
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 e540ad5..d165832 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
@@ -38,6 +38,7 @@
* Click on the pip window
* Click on dismiss button and wait window disappear
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 e079d547..db18edb 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
@@ -53,6 +53,7 @@
* Launch [pipApp] on a fixed landscape orientation
* Broadcast action [ACTION_ENTER_PIP] to enter pip mode
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 e40e5ea..51f0136 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
@@ -44,9 +44,7 @@
@Presubmit
@Test
open fun pipAppLayerAlwaysVisible() {
- flicker.assertLayers {
- this.isVisible(pipApp)
- }
+ flicker.assertLayers { this.isVisible(pipApp) }
}
/** Checks the content overlay appears then disappears during the animation */
@@ -55,11 +53,7 @@
open fun pipOverlayLayerAppearThenDisappear() {
val overlay = ComponentNameMatcher.PIP_CONTENT_OVERLAY
flicker.assertLayers {
- this.notContains(overlay)
- .then()
- .contains(overlay)
- .then()
- .notContains(overlay)
+ this.notContains(overlay).then().contains(overlay).then().notContains(overlay)
}
}
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 1f060e9..f1925d8 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
@@ -35,6 +35,7 @@
* Launch an app in full screen
* Press an "enter pip" button to put [pipApp] in pip mode
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 313631c..43f7e00b 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
@@ -42,6 +42,7 @@
* Expand [pipApp] app to full screen by clicking on the pip window and
* then on the expand button
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 93ffdd8d..617b3da 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
@@ -41,6 +41,7 @@
* Launch another full screen mode [testApp]
* Expand [pipApp] app to full screen via an intent
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 7d5f740..6deba1b 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
@@ -40,6 +40,7 @@
* Launch an app in pip mode [pipApp],
* Expand [pipApp] app to its maximum pip size by double clicking on it
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 9c00744..d8d57d2 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
@@ -40,6 +40,7 @@
* Launch [testApp]
* Check if pip window moves down (visually)
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 c8d5624..ae3f879 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
@@ -40,6 +40,7 @@
* Press home
* Check if pip window moves up (visually)
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
index 083cfd2..4e2a4e7 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragTest.kt
@@ -30,9 +30,7 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/**
- * Test the dragging of a PIP window.
- */
+/** Test the dragging of a PIP window. */
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@@ -59,9 +57,7 @@
pipApp.exit(wmHelper)
tapl.setEnableRotation(false)
}
- transitions {
- pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50)
- }
+ transitions { pipApp.dragPipWindowAwayFromEdgeWithoutRelease(wmHelper, 50) }
}
@Postsubmit
@@ -92,4 +88,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
index 53ce393..9fe9f52 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipDragThenSnapTest.kt
@@ -16,10 +16,10 @@
package com.android.wm.shell.flicker.pip
+import android.graphics.Rect
import android.platform.test.annotations.Postsubmit
import android.tools.common.Rotation
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
-import android.graphics.Rect
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.FlickerTest
import android.tools.device.flicker.legacy.FlickerTestFactory
@@ -33,14 +33,12 @@
import org.junit.runners.MethodSorters
import org.junit.runners.Parameterized
-/**
- * Test the snapping of a PIP window via dragging, releasing, and checking its final location.
- */
+/** Test the snapping of a PIP window via dragging, releasing, and checking its final location. */
@RequiresDevice
@RunWith(Parameterized::class)
@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
-class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker){
+class PipDragThenSnapTest(flicker: FlickerTest) : PipTransition(flicker) {
// represents the direction in which the pip window should be snapping
private var willSnapRight: Boolean = true
@@ -60,8 +58,12 @@
// get the initial region bounds and cache them
val initRegion = pipApp.getWindowRect(wmHelper)
- startBounds
- .set(initRegion.left, initRegion.top, initRegion.right, initRegion.bottom)
+ startBounds.set(
+ initRegion.left,
+ initRegion.top,
+ initRegion.right,
+ initRegion.bottom
+ )
// drag the pip window away from the edge
pipApp.dragPipWindowAwayFromEdge(wmHelper, 50)
@@ -108,4 +110,4 @@
)
}
}
-}
\ No newline at end of file
+}
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 2cf8f61..703784d 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
@@ -43,6 +43,7 @@
* Rotate the screen from [flicker.scenario.startRotation] to [flicker.scenario.endRotation]
* (usually, 0->90 and 90->0)
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 17f174b..8938a2c 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
@@ -107,9 +107,7 @@
}
}
- @Presubmit
- @Test
- fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
+ @Presubmit @Test fun primaryAppWindowKeepVisible() = flicker.appWindowKeepVisible(primaryApp)
@Presubmit
@Test
@@ -136,9 +134,7 @@
)
/** {@inheritDoc} */
- @Presubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
companion object {
@Parameterized.Parameters(name = "{0}")
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 5b06c9c..fc006ab 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
@@ -79,8 +79,13 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
- appExistAtStart = false)
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
@Presubmit
@Test
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 c840183..95de744 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
@@ -82,8 +82,8 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, sendNotificationApp,
- fromOtherApp = false)
+ fun cujCompleted() =
+ flicker.splitScreenEntered(primaryApp, sendNotificationApp, fromOtherApp = false)
@Presubmit
@Test
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 5c99209..533d5dd 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
@@ -80,8 +80,13 @@
@IwTest(focusArea = "sysui")
@Presubmit
@Test
- fun cujCompleted() = flicker.splitScreenEntered(primaryApp, secondaryApp, fromOtherApp = false,
- appExistAtStart = false)
+ fun cujCompleted() =
+ flicker.splitScreenEntered(
+ primaryApp,
+ secondaryApp,
+ fromOtherApp = false,
+ appExistAtStart = false
+ )
@Presubmit
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
index 4ccc467..c9bd695 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt
@@ -81,7 +81,8 @@
@Mock lateinit var syncQueue: SyncTransactionQueue
@Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@Mock lateinit var transitions: Transitions
- @Mock lateinit var transitionHandler: EnterDesktopTaskTransitionHandler
+ @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
+ @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
lateinit var mockitoSession: StaticMockitoSession
lateinit var controller: DesktopTasksController
@@ -117,7 +118,8 @@
syncQueue,
rootTaskDisplayAreaOrganizer,
transitions,
- transitionHandler,
+ enterDesktopTransitionHandler,
+ exitDesktopTransitionHandler,
desktopModeTaskRepository,
TestShellExecutor()
)
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
new file mode 100644
index 0000000..2c5a5cd
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/ExitDesktopTaskTransitionHandlerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.desktopmode;
+
+
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+
+import static androidx.test.internal.runner.junit4.statement.UiThreadStatement.runOnUiThread;
+
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.WindowConfiguration;
+import android.content.Context;
+import android.content.res.Resources;
+import android.os.IBinder;
+import android.util.DisplayMetrics;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+import android.window.IWindowContainerToken;
+import android.window.TransitionInfo;
+import android.window.WindowContainerToken;
+import android.window.WindowContainerTransaction;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.wm.shell.ShellTestCase;
+import com.android.wm.shell.common.ShellExecutor;
+import com.android.wm.shell.transition.Transitions;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.function.Supplier;
+
+/** Tests of {@link com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler} */
+@SmallTest
+public class ExitDesktopTaskTransitionHandlerTest extends ShellTestCase {
+
+ @Mock
+ private Transitions mTransitions;
+ @Mock
+ IBinder mToken;
+ @Mock
+ Supplier<SurfaceControl.Transaction> mTransactionFactory;
+ @Mock
+ Context mContext;
+ @Mock
+ DisplayMetrics mDisplayMetrics;
+ @Mock
+ Resources mResources;
+ @Mock
+ SurfaceControl.Transaction mStartT;
+ @Mock
+ SurfaceControl.Transaction mFinishT;
+ @Mock
+ SurfaceControl.Transaction mAnimationT;
+ @Mock
+ Transitions.TransitionFinishCallback mTransitionFinishCallback;
+ @Mock
+ ShellExecutor mExecutor;
+
+ private ExitDesktopTaskTransitionHandler mExitDesktopTaskTransitionHandler;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(mExecutor).when(mTransitions).getMainExecutor();
+ doReturn(mAnimationT).when(mTransactionFactory).get();
+ doReturn(mResources).when(mContext).getResources();
+ doReturn(mDisplayMetrics).when(mResources).getDisplayMetrics();
+ when(mResources.getDisplayMetrics())
+ .thenReturn(getContext().getResources().getDisplayMetrics());
+
+ mExitDesktopTaskTransitionHandler = new ExitDesktopTaskTransitionHandler(mTransitions,
+ mContext);
+ }
+
+ @Test
+ public void testTransitExitDesktopModeAnimation() throws Throwable {
+ final int transitionType = Transitions.TRANSIT_EXIT_DESKTOP_MODE;
+ final int taskId = 1;
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ doReturn(mToken).when(mTransitions)
+ .startTransition(transitionType, wct, mExitDesktopTaskTransitionHandler);
+
+ mExitDesktopTaskTransitionHandler.startTransition(transitionType, wct);
+
+ TransitionInfo.Change change =
+ createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FULLSCREEN);
+ TransitionInfo info = createTransitionInfo(Transitions.TRANSIT_EXIT_DESKTOP_MODE, change);
+ ArrayList<Exception> exceptions = new ArrayList<>();
+ runOnUiThread(() -> {
+ try {
+ assertTrue(mExitDesktopTaskTransitionHandler
+ .startAnimation(mToken, info, mStartT, mFinishT,
+ mTransitionFinishCallback));
+ } catch (Exception e) {
+ exceptions.add(e);
+ }
+ });
+ if (!exceptions.isEmpty()) {
+ throw exceptions.get(0);
+ }
+ }
+
+ private TransitionInfo.Change createChange(@WindowManager.TransitionType int type, int taskId,
+ @WindowConfiguration.WindowingMode int windowingMode) {
+ final ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.taskId = taskId;
+ taskInfo.token = new WindowContainerToken(mock(IWindowContainerToken.class));
+ taskInfo.configuration.windowConfiguration.setWindowingMode(windowingMode);
+ SurfaceControl.Builder b = new SurfaceControl.Builder()
+ .setName("test task");
+ final TransitionInfo.Change change = new TransitionInfo.Change(
+ taskInfo.token, b.build());
+ change.setMode(type);
+ change.setTaskInfo(taskInfo);
+ return change;
+ }
+
+ private static TransitionInfo createTransitionInfo(
+ @WindowManager.TransitionType int type, @NonNull TransitionInfo.Change change) {
+ TransitionInfo info = new TransitionInfo(type, 0);
+ info.addChange(change);
+ return info;
+ }
+
+}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
index 5b62a94..ada3455 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipResizeGestureHandlerTest.java
@@ -159,7 +159,7 @@
mPipResizeGestureHandler.onPinchResize(upEvent);
verify(mPipTaskOrganizer, times(1))
- .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+ .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
assertTrue("The new size should be bigger than the original PiP size.",
mPipResizeGestureHandler.getLastResizeBounds().width()
@@ -198,7 +198,7 @@
mPipResizeGestureHandler.onPinchResize(upEvent);
verify(mPipTaskOrganizer, times(1))
- .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any());
+ .scheduleAnimateResizePip(any(), any(), anyInt(), anyFloat(), any(), any());
assertTrue("The new size should be smaller than the original PiP size.",
mPipResizeGestureHandler.getLastResizeBounds().width()
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
index 1b29146..a9f311f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/splitscreen/SplitTransitionTests.java
@@ -181,7 +181,7 @@
IBinder transition = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(testRemote), mStageCoordinator, null, null);
+ new RemoteTransition(testRemote, "Test"), mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
boolean accepted = mStageCoordinator.startAnimation(transition, info,
@@ -407,7 +407,8 @@
TransitionInfo enterInfo = createEnterPairInfo();
IBinder enterTransit = mSplitScreenTransitions.startEnterTransition(
TRANSIT_SPLIT_SCREEN_PAIR_OPEN, new WindowContainerTransaction(),
- new RemoteTransition(new TestRemoteTransition()), mStageCoordinator, null, null);
+ new RemoteTransition(new TestRemoteTransition(), "Test"),
+ mStageCoordinator, null, null);
mMainStage.onTaskAppeared(mMainChild, createMockSurface());
mSideStage.onTaskAppeared(mSideChild, createMockSurface());
mStageCoordinator.startAnimation(enterTransit, enterInfo,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
index 60d6978..5cd548b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/transition/ShellTransitionTests.java
@@ -277,7 +277,7 @@
IBinder transitToken = new Binder();
transitions.requestStartTransition(transitToken,
new TransitionRequestInfo(TRANSIT_OPEN, null /* trigger */,
- new RemoteTransition(testRemote)));
+ new RemoteTransition(testRemote, "Test")));
verify(mOrganizer, times(1)).startTransition(eq(transitToken), any());
TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN)
.addChange(TRANSIT_OPEN).addChange(TRANSIT_CLOSE).build();
@@ -422,7 +422,7 @@
new TransitionFilter.Requirement[]{new TransitionFilter.Requirement()};
filter.mRequirements[0].mModes = new int[]{TRANSIT_OPEN, TRANSIT_TO_FRONT};
- transitions.registerRemote(filter, new RemoteTransition(testRemote));
+ transitions.registerRemote(filter, new RemoteTransition(testRemote, "Test"));
mMainExecutor.flushAll();
IBinder transitToken = new Binder();
@@ -466,11 +466,12 @@
final int transitType = TRANSIT_FIRST_CUSTOM + 1;
OneShotRemoteHandler oneShot = new OneShotRemoteHandler(mMainExecutor,
- new RemoteTransition(testRemote));
+ new RemoteTransition(testRemote, "Test"));
// Verify that it responds to the remote but not other things.
IBinder transitToken = new Binder();
assertNotNull(oneShot.handleRequest(transitToken,
- new TransitionRequestInfo(transitType, null, new RemoteTransition(testRemote))));
+ new TransitionRequestInfo(transitType, null,
+ new RemoteTransition(testRemote, "Test"))));
assertNull(oneShot.handleRequest(transitToken,
new TransitionRequestInfo(transitType, null, null)));
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 3b12972..5d79104 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -738,6 +738,9 @@
"tests/unit/VectorDrawableTests.cpp",
"tests/unit/WebViewFunctorManagerTests.cpp",
],
+ data: [
+ ":hwuimicro",
+ ],
}
// ------------------------
diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
index 768dfcd..706f18c 100644
--- a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp
@@ -85,28 +85,20 @@
}
static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) {
- auto matrix = SkMatrix();
switch (transform) {
case ANATIVEWINDOW_TRANSFORM_ROTATE_90:
- matrix.setRotate(90);
- matrix.postTranslate(width, 0);
- break;
+ return SkMatrix::MakeAll(0, -1, height, 1, 0, 0, 0, 0, 1);
case ANATIVEWINDOW_TRANSFORM_ROTATE_180:
- matrix.setRotate(180);
- matrix.postTranslate(width, height);
- break;
+ return SkMatrix::MakeAll(-1, 0, width, 0, -1, height, 0, 0, 1);
case ANATIVEWINDOW_TRANSFORM_ROTATE_270:
- matrix.setRotate(270);
- matrix.postTranslate(0, width);
- break;
+ return SkMatrix::MakeAll(0, 1, 0, -1, 0, width, 0, 0, 1);
default:
ALOGE("Invalid transform provided. Transform should be validated from"
"the java side. Leveraging identity transform as a fallback");
[[fallthrough]];
case ANATIVEWINDOW_TRANSFORM_IDENTITY:
- break;
+ return SkMatrix::I();
}
- return matrix;
}
static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jlong renderProxy,
@@ -117,8 +109,8 @@
auto skHeight = static_cast<SkScalar>(height);
auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform);
auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr);
- proxy->setHardwareBufferRenderParams(
- HardwareBufferRenderParams(matrix, colorSpace, createRenderCallback(env, consumer)));
+ proxy->setHardwareBufferRenderParams(HardwareBufferRenderParams(
+ width, height, matrix, colorSpace, createRenderCallback(env, consumer)));
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo())
.setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
index 202a62c..cc987bc 100644
--- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp
@@ -69,15 +69,9 @@
}
Frame SkiaOpenGLPipeline::getFrame() {
- if (mHardwareBuffer) {
- AHardwareBuffer_Desc description;
- AHardwareBuffer_describe(mHardwareBuffer, &description);
- return Frame(description.width, description.height, 0);
- } else {
- LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
- "drawRenderNode called on a context with no surface!");
- return mEglManager.beginFrame(mEglSurface);
- }
+ LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE,
+ "drawRenderNode called on a context with no surface!");
+ return mEglManager.beginFrame(mEglSurface);
}
IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
index 99298bc..c8f2e69 100644
--- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp
@@ -66,15 +66,8 @@
}
Frame SkiaVulkanPipeline::getFrame() {
- if (mHardwareBuffer) {
- AHardwareBuffer_Desc description;
- AHardwareBuffer_describe(mHardwareBuffer, &description);
- return Frame(description.width, description.height, 0);
- } else {
- LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr,
- "getFrame() called on a context with no surface!");
- return vulkanManager().dequeueNextBuffer(mVkSurface);
- }
+ LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!");
+ return vulkanManager().dequeueNextBuffer(mVkSurface);
}
IRenderPipeline::DrawResult SkiaVulkanPipeline::draw(
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index dd781bb..6b2c995 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -528,6 +528,14 @@
sendLoadResetHint();
}
+Frame CanvasContext::getFrame() {
+ if (mHardwareBuffer != nullptr) {
+ return {mBufferParams.getLogicalWidth(), mBufferParams.getLogicalHeight(), 0};
+ } else {
+ return mRenderPipeline->getFrame();
+ }
+}
+
void CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
@@ -569,7 +577,8 @@
mCurrentFrameInfo->markIssueDrawCommandsStart();
- Frame frame = mRenderPipeline->getFrame();
+ Frame frame = getFrame();
+
SkRect windowDirty = computeDirtyRect(frame, &dirty);
ATRACE_FORMAT("Drawing " RECT_STRING, SK_RECT_ARGS(dirty));
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index b26c018..3f25339 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -264,6 +264,8 @@
FrameInfo* getFrameInfoFromLast4(uint64_t frameNumber, uint32_t surfaceControlId);
+ Frame getFrame();
+
// The same type as Frame.mWidth and Frame.mHeight
int32_t mLastFrameWidth = 0;
int32_t mLastFrameHeight = 0;
diff --git a/libs/hwui/renderthread/HardwareBufferRenderParams.h b/libs/hwui/renderthread/HardwareBufferRenderParams.h
index 91fe3f6..8c942d0 100644
--- a/libs/hwui/renderthread/HardwareBufferRenderParams.h
+++ b/libs/hwui/renderthread/HardwareBufferRenderParams.h
@@ -36,9 +36,12 @@
class HardwareBufferRenderParams {
public:
HardwareBufferRenderParams() = default;
- HardwareBufferRenderParams(const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
+ HardwareBufferRenderParams(int32_t logicalWidth, int32_t logicalHeight,
+ const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace,
RenderCallback&& callback)
- : mTransform(transform)
+ : mLogicalWidth(logicalWidth)
+ , mLogicalHeight(logicalHeight)
+ , mTransform(transform)
, mColorSpace(colorSpace)
, mRenderCallback(std::move(callback)) {}
const SkMatrix& getTransform() const { return mTransform; }
@@ -50,7 +53,12 @@
}
}
+ int32_t getLogicalWidth() { return mLogicalWidth; }
+ int32_t getLogicalHeight() { return mLogicalHeight; }
+
private:
+ int32_t mLogicalWidth;
+ int32_t mLogicalHeight;
SkMatrix mTransform = SkMatrix::I();
sk_sp<SkColorSpace> mColorSpace = SkColorSpace::MakeSRGB();
RenderCallback mRenderCallback = nullptr;
diff --git a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
index dcf3945..47d8426 100644
--- a/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
+++ b/media/aidl/android/media/soundtrigger_middleware/IInjectGlobalEvent.aidl
@@ -25,14 +25,14 @@
oneway interface IInjectGlobalEvent {
/**
- * Request a fake STHAL restart.
+ * Trigger a fake STHAL restart.
* This invalidates the {@link IInjectGlobalEvent}.
*/
void triggerRestart();
/**
- * Triggers global resource contention into the fake STHAL. Loads/startRecognition
- * will fail with RESOURCE_CONTENTION.
+ * Set global resource contention within the fake STHAL. Loads/startRecognition
+ * will fail with {@code RESOURCE_CONTENTION} until unset.
* @param isContended - true to enable resource contention. false to disable resource contention
* and resume normal functionality.
* @param callback - Call {@link IAcknowledgeEvent#eventReceived()} on this interface once
@@ -40,4 +40,11 @@
*/
void setResourceContention(boolean isContended, IAcknowledgeEvent callback);
+ /**
+ * Trigger an
+ * {@link android.hardware.soundtrigger3.ISoundTriggerHwGlobalCallback#onResourcesAvailable}
+ * callback from the fake STHAL. This callback is used to signal to the framework that
+ * previous operations which failed may now succeed.
+ */
+ void triggerOnResourcesAvailable();
}
diff --git a/native/android/tests/activitymanager/nativeTests/Android.bp b/native/android/tests/activitymanager/nativeTests/Android.bp
index 528ac12..ebd7533 100644
--- a/native/android/tests/activitymanager/nativeTests/Android.bp
+++ b/native/android/tests/activitymanager/nativeTests/Android.bp
@@ -45,4 +45,7 @@
required: [
"UidImportanceHelperApp",
],
+ data: [
+ ":UidImportanceHelperApp",
+ ],
}
diff --git a/packages/CompanionDeviceManager/res/values/strings.xml b/packages/CompanionDeviceManager/res/values/strings.xml
index c898fe5..d87abb9 100644
--- a/packages/CompanionDeviceManager/res/values/strings.xml
+++ b/packages/CompanionDeviceManager/res/values/strings.xml
@@ -20,7 +20,7 @@
<string name="app_label">Companion Device Manager</string>
<!-- Title of the device association confirmation dialog. -->
- <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to access <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong></string>
+ <string name="confirmation_title">Allow <strong><xliff:g id="app_name" example="Android Wear">%1$s</xliff:g></strong> to access <strong><xliff:g id="device_name" example="ASUS ZenWatch 2">%2$s</xliff:g></strong>?</string>
<!-- ================= DEVICE_PROFILE_WATCH and null profile ================= -->
@@ -34,7 +34,7 @@
<string name="summary_watch">This app is needed to manage your <xliff:g id="device_name" example="My Watch">%1$s</xliff:g>. <xliff:g id="app_name" example="Android Wear">%2$s</xliff:g> will be allowed to sync info, like the name of someone calling, interact with your notifications and access your Phone, SMS, Contacts, Calendar, Call logs and Nearby devices permissions.</string>
<!-- Description of the privileges the application will get if associated with the companion device of WATCH profile for singleDevice(type) [CHAR LIMIT=NONE] -->
- <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions</string>
+ <string name="summary_watch_single_device">This app will be allowed to sync info, like the name of someone calling, and access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
<!-- ================= DEVICE_PROFILE_GLASSES ================= -->
@@ -48,7 +48,7 @@
<string name="summary_glasses_multi_device">This app is needed to manage <xliff:g id="device_name" example="My Glasses">%1$s</xliff:g>. <xliff:g id="app_name" example="Glasses">%2$s</xliff:g> will be allowed to interact with your notifications and access your Phone, SMS, Contacts, Microphone and Nearby devices permissions.</string>
<!-- Description of the privileges the application will get if associated with the companion device of GLASSES profile for singleDevice(type) [CHAR LIMIT=NONE] -->
- <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your phone</string>
+ <string name="summary_glasses_single_device">This app will be allowed to access these permissions on your <xliff:g id="device_name" example="phone">%1$s</xliff:g></string>
<!-- ================= DEVICE_PROFILE_APP_STREAMING ================= -->
@@ -194,4 +194,10 @@
<!-- Description of nearby_device_streaming permission of corresponding profile [CHAR LIMIT=NONE] -->
<string name="permission_nearby_device_streaming_summary">Stream apps and other system features from your phone</string>
+ <!-- The type of the device for phone [CHAR LIMIT=30] -->
+ <string name="device_type" product="default">phone</string>
+
+ <!-- The type of the device for tablet [CHAR LIMIT=30] -->
+ <string name="device_type" product="tablet">tablet</string>
+
</resources>
diff --git a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
index ae08823..4154029 100644
--- a/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
+++ b/packages/CompanionDeviceManager/src/com/android/companiondevicemanager/CompanionDeviceActivity.java
@@ -551,7 +551,8 @@
summary = getHtmlFromResources(this, SUMMARIES.get(null), deviceName);
mConstraintList.setVisibility(View.GONE);
} else {
- summary = getHtmlFromResources(this, SUMMARIES.get(deviceProfile));
+ summary = getHtmlFromResources(
+ this, SUMMARIES.get(deviceProfile), getString(R.string.device_type));
mPermissionTypes.addAll(PERMISSION_TYPES.get(deviceProfile));
setupPermissionList();
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index a3d632c..e884cf8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -305,10 +305,11 @@
synchronized (mProfileLock) {
if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
for (CachedBluetoothDevice member : getMemberDevice()) {
- Log.d(TAG, "Disconnect the member(" + member.getAddress() + ")");
+ Log.d(TAG, "Disconnect the member:" + member);
member.disconnect();
}
}
+ Log.d(TAG, "Disconnect " + this);
mDevice.disconnect();
}
// Disconnect PBAP server in case its connected
@@ -440,11 +441,11 @@
Log.d(TAG, "No profiles. Maybe we will connect later for device " + mDevice);
return;
}
-
+ Log.d(TAG, "connect " + this);
mDevice.connect();
if (getGroupId() != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
for (CachedBluetoothDevice member : getMemberDevice()) {
- Log.d(TAG, "connect the member(" + member.getAddress() + ")");
+ Log.d(TAG, "connect the member:" + member);
member.connect();
}
}
@@ -530,7 +531,7 @@
}
// TODO: do any of these need to run async on a background thread?
- private void fillData() {
+ void fillData() {
updateProfiles();
fetchActiveDevices();
migratePhonebookPermissionChoice();
@@ -933,14 +934,15 @@
@Override
public String toString() {
- return "CachedBluetoothDevice ("
+ return "CachedBluetoothDevice{"
+ "anonymizedAddress="
+ mDevice.getAnonymizedAddress()
+ ", name="
+ getName()
+ ", groupId="
+ mGroupId
- + ")";
+ + ", member=" + mMemberDevices
+ + "}";
}
@Override
@@ -1482,6 +1484,7 @@
* Store the member devices that are in the same coordinated set.
*/
public void addMemberDevice(CachedBluetoothDevice memberDevice) {
+ Log.d(TAG, this + " addMemberDevice = " + memberDevice);
mMemberDevices.add(memberDevice);
}
@@ -1511,13 +1514,14 @@
mDevice = newMainDevice.mDevice;
mRssi = newMainDevice.mRssi;
mJustDiscovered = newMainDevice.mJustDiscovered;
+ fillData();
// Set sub device from backup
newMainDevice.release();
newMainDevice.mDevice = tmpDevice;
newMainDevice.mRssi = tmpRssi;
newMainDevice.mJustDiscovered = tmpJustDiscovered;
- fetchActiveDevices();
+ newMainDevice.fillData();
}
/**
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
index 20a6cd8..356bb82 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CsipDeviceManager.java
@@ -71,7 +71,7 @@
return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
}
- for (Map.Entry<Integer, ParcelUuid> entry: groupIdMap.entrySet()) {
+ for (Map.Entry<Integer, ParcelUuid> entry : groupIdMap.entrySet()) {
if (entry.getValue().equals(BluetoothUuid.CAP)) {
return entry.getKey();
}
@@ -153,72 +153,13 @@
return;
}
log("onGroupIdChanged: mCachedDevices list =" + mCachedDevices.toString());
- final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
- final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
- final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
- final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT()) ?
- leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+ List<CachedBluetoothDevice> memberDevicesList = getMemberDevicesList(groupId);
CachedBluetoothDevice newMainDevice =
- mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
- if (newMainDevice != null) {
- final CachedBluetoothDevice finalNewMainDevice = newMainDevice;
- final List<CachedBluetoothDevice> memberDevices = mCachedDevices.stream()
- .filter(cachedDevice -> !cachedDevice.equals(finalNewMainDevice)
- && cachedDevice.getGroupId() == groupId)
- .collect(Collectors.toList());
- if (memberDevices == null || memberDevices.isEmpty()) {
- log("onGroupIdChanged: There is no member device in list.");
- return;
- }
- log("onGroupIdChanged: removed from UI device =" + memberDevices
- + ", with groupId=" + groupId + " mainDevice= " + newMainDevice);
- for (CachedBluetoothDevice memberDeviceItem : memberDevices) {
- Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
- if (!memberSet.isEmpty()) {
- log("onGroupIdChanged: Transfer the member list into new main device.");
- for (CachedBluetoothDevice memberListItem : memberSet) {
- if (!memberListItem.equals(newMainDevice)) {
- newMainDevice.addMemberDevice(memberListItem);
- }
- }
- memberSet.clear();
- }
+ getPreferredMainDeviceWithoutConectionState(groupId, memberDevicesList);
- newMainDevice.addMemberDevice(memberDeviceItem);
- mCachedDevices.remove(memberDeviceItem);
- mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
- }
-
- if (!mCachedDevices.contains(newMainDevice)) {
- mCachedDevices.add(newMainDevice);
- mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
- }
- } else {
- log("onGroupIdChanged: There is no main device from the LE profile.");
- int firstMatchedIndex = -1;
-
- for (int i = mCachedDevices.size() - 1; i >= 0; i--) {
- final CachedBluetoothDevice cachedDevice = mCachedDevices.get(i);
- if (cachedDevice.getGroupId() != groupId) {
- continue;
- }
-
- if (firstMatchedIndex == -1) {
- // Found the first one
- firstMatchedIndex = i;
- newMainDevice = cachedDevice;
- continue;
- }
-
- log("onGroupIdChanged: removed from UI device =" + cachedDevice
- + ", with groupId=" + groupId + " firstMatchedIndex=" + firstMatchedIndex);
-
- newMainDevice.addMemberDevice(cachedDevice);
- mCachedDevices.remove(i);
- mBtManager.getEventManager().dispatchDeviceRemoved(cachedDevice);
- break;
- }
- }
+ log("onGroupIdChanged: The mainDevice= " + newMainDevice
+ + " and the memberDevicesList of groupId= " + groupId + " =" + memberDevicesList);
+ addMemberDevicesIntoMainDevice(memberDevicesList, newMainDevice);
}
// @return {@code true}, the event is processed inside the method. It is for updating
@@ -263,7 +204,7 @@
break;
}
- for (CachedBluetoothDevice device: memberSet) {
+ for (CachedBluetoothDevice device : memberSet) {
if (device.isConnected()) {
log("set device: " + device + " as the main device");
// Main device is disconnected and sub device is connected
@@ -296,7 +237,7 @@
continue;
}
- for (CachedBluetoothDevice memberDevice: memberSet) {
+ for (CachedBluetoothDevice memberDevice : memberSet) {
if (memberDevice != null && memberDevice.equals(device)) {
return cachedDevice;
}
@@ -310,7 +251,6 @@
* Check if the {@code groupId} is existed.
*
* @param groupId The group id
- *
* @return {@code true}, if we could find a device with this {@code groupId}; Otherwise,
* return {@code false}.
*/
@@ -322,6 +262,116 @@
return false;
}
+ private List<CachedBluetoothDevice> getMemberDevicesList(int groupId) {
+ return mCachedDevices.stream()
+ .filter(cacheDevice -> cacheDevice.getGroupId() == groupId)
+ .collect(Collectors.toList());
+ }
+
+ private CachedBluetoothDevice getPreferredMainDeviceWithoutConectionState(int groupId,
+ List<CachedBluetoothDevice> memberDevicesList) {
+ // First, priority connected lead device from LE profile
+ // Second, the DUAL mode device which has A2DP/HFP and LE audio
+ // Last, any one of LE device in the list.
+ if (memberDevicesList == null || memberDevicesList.isEmpty()) {
+ return null;
+ }
+
+ final LocalBluetoothProfileManager profileManager = mBtManager.getProfileManager();
+ final CachedBluetoothDeviceManager deviceManager = mBtManager.getCachedDeviceManager();
+ final LeAudioProfile leAudioProfile = profileManager.getLeAudioProfile();
+ final BluetoothDevice mainBluetoothDevice = (leAudioProfile != null && isAtLeastT())
+ ? leAudioProfile.getConnectedGroupLeadDevice(groupId) : null;
+
+ if (mainBluetoothDevice != null) {
+ log("getPreferredMainDevice: The LeadDevice from LE profile is "
+ + mainBluetoothDevice.getAnonymizedAddress());
+ }
+
+ // 1st
+ CachedBluetoothDevice newMainDevice =
+ mainBluetoothDevice != null ? deviceManager.findDevice(mainBluetoothDevice) : null;
+ if (newMainDevice != null) {
+ if (newMainDevice.isConnected()) {
+ log("getPreferredMainDevice: The connected LeadDevice from LE profile");
+ return newMainDevice;
+ } else {
+ log("getPreferredMainDevice: The LeadDevice is not connect.");
+ }
+ } else {
+ log("getPreferredMainDevice: The LeadDevice is not in the all of devices list");
+ }
+
+ // 2nd
+ newMainDevice = memberDevicesList.stream()
+ .filter(cachedDevice -> cachedDevice.getConnectableProfiles().stream()
+ .anyMatch(profile -> profile instanceof A2dpProfile
+ || profile instanceof HeadsetProfile))
+ .findFirst().orElse(null);
+ if (newMainDevice != null) {
+ log("getPreferredMainDevice: The DUAL mode device");
+ return newMainDevice;
+ }
+
+ // last
+ if (!memberDevicesList.isEmpty()) {
+ newMainDevice = memberDevicesList.get(0);
+ }
+ return newMainDevice;
+ }
+
+ private void addMemberDevicesIntoMainDevice(List<CachedBluetoothDevice> memberDevicesList,
+ CachedBluetoothDevice newMainDevice) {
+ if (newMainDevice == null) {
+ log("addMemberDevicesIntoMainDevice: No main device. Do nothing.");
+ return;
+ }
+ if (memberDevicesList.isEmpty()) {
+ log("addMemberDevicesIntoMainDevice: No member device in list. Do nothing.");
+ return;
+ }
+ CachedBluetoothDevice mainDeviceOfNewMainDevice = findMainDevice(newMainDevice);
+ boolean isMemberInOtherMainDevice = mainDeviceOfNewMainDevice != null;
+ if (!memberDevicesList.contains(newMainDevice) && isMemberInOtherMainDevice) {
+ log("addMemberDevicesIntoMainDevice: The 'new main device' is not in list, and it is "
+ + "the member at other device. Do switch main and member.");
+ // To switch content and dispatch to notify UI change
+ mBtManager.getEventManager().dispatchDeviceRemoved(mainDeviceOfNewMainDevice);
+ mainDeviceOfNewMainDevice.switchMemberDeviceContent(newMainDevice);
+ mainDeviceOfNewMainDevice.refresh();
+ // It is necessary to do remove and add for updating the mapping on
+ // preference and device
+ mBtManager.getEventManager().dispatchDeviceAdded(mainDeviceOfNewMainDevice);
+ } else {
+ log("addMemberDevicesIntoMainDevice: Set new main device");
+ for (CachedBluetoothDevice memberDeviceItem : memberDevicesList) {
+ if (memberDeviceItem.equals(newMainDevice)) {
+ continue;
+ }
+ Set<CachedBluetoothDevice> memberSet = memberDeviceItem.getMemberDevice();
+ if (!memberSet.isEmpty()) {
+ for (CachedBluetoothDevice memberSetItem : memberSet) {
+ if (!memberSetItem.equals(newMainDevice)) {
+ newMainDevice.addMemberDevice(memberSetItem);
+ }
+ }
+ memberSet.clear();
+ }
+
+ newMainDevice.addMemberDevice(memberDeviceItem);
+ mCachedDevices.remove(memberDeviceItem);
+ mBtManager.getEventManager().dispatchDeviceRemoved(memberDeviceItem);
+ }
+
+ if (!mCachedDevices.contains(newMainDevice)) {
+ mCachedDevices.add(newMainDevice);
+ mBtManager.getEventManager().dispatchDeviceAdded(newMainDevice);
+ }
+ }
+ log("addMemberDevicesIntoMainDevice: After changed, CachedBluetoothDevice list: "
+ + mCachedDevices);
+ }
+
private void log(String msg) {
if (DEBUG) {
Log.d(TAG, msg);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
index 1c179f8..6444f3b 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/CachedBluetoothDeviceTest.java
@@ -1150,9 +1150,11 @@
assertThat(mCachedDevice.mRssi).isEqualTo(RSSI_2);
assertThat(mCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_2);
assertThat(mCachedDevice.mDevice).isEqualTo(mSubDevice);
+ verify(mCachedDevice).fillData();
assertThat(mSubCachedDevice.mRssi).isEqualTo(RSSI_1);
assertThat(mSubCachedDevice.mJustDiscovered).isEqualTo(JUSTDISCOVERED_1);
assertThat(mSubCachedDevice.mDevice).isEqualTo(mDevice);
+ verify(mSubCachedDevice).fillData();
assertThat(mCachedDevice.getMemberDevice().contains(mSubCachedDevice)).isTrue();
}
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/OWNERS b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
new file mode 100644
index 0000000..0b71816
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/OWNERS
@@ -0,0 +1 @@
+per-file WritableNamespacePrefixes.java = cbrubaker@google.com,tedbauer@google.com
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
index 5a8c594..3e1b597 100644
--- a/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
+++ b/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java
@@ -2323,7 +2323,15 @@
return;
} else if (hasAllowlistPermission) {
for (String flag : flags) {
- if (!DeviceConfig.getAdbWritableFlags().contains(flag)) {
+ boolean namespaceAllowed = false;
+ for (String allowlistedPrefix : WritableNamespacePrefixes.ALLOWLIST) {
+ if (flag.startsWith(allowlistedPrefix)) {
+ namespaceAllowed = true;
+ break;
+ }
+ }
+
+ if (!namespaceAllowed && !DeviceConfig.getAdbWritableFlags().contains(flag)) {
throw new SecurityException("Permission denial for flag '"
+ flag
+ "'; allowlist permission granted, but must add flag to the allowlist.");
diff --git a/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
new file mode 100644
index 0000000..28f25e0
--- /dev/null
+++ b/packages/SettingsProvider/src/com/android/providers/settings/WritableNamespacePrefixes.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2007 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 android.util.ArraySet;
+
+import java.util.Arrays;
+import java.util.Set;
+
+/**
+ * Contains the list of prefixes for namespaces in which any flag can be written with adb.
+ * <p>
+ * A security review is required for any prefix that's added to this list. To add to
+ * the list, create a change and tag the OWNER. In the change description, include a
+ * description of the flag's functionality, and a justification for why it needs to be
+ * allowlisted.
+ */
+final class WritableNamespacePrefixes {
+ public static final Set<String> ALLOWLIST =
+ new ArraySet<String>(Arrays.asList(
+ "app_compat_overrides",
+ "game_overlay",
+ "namespace1"
+ ));
+}
diff --git a/packages/SystemUI/Android.bp b/packages/SystemUI/Android.bp
index ac75cc8..3007d4a 100644
--- a/packages/SystemUI/Android.bp
+++ b/packages/SystemUI/Android.bp
@@ -182,7 +182,6 @@
"androidx.dynamicanimation_dynamicanimation",
"androidx-constraintlayout_constraintlayout",
"androidx.exifinterface_exifinterface",
- "androidx.test.ext.junit",
"com.google.android.material_material",
"kotlinx_coroutines_android",
"kotlinx_coroutines",
@@ -191,6 +190,7 @@
"SystemUI-proto",
"monet",
"dagger2",
+ "jsr305",
"jsr330",
"lottie",
"LowLightDreamLib",
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
index 91cb4ba..462c90b 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
+++ b/packages/SystemUI/accessibility/accessibilitymenu/res/layout/footerlayout_switch_page.xml
@@ -56,6 +56,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingStart="16dp"
+ android:paddingEnd="16dp"
android:gravity="center_vertical"
android:textColor="@color/colorControlNormal"
android:textSize="@dimen/label_text_size"
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
index 4b6f9a4..02d279f 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/activity/A11yMenuSettingsActivity.java
@@ -24,7 +24,9 @@
import android.os.Bundle;
import android.provider.Browser;
import android.provider.Settings;
+import android.view.View;
+import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentActivity;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
@@ -56,6 +58,13 @@
initializeHelpAndFeedbackPreference();
}
+ @Override
+ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ view.setLayoutDirection(
+ view.getResources().getConfiguration().getLayoutDirection());
+ }
+
/**
* Returns large buttons settings state.
*
diff --git a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
index a25790a..5b7bbe8 100644
--- a/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
+++ b/packages/SystemUI/accessibility/accessibilitymenu/src/com/android/systemui/accessibility/accessibilitymenu/view/A11yMenuOverlayLayout.java
@@ -194,13 +194,15 @@
/** Updates a11y menu layout position by configuring layout params. */
private void updateLayoutPosition() {
final Display display = mDisplayManager.getDisplay(Display.DEFAULT_DISPLAY);
- final int orientation = mService.getResources().getConfiguration().orientation;
+ final Configuration configuration = mService.getResources().getConfiguration();
+ final int orientation = configuration.orientation;
if (display != null && orientation == Configuration.ORIENTATION_LANDSCAPE) {
+ final boolean ltr = configuration.getLayoutDirection() == View.LAYOUT_DIRECTION_LTR;
switch (display.getRotation()) {
- case Surface.ROTATION_90:
+ case Surface.ROTATION_0:
case Surface.ROTATION_180:
mLayoutParameter.gravity =
- Gravity.END | Gravity.BOTTOM
+ (ltr ? Gravity.END : Gravity.START) | Gravity.BOTTOM
| Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
@@ -208,10 +210,10 @@
mLayoutParameter.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
mLayout.setBackgroundResource(R.drawable.shadow_90deg);
break;
- case Surface.ROTATION_0:
+ case Surface.ROTATION_90:
case Surface.ROTATION_270:
mLayoutParameter.gravity =
- Gravity.START | Gravity.BOTTOM
+ (ltr ? Gravity.START : Gravity.END) | Gravity.BOTTOM
| Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL;
mLayoutParameter.width = WindowManager.LayoutParams.WRAP_CONTENT;
mLayoutParameter.height = WindowManager.LayoutParams.MATCH_PARENT;
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 03e1e66..197b217 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -384,8 +384,15 @@
}
@JvmStatic
- fun adaptRemoteAnimation(adapter: RemoteAnimationAdapter): RemoteTransition {
- return RemoteTransition(adaptRemoteRunner(adapter.runner), adapter.callingApplication)
+ fun adaptRemoteAnimation(
+ adapter: RemoteAnimationAdapter,
+ debugName: String
+ ): RemoteTransition {
+ return RemoteTransition(
+ adaptRemoteRunner(adapter.runner),
+ adapter.callingApplication,
+ debugName
+ )
}
}
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 e73afe7..a7e95b5 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
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.Assert
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
@@ -41,6 +42,18 @@
private const val DEBUG = true
private val KEY_TIMESTAMP = "appliedTimestamp"
+private fun <TKey, TVal> ConcurrentHashMap<TKey, TVal>.concurrentGetOrPut(
+ key: TKey,
+ value: TVal,
+ onNew: () -> Unit
+): TVal {
+ val result = this.putIfAbsent(key, value)
+ if (result == null) {
+ onNew()
+ }
+ return result ?: value
+}
+
/** ClockRegistry aggregates providers and plugins */
open class ClockRegistry(
val context: Context,
@@ -64,7 +77,7 @@
fun onAvailableClocksChanged() {}
}
- private val availableClocks = mutableMapOf<ClockId, ClockInfo>()
+ private val availableClocks = ConcurrentHashMap<ClockId, ClockInfo>()
private val clockChangeListeners = mutableListOf<ClockChangeListener>()
private val settingObserver =
object : ContentObserver(null) {
@@ -92,18 +105,12 @@
var isClockListChanged = false
for (clock in plugin.getClocks()) {
val id = clock.clockId
- var isNew = false
val info =
- availableClocks.getOrPut(id) {
- isNew = true
- ClockInfo(clock, plugin, manager)
+ availableClocks.concurrentGetOrPut(id, ClockInfo(clock, plugin, manager)) {
+ isClockListChanged = true
+ onConnected(id)
}
- if (isNew) {
- isClockListChanged = true
- onConnected(id)
- }
-
if (manager != info.manager) {
Log.e(
TAG,
@@ -254,10 +261,8 @@
return
}
- android.util.Log.e("HAWK", "triggerOnCurrentClockChanged")
scope.launch(mainDispatcher) {
assertMainThread()
- android.util.Log.e("HAWK", "isClockChanged")
isClockChanged.set(false)
clockChangeListeners.forEach { it.onCurrentClockChanged() }
}
@@ -270,10 +275,8 @@
return
}
- android.util.Log.e("HAWK", "triggerOnAvailableClocksChanged")
scope.launch(mainDispatcher) {
assertMainThread()
- android.util.Log.e("HAWK", "isClockListChanged")
isClockListChanged.set(false)
clockChangeListeners.forEach { it.onAvailableClocksChanged() }
}
@@ -356,7 +359,7 @@
}
private var isVerifying = AtomicBoolean(false)
- private fun verifyLoadedProviders() {
+ fun verifyLoadedProviders() {
val shouldSchedule = isVerifying.compareAndSet(false, true)
if (!shouldSchedule) {
return
diff --git a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
index b49afee..4b94707 100644
--- a/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
+++ b/packages/SystemUI/res-keyguard/layout/keyguard_clock_switch.xml
@@ -28,9 +28,10 @@
<FrameLayout
android:id="@+id/lockscreen_clock_view"
android:layout_width="wrap_content"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/small_clock_height"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
+ android:clipChildren="false"
android:paddingStart="@dimen/clock_padding_start"
android:visibility="invisible" />
<FrameLayout
diff --git a/packages/SystemUI/res-keyguard/values/dimens.xml b/packages/SystemUI/res-keyguard/values/dimens.xml
index 1f44f05..cad2c16 100644
--- a/packages/SystemUI/res-keyguard/values/dimens.xml
+++ b/packages/SystemUI/res-keyguard/values/dimens.xml
@@ -95,6 +95,7 @@
<dimen name="num_pad_key_margin_end">12dp</dimen>
<!-- additional offset for clock switch area items -->
+ <dimen name="small_clock_height">114dp</dimen>
<dimen name="clock_padding_start">28dp</dimen>
<dimen name="below_clock_padding_start">32dp</dimen>
<dimen name="below_clock_padding_end">16dp</dimen>
diff --git a/packages/SystemUI/res/layout/controls_management.xml b/packages/SystemUI/res/layout/controls_management.xml
index b9e711e..d8967d4 100644
--- a/packages/SystemUI/res/layout/controls_management.xml
+++ b/packages/SystemUI/res/layout/controls_management.xml
@@ -77,6 +77,29 @@
app:layout_constraintStart_toStartOf="parent"/>
<Button
+ android:id="@+id/rearrange"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+
+ <Button
+ android:id="@+id/addControls"
+ android:visibility="gone"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:gravity="center_vertical"
+ android:text="@string/controls_favorite_add_controls"
+ style="@*android:style/Widget.DeviceDefault.Button.Borderless.Colored"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toStartOf="parent"/>
+
+ <Button
android:id="@+id/done"
android:layout_width="wrap_content"
android:layout_height="match_parent"
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 324ba02..1dd12ee 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2471,6 +2471,15 @@
<!-- Controls management favorites screen. See other apps button [CHAR LIMIT=30] -->
<string name="controls_favorite_see_other_apps">See other apps</string>
+ <!-- Controls management favorites screen. Rearrange controls button [CHAR LIMIT=30]-->
+ <string name="controls_favorite_rearrange_button">Rearrange</string>
+
+ <!-- Controls management edit screen. Add controls button [CHAR LIMIT=30]-->
+ <string name="controls_favorite_add_controls">Add controls</string>
+
+ <!-- Controls management edit screen. Return to editing button [CHAR LIMIT=30]-->
+ <string name="controls_favorite_back_to_editing">Back to editing</string>
+
<!-- Controls management controls screen error on load message [CHAR LIMIT=NONE] -->
<string name="controls_favorite_load_error">Controls could not be loaded. Check the <xliff:g id="app" example="System UI">%s</xliff:g> app to make sure that the app settings haven\u2019t changed.</string>
<!-- Controls management controls screen no controls found on load message [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
index 53fab69..cab54d0 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/rotation/RotationButtonController.java
@@ -66,7 +66,6 @@
import java.io.PrintWriter;
import java.util.Optional;
-import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -244,7 +243,12 @@
mListenersRegistered = false;
- mContext.unregisterReceiver(mDockedReceiver);
+ try {
+ mContext.unregisterReceiver(mDockedReceiver);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Docked receiver already unregistered", e);
+ }
+
if (mRotationWatcherRegistered) {
try {
WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
index 44f9d43..f094102 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteAnimationTargetCompat.java
@@ -63,6 +63,7 @@
final ArrayList<RemoteAnimationTarget> out = new ArrayList<>();
for (int i = 0; i < info.getChanges().size(); i++) {
TransitionInfo.Change change = info.getChanges().get(i);
+ if (TransitionUtil.isOrderOnly(change)) continue;
if (filter.test(change)) {
out.add(TransitionUtil.newTarget(
change, info.getChanges().size() - i, info, t, leashMap));
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
index 58e7747..1fbf743 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/RemoteTransitionCompat.java
@@ -89,7 +89,7 @@
}
}
};
- return new RemoteTransition(remote, appThread);
+ return new RemoteTransition(remote, appThread, "Recents");
}
/**
diff --git a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
index 31234cf..c22d689 100644
--- a/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-debug/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
id: Int,
name: String,
namespace: String = "systemui",
- teamfood: Boolean = false
): ReleasedFlag {
- val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
checkForDupesAndAdd(flag)
return flag
}
@@ -55,7 +54,6 @@
@BoolRes resourceId: Int,
name: String,
namespace: String = "systemui",
- teamfood: Boolean = false
): ResourceBooleanFlag {
val flag =
ResourceBooleanFlag(
@@ -63,7 +61,7 @@
name = name,
namespace = namespace,
resourceId = resourceId,
- teamfood = teamfood
+ teamfood = false,
)
checkForDupesAndAdd(flag)
return flag
diff --git a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
index 27c5699..5502da1 100644
--- a/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
+++ b/packages/SystemUI/src-release/com/android/systemui/flags/FlagsFactory.kt
@@ -43,9 +43,8 @@
id: Int,
name: String,
namespace: String = "systemui",
- teamfood: Boolean = false
): ReleasedFlag {
- val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = teamfood)
+ val flag = ReleasedFlag(id = id, name = name, namespace = namespace, teamfood = false)
flagMap[name] = flag
return flag
}
@@ -55,7 +54,6 @@
@BoolRes resourceId: Int,
name: String,
namespace: String = "systemui",
- teamfood: Boolean = false
): ResourceBooleanFlag {
val flag =
ResourceBooleanFlag(
@@ -63,7 +61,7 @@
name = name,
namespace = namespace,
resourceId = resourceId,
- teamfood = teamfood
+ teamfood = false,
)
flagMap[name] = flag
return flag
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
index 9f2333d8..1980f70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardAbsKeyInputViewController.java
@@ -107,14 +107,7 @@
// start fresh
mDismissing = false;
mView.resetPasswordText(false /* animate */, false /* announce */);
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (shouldLockout(deadline)) {
- handleAttemptLockout(deadline);
- } else {
- resetState();
- }
+ resetState();
}
@Override
@@ -277,7 +270,12 @@
@Override
public void onResume(int reason) {
mResumed = true;
- reset();
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (shouldLockout(deadline)) {
+ handleAttemptLockout(deadline);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index cdaed87..07333f7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -390,6 +390,13 @@
PropertyAnimator.setProperty(mStatusArea, AnimatableProperty.TRANSLATION_X,
x, props, animate);
}
+
+ }
+
+ void updateKeyguardStatusViewOffset() {
+ // updateClockTargetRegions will call onTargetRegionChanged
+ // which will require the correct translationY property of keyguardStatusView after updating
+ mView.updateClockTargetRegions();
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
index 68b40ab..5c56aab 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPatternViewController.java
@@ -260,20 +260,18 @@
mLockPatternView.setEnabled(true);
mLockPatternView.clearPattern();
- // if the user is currently locked out, enforce it.
- long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
- KeyguardUpdateMonitor.getCurrentUser());
- if (deadline != 0) {
- handleAttemptLockout(deadline);
- } else {
- displayDefaultSecurityMessage();
- }
+ displayDefaultSecurityMessage();
}
@Override
public void onResume(int reason) {
super.onResume(reason);
- reset();
+ // if the user is currently locked out, enforce it.
+ long deadline = mLockPatternUtils.getLockoutAttemptDeadline(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (deadline != 0) {
+ handleAttemptLockout(deadline);
+ }
}
@Override
@@ -300,34 +298,38 @@
@Override
public void showPromptReason(int reason) {
/// TODO: move all this logic into the MessageAreaController?
+ int resId = 0;
switch (reason) {
case PROMPT_REASON_RESTART:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_restart_pattern);
+ resId = R.string.kg_prompt_reason_restart_pattern;
break;
case PROMPT_REASON_TIMEOUT:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
case PROMPT_REASON_DEVICE_ADMIN:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_device_admin);
+ resId = R.string.kg_prompt_reason_device_admin;
break;
case PROMPT_REASON_USER_REQUEST:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_user_request);
+ resId = R.string.kg_prompt_reason_user_request;
break;
case PROMPT_REASON_PREPARE_FOR_UPDATE:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
case PROMPT_REASON_NON_STRONG_BIOMETRIC_TIMEOUT:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
case PROMPT_REASON_TRUSTAGENT_EXPIRED:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
case PROMPT_REASON_NONE:
break;
default:
- mMessageAreaController.setMessage(R.string.kg_prompt_reason_timeout_pattern);
+ resId = R.string.kg_prompt_reason_timeout_pattern;
break;
}
+ if (resId != 0) {
+ mMessageAreaController.setMessage(getResources().getText(resId), /* animate= */ false);
+ }
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 67874e1..87a7758 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -40,6 +40,7 @@
import android.os.SystemClock;
import android.os.UserHandle;
import android.telephony.TelephonyManager;
+import android.text.TextUtils;
import android.util.Log;
import android.util.MathUtils;
import android.util.Slog;
@@ -64,6 +65,7 @@
import com.android.keyguard.KeyguardSecurityContainer.SwipeListener;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
+import com.android.settingslib.Utils;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
@@ -634,6 +636,16 @@
mKeyguardStateController.isFaceAuthEnabled());
}
+ /** Sets an initial message that would override the default message */
+ public void setInitialMessage() {
+ CharSequence customMessage = mViewMediatorCallback.consumeCustomMessage();
+ if (!TextUtils.isEmpty(customMessage)) {
+ showMessage(customMessage, Utils.getColorError(getContext()));
+ return;
+ }
+ showPromptReason(mViewMediatorCallback.getBouncerPromptReason());
+ }
+
/**
* Show the bouncer and start appear animations.
*
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index f4c5815..fd55d69 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -172,11 +172,15 @@
* Update position of the view with an optional animation
*/
public void updatePosition(int x, int y, float scale, boolean animate) {
+ float oldY = mView.getY();
PropertyAnimator.setProperty(mView, AnimatableProperty.Y, y, CLOCK_ANIMATION_PROPERTIES,
animate);
mKeyguardClockSwitchController.updatePosition(x, scale, CLOCK_ANIMATION_PROPERTIES,
animate);
+ if (oldY != y) {
+ mKeyguardClockSwitchController.updateKeyguardStatusViewOffset();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index a678edc..ac0a3fd 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -28,6 +28,7 @@
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.phone.AnimatorHandle;
import com.android.systemui.statusbar.phone.DozeParameters;
import com.android.systemui.statusbar.phone.ScreenOffAnimationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -47,6 +48,7 @@
private final ScreenOffAnimationController mScreenOffAnimationController;
private boolean mAnimateYPos;
private boolean mKeyguardViewVisibilityAnimating;
+ private AnimatorHandle mKeyguardAnimatorHandle;
private boolean mLastOccludedState = false;
private final AnimationProperties mAnimationProperties = new AnimationProperties();
private final LogBuffer mLogBuffer;
@@ -83,6 +85,10 @@
boolean keyguardFadingAway,
boolean goingToFullShade,
int oldStatusBarState) {
+ if (mKeyguardAnimatorHandle != null) {
+ mKeyguardAnimatorHandle.cancel();
+ mKeyguardAnimatorHandle = null;
+ }
mView.animate().cancel();
boolean isOccluded = mKeyguardStateController.isOccluded();
mKeyguardViewVisibilityAnimating = false;
@@ -116,7 +122,7 @@
.setDuration(320)
.setInterpolator(Interpolators.ALPHA_IN)
.withEndAction(mAnimateKeyguardStatusViewVisibleEndRunnable);
- log("keyguardFadingAway transition w/ Y Aniamtion");
+ log("keyguardFadingAway transition w/ Y Animation");
} else if (statusBarState == KEYGUARD) {
if (keyguardFadingAway) {
mKeyguardViewVisibilityAnimating = true;
@@ -148,7 +154,7 @@
// Ask the screen off animation controller to animate the keyguard visibility for us
// since it may need to be cancelled due to keyguard lifecycle events.
- mScreenOffAnimationController.animateInKeyguard(
+ mKeyguardAnimatorHandle = mScreenOffAnimationController.animateInKeyguard(
mView, mAnimateKeyguardStatusViewVisibleEndRunnable);
} else {
log("Direct set Visibility to VISIBLE");
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 1ae380e..235a8bc 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -22,7 +22,6 @@
import static com.android.keyguard.LockIconView.ICON_FINGERPRINT;
import static com.android.keyguard.LockIconView.ICON_LOCK;
import static com.android.keyguard.LockIconView.ICON_UNLOCK;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.doze.util.BurnInHelperKt.getBurnInOffset;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.util.kotlin.JavaAdapterKt.collectFlow;
@@ -127,8 +126,6 @@
private boolean mCanDismissLockScreen;
private int mStatusBarState;
private boolean mIsKeyguardShowing;
- private boolean mUserUnlockedWithBiometric;
- private Runnable mCancelDelayedUpdateVisibilityRunnable;
private Runnable mOnGestureDetectedRunnable;
private Runnable mLongPressCancelRunnable;
@@ -229,7 +226,6 @@
updateIsUdfpsEnrolled();
updateConfiguration();
updateKeyguardShowing();
- mUserUnlockedWithBiometric = false;
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
mIsDozing = mStatusBarStateController.isDozing();
@@ -270,11 +266,6 @@
mStatusBarStateController.removeCallback(mStatusBarStateListener);
mKeyguardStateController.removeCallback(mKeyguardStateCallback);
- if (mCancelDelayedUpdateVisibilityRunnable != null) {
- mCancelDelayedUpdateVisibilityRunnable.run();
- mCancelDelayedUpdateVisibilityRunnable = null;
- }
-
mAccessibilityManager.removeAccessibilityStateChangeListener(
mAccessibilityStateChangeListener);
}
@@ -288,11 +279,6 @@
}
private void updateVisibility() {
- if (mCancelDelayedUpdateVisibilityRunnable != null) {
- mCancelDelayedUpdateVisibilityRunnable.run();
- mCancelDelayedUpdateVisibilityRunnable = null;
- }
-
if (!mIsKeyguardShowing && !mIsDozing) {
mView.setVisibility(View.INVISIBLE);
return;
@@ -300,9 +286,9 @@
boolean wasShowingFpIcon = mUdfpsEnrolled && !mShowUnlockIcon && !mShowLockIcon
&& !mShowAodUnlockedIcon && !mShowAodLockIcon;
- mShowLockIcon = !mCanDismissLockScreen && !mUserUnlockedWithBiometric && isLockScreen()
+ mShowLockIcon = !mCanDismissLockScreen && isLockScreen()
&& (!mUdfpsEnrolled || !mRunningFPS);
- mShowUnlockIcon = (mCanDismissLockScreen || mUserUnlockedWithBiometric) && isLockScreen();
+ mShowUnlockIcon = mCanDismissLockScreen && isLockScreen();
mShowAodUnlockedIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && mCanDismissLockScreen;
mShowAodLockIcon = mIsDozing && mUdfpsEnrolled && !mRunningFPS && !mCanDismissLockScreen;
@@ -426,7 +412,6 @@
pw.println(" isFlagEnabled(DOZING_MIGRATION_1): "
+ mFeatureFlags.isEnabled(DOZING_MIGRATION_1));
pw.println(" mIsBouncerShowing: " + mIsBouncerShowing);
- pw.println(" mUserUnlockedWithBiometric: " + mUserUnlockedWithBiometric);
pw.println(" mRunningFPS: " + mRunningFPS);
pw.println(" mCanDismissLockScreen: " + mCanDismissLockScreen);
pw.println(" mStatusBarState: " + StatusBarState.toString(mStatusBarState));
@@ -469,17 +454,6 @@
}
}
- /**
- * @return whether the userUnlockedWithBiometric state changed
- */
- private boolean updateUserUnlockedWithBiometric() {
- final boolean wasUserUnlockedWithBiometric = mUserUnlockedWithBiometric;
- mUserUnlockedWithBiometric =
- mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(
- KeyguardUpdateMonitor.getCurrentUser());
- return wasUserUnlockedWithBiometric != mUserUnlockedWithBiometric;
- }
-
private StatusBarStateController.StateListener mStatusBarStateListener =
new StatusBarStateController.StateListener() {
@Override
@@ -516,36 +490,15 @@
}
@Override
- public void onBiometricsCleared() {
- if (updateUserUnlockedWithBiometric()) {
- updateVisibility();
- }
- }
-
- @Override
public void onBiometricRunningStateChanged(boolean running,
BiometricSourceType biometricSourceType) {
final boolean wasRunningFps = mRunningFPS;
- final boolean userUnlockedWithBiometricChanged =
- updateUserUnlockedWithBiometric();
if (biometricSourceType == FINGERPRINT) {
mRunningFPS = running;
- if (wasRunningFps && !mRunningFPS) {
- if (mCancelDelayedUpdateVisibilityRunnable != null) {
- mCancelDelayedUpdateVisibilityRunnable.run();
- }
-
- // For some devices, auth is cancelled immediately on screen off but
- // before dozing state is set. We want to avoid briefly showing the
- // button in this case, so we delay updating the visibility by 50ms.
- mCancelDelayedUpdateVisibilityRunnable =
- mExecutor.executeDelayed(() -> updateVisibility(), 50);
- return;
- }
}
- if (userUnlockedWithBiometricChanged || wasRunningFps != mRunningFPS) {
+ if (wasRunningFps != mRunningFPS) {
updateVisibility();
}
}
@@ -556,7 +509,6 @@
@Override
public void onUnlockedChanged() {
mCanDismissLockScreen = mKeyguardStateController.canDismissLockScreen();
- updateUserUnlockedWithBiometric();
updateKeyguardShowing();
updateVisibility();
}
@@ -573,9 +525,6 @@
mIsBouncerShowing = mKeyguardViewController.isBouncerShowing();
updateKeyguardShowing();
- if (mIsKeyguardShowing) {
- updateUserUnlockedWithBiometric();
- }
updateVisibility();
}
@@ -694,7 +643,7 @@
private void onLongPress() {
cancelTouches();
- if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
Log.v(TAG, "lock icon long-press rejected by the falsing manager.");
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index cbc0a1b..8e5c76c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -25,7 +25,6 @@
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR;
import static com.android.internal.util.Preconditions.checkNotNull;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import android.content.BroadcastReceiver;
@@ -983,7 +982,7 @@
}
if (!mKeyguardUpdateMonitor.isFingerprintDetectionRunning()) {
- if (mFalsingManager.isFalseTouch(LOCK_ICON)) {
+ if (mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
Log.v(TAG, "aod lock icon long-press rejected by the falsing manager.");
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
index 701df89..334cf93 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/Classifier.java
@@ -41,7 +41,6 @@
public static final int SHADE_DRAG = 11;
public static final int QS_COLLAPSE = 12;
public static final int UDFPS_AUTHENTICATION = 13;
- public static final int LOCK_ICON = 14;
public static final int QS_SWIPE_SIDE = 15;
public static final int BACK_GESTURE = 16;
public static final int QS_SWIPE_NESTED = 17;
@@ -58,12 +57,10 @@
GENERIC,
BOUNCER_UNLOCK,
PULSE_EXPAND,
- BRIGHTNESS_SLIDER,
SHADE_DRAG,
QS_COLLAPSE,
BRIGHTNESS_SLIDER,
UDFPS_AUTHENTICATION,
- LOCK_ICON,
QS_SWIPE_SIDE,
QS_SWIPE_NESTED,
BACK_GESTURE,
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
index d17eadd..8ec48b9 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DiagonalClassifier.java
@@ -19,7 +19,6 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_HORIZONTAL_ANGLE_RANGE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_DIAGONAL_VERTICAL_ANGLE_RANGE;
import static com.android.systemui.classifier.Classifier.LEFT_AFFORDANCE;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.RIGHT_AFFORDANCE;
import android.provider.DeviceConfig;
@@ -73,8 +72,7 @@
}
if (interactionType == LEFT_AFFORDANCE
- || interactionType == RIGHT_AFFORDANCE
- || interactionType == LOCK_ICON) {
+ || interactionType == RIGHT_AFFORDANCE) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
index f8ee49a..15e2e9a 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/DistanceClassifier.java
@@ -158,7 +158,6 @@
|| interactionType == SHADE_DRAG
|| interactionType == QS_COLLAPSE
|| interactionType == Classifier.UDFPS_AUTHENTICATION
- || interactionType == Classifier.LOCK_ICON
|| interactionType == Classifier.QS_SWIPE_SIDE
|| interactionType == QS_SWIPE_NESTED) {
return Result.passed(0);
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
index d8d2c98..2fb6aaf 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/TypeClassifier.java
@@ -47,8 +47,7 @@
Result calculateFalsingResult(
@Classifier.InteractionType int interactionType,
double historyBelief, double historyConfidence) {
- if (interactionType == Classifier.UDFPS_AUTHENTICATION
- || interactionType == Classifier.LOCK_ICON) {
+ if (interactionType == Classifier.UDFPS_AUTHENTICATION) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
index 840982c..4a3710b 100644
--- a/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
+++ b/packages/SystemUI/src/com/android/systemui/classifier/ZigZagClassifier.java
@@ -21,7 +21,6 @@
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_PRIMARY_DEVIANCE;
import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.BRIGHTLINE_FALSING_ZIGZAG_Y_SECONDARY_DEVIANCE;
import static com.android.systemui.classifier.Classifier.BRIGHTNESS_SLIDER;
-import static com.android.systemui.classifier.Classifier.LOCK_ICON;
import static com.android.systemui.classifier.Classifier.MEDIA_SEEKBAR;
import static com.android.systemui.classifier.Classifier.SHADE_DRAG;
@@ -93,8 +92,7 @@
double historyBelief, double historyConfidence) {
if (interactionType == BRIGHTNESS_SLIDER
|| interactionType == MEDIA_SEEKBAR
- || interactionType == SHADE_DRAG
- || interactionType == LOCK_ICON) {
+ || interactionType == SHADE_DRAG) {
return Result.passed(0);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index e049ae0..c312f69 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -308,7 +308,7 @@
if (model.isSensitive()) {
mView.showTextPreview(mContext.getString(R.string.clipboard_asterisks), true);
} else {
- mView.showTextPreview(model.getText(), false);
+ mView.showTextPreview(model.getText().toString(), false);
}
mView.setEditAccessibilityAction(true);
mOnPreviewTapped = this::editText;
@@ -527,7 +527,7 @@
}
private void showEditableText(CharSequence text, boolean hidden) {
- mView.showTextPreview(text, hidden);
+ mView.showTextPreview(text.toString(), hidden);
mView.setEditAccessibilityAction(true);
mOnPreviewTapped = this::editText;
}
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 ac1150e..e8c97bf 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/controller/ControlsControllerImpl.kt
@@ -31,7 +31,6 @@
import android.util.ArrayMap
import android.util.Log
import com.android.internal.annotations.VisibleForTesting
-import com.android.internal.notification.NotificationAccessConfirmationActivityContract.EXTRA_USER_ID
import com.android.systemui.Dumpable
import com.android.systemui.backup.BackupHelper
import com.android.systemui.controls.ControlStatus
@@ -44,7 +43,6 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dump.DumpManager
-import com.android.systemui.people.widget.PeopleSpaceWidgetProvider.EXTRA_USER_HANDLE
import com.android.systemui.settings.UserFileManager
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.DeviceControlsControllerImpl.Companion.PREFS_CONTROLS_FILE
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
index 00a406e..be428a8 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/AllModel.kt
@@ -75,9 +75,12 @@
} else {
favoriteIds.remove(controlId)
}
- if (changed && !modified) {
- modified = true
- controlsModelCallback.onFirstChange()
+ if (changed) {
+ if (!modified) {
+ modified = true
+ controlsModelCallback.onFirstChange()
+ }
+ controlsModelCallback.onChange()
}
toChange?.let {
it.controlStatus.favorite = favorite
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
index 7df0865..d629e3e 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -27,6 +27,7 @@
import android.view.ViewStub
import android.widget.Button
import android.widget.TextView
+import android.widget.Toast
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
@@ -38,8 +39,9 @@
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -48,17 +50,19 @@
* Activity for rearranging and removing controls for a given structure
*/
open class ControlsEditingActivity @Inject constructor(
+ featureFlags: FeatureFlags,
@Main private val mainExecutor: Executor,
private val controller: ControlsControllerImpl,
private val userTracker: UserTracker,
private val customIconCache: CustomIconCache,
- private val uiController: ControlsUiController
) : ComponentActivity() {
companion object {
private const val DEBUG = false
private const val TAG = "ControlsEditingActivity"
const val EXTRA_STRUCTURE = ControlsFavoritingActivity.EXTRA_STRUCTURE
+ const val EXTRA_APP = ControlsFavoritingActivity.EXTRA_APP
+ const val EXTRA_FROM_FAVORITING = "extra_from_favoriting"
private val SUBTITLE_ID = R.string.controls_favorite_rearrange
private val EMPTY_TEXT_ID = R.string.controls_favorite_removed
}
@@ -68,7 +72,12 @@
private lateinit var model: FavoritesModel
private lateinit var subtitle: TextView
private lateinit var saveButton: View
+ private lateinit var addControls: View
+ private var isFromFavoriting: Boolean = false
+
+ private val isNewFlowEnabled: Boolean =
+ featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
private val startingUser = controller.currentUserId
@@ -93,7 +102,7 @@
intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)?.let {
component = it
} ?: run(this::finish)
-
+ isFromFavoriting = intent.getBooleanExtra(EXTRA_FROM_FAVORITING, false)
intent.getCharSequenceExtra(EXTRA_STRUCTURE)?.let {
structure = it
} ?: run(this::finish)
@@ -165,8 +174,42 @@
}
private fun bindButtons() {
+ addControls = requireViewById<Button>(R.id.addControls).apply {
+ isEnabled = true
+ visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+ setOnClickListener {
+ if (saveButton.isEnabled) {
+ // The user has made changes
+ Toast.makeText(
+ applicationContext,
+ R.string.controls_favorite_toast_no_changes,
+ Toast.LENGTH_SHORT
+ ).show()
+ }
+ if (isFromFavoriting) {
+ animateExitAndFinish()
+ } else {
+ startActivity(Intent(context, ControlsFavoritingActivity::class.java).also {
+ it.putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+ it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+ it.putExtra(
+ ControlsFavoritingActivity.EXTRA_APP,
+ intent.getCharSequenceExtra(EXTRA_APP),
+ )
+ it.putExtra(
+ ControlsFavoritingActivity.EXTRA_SOURCE,
+ ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING,
+ )
+ },
+ ActivityOptions.makeSceneTransitionAnimation(
+ this@ControlsEditingActivity
+ ).toBundle(),
+ )
+ }
+ }
+ }
saveButton = requireViewById<Button>(R.id.done).apply {
- isEnabled = false
+ isEnabled = isFromFavoriting
setText(R.string.save)
setOnClickListener {
saveFavorites()
@@ -194,6 +237,8 @@
}
}
+ override fun onChange() = Unit
+
override fun onFirstChange() {
saveButton.isEnabled = true
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
index 3e97d31..d3ffc95 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -37,6 +37,7 @@
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.activity.ComponentActivity
+import androidx.annotation.VisibleForTesting
import androidx.viewpager2.widget.ViewPager2
import com.android.systemui.Prefs
import com.android.systemui.R
@@ -45,20 +46,20 @@
import com.android.systemui.controls.controller.ControlsControllerImpl
import com.android.systemui.controls.controller.StructureInfo
import com.android.systemui.controls.ui.ControlsActivity
-import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
import java.text.Collator
import java.util.concurrent.Executor
-import java.util.function.Consumer
import javax.inject.Inject
open class ControlsFavoritingActivity @Inject constructor(
+ featureFlags: FeatureFlags,
@Main private val executor: Executor,
private val controller: ControlsControllerImpl,
private val listingController: ControlsListingController,
private val userTracker: UserTracker,
- private val uiController: ControlsUiController
) : ComponentActivity() {
companion object {
@@ -71,7 +72,10 @@
// If provided, show this structure page first
const val EXTRA_STRUCTURE = "extra_structure"
const val EXTRA_SINGLE_STRUCTURE = "extra_single_structure"
- const val EXTRA_FROM_PROVIDER_SELECTOR = "extra_from_provider_selector"
+ const val EXTRA_SOURCE = "extra_source"
+ const val EXTRA_SOURCE_UNDEFINED: Byte = 0
+ const val EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR: Byte = 1
+ const val EXTRA_SOURCE_VALUE_FROM_EDITING: Byte = 2
private const val TOOLTIP_PREFS_KEY = Prefs.Key.CONTROLS_STRUCTURE_SWIPE_TOOLTIP_COUNT
private const val TOOLTIP_MAX_SHOWN = 2
}
@@ -79,7 +83,7 @@
private var component: ComponentName? = null
private var appName: CharSequence? = null
private var structureExtra: CharSequence? = null
- private var fromProviderSelector = false
+ private var openSource = EXTRA_SOURCE_UNDEFINED
private lateinit var structurePager: ViewPager2
private lateinit var statusText: TextView
@@ -89,12 +93,19 @@
private var mTooltipManager: TooltipManager? = null
private lateinit var doneButton: View
private lateinit var otherAppsButton: View
+ private lateinit var rearrangeButton: Button
private var listOfStructures = emptyList<StructureContainer>()
private lateinit var comparator: Comparator<StructureContainer>
private var cancelLoadRunnable: Runnable? = null
private var isPagerLoaded = false
+ private val fromProviderSelector: Boolean
+ get() = openSource == EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR
+ private val fromEditing: Boolean
+ get() = openSource == EXTRA_SOURCE_VALUE_FROM_EDITING
+ private val isNewFlowEnabled: Boolean =
+ featureFlags.isEnabled(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS)
private val userTrackerCallback: UserTracker.Callback = object : UserTracker.Callback {
private val startingUser = controller.currentUserId
@@ -117,14 +128,20 @@
override fun onServicesUpdated(serviceInfos: List<ControlsServiceInfo>) {
if (serviceInfos.size > 1) {
- otherAppsButton.post {
- otherAppsButton.visibility = View.VISIBLE
+ val newVisibility = if (isNewFlowEnabled) View.GONE else View.VISIBLE
+ if (otherAppsButton.visibility != newVisibility) {
+ otherAppsButton.post {
+ otherAppsButton.visibility = newVisibility
+ }
}
}
}
}
override fun onBackPressed() {
+ if (fromEditing) {
+ animateExitAndFinish()
+ }
if (!fromProviderSelector) {
openControlsOrigin()
}
@@ -139,7 +156,7 @@
appName = intent.getCharSequenceExtra(EXTRA_APP)
structureExtra = intent.getCharSequenceExtra(EXTRA_STRUCTURE)
component = intent.getParcelableExtra<ComponentName>(Intent.EXTRA_COMPONENT_NAME)
- fromProviderSelector = intent.getBooleanExtra(EXTRA_FROM_PROVIDER_SELECTOR, false)
+ openSource = intent.getByteExtra(EXTRA_SOURCE, EXTRA_SOURCE_UNDEFINED)
bindViews()
}
@@ -148,14 +165,19 @@
override fun onFirstChange() {
doneButton.isEnabled = true
}
+
+ override fun onChange() {
+ val structure: StructureContainer = listOfStructures[structurePager.currentItem]
+ rearrangeButton.isEnabled = structure.model.favorites.isNotEmpty()
+ }
}
private fun loadControls() {
- component?.let {
+ component?.let { componentName ->
statusText.text = resources.getText(com.android.internal.R.string.loading)
val emptyZoneString = resources.getText(
R.string.controls_favorite_other_zone_header)
- controller.loadForComponent(it, Consumer { data ->
+ controller.loadForComponent(componentName, { data ->
val allControls = data.allControls
val favoriteKeys = data.favoritesIds
val error = data.errorOnLoad
@@ -213,7 +235,7 @@
ControlsAnimations.enterAnimation(structurePager).start()
}
}
- }, Consumer { runnable -> cancelLoadRunnable = runnable })
+ }, { runnable -> cancelLoadRunnable = runnable })
}
}
@@ -299,7 +321,8 @@
bindButtons()
}
- private fun animateExitAndFinish() {
+ @VisibleForTesting
+ internal open fun animateExitAndFinish() {
val rootView = requireViewById<ViewGroup>(R.id.controls_management_root)
ControlsAnimations.exitAnimation(
rootView,
@@ -312,6 +335,32 @@
}
private fun bindButtons() {
+ rearrangeButton = requireViewById<Button>(R.id.rearrange).apply {
+ text = if (fromEditing) {
+ getString(R.string.controls_favorite_back_to_editing)
+ } else {
+ getString(R.string.controls_favorite_rearrange_button)
+ }
+ isEnabled = false
+ visibility = if (isNewFlowEnabled) View.VISIBLE else View.GONE
+ setOnClickListener {
+ if (component == null) return@setOnClickListener
+ saveFavorites()
+ startActivity(
+ Intent(context, ControlsEditingActivity::class.java).also {
+ it.putExtra(Intent.EXTRA_COMPONENT_NAME, component)
+ it.putExtra(ControlsEditingActivity.EXTRA_APP, appName)
+ it.putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, true)
+ it.putExtra(
+ ControlsEditingActivity.EXTRA_STRUCTURE,
+ listOfStructures[structurePager.currentItem].structureName,
+ )
+ },
+ ActivityOptions
+ .makeSceneTransitionAnimation(this@ControlsFavoritingActivity).toBundle()
+ )
+ }
+ }
otherAppsButton = requireViewById<Button>(R.id.other_apps).apply {
setOnClickListener {
if (doneButton.isEnabled) {
@@ -335,18 +384,22 @@
isEnabled = false
setOnClickListener {
if (component == null) return@setOnClickListener
- listOfStructures.forEach {
- val favoritesForStorage = it.model.favorites
- controller.replaceFavoritesForStructure(
- StructureInfo(component!!, it.structureName, favoritesForStorage)
- )
- }
+ saveFavorites()
animateExitAndFinish()
openControlsOrigin()
}
}
}
+ private fun saveFavorites() {
+ listOfStructures.forEach {
+ val favoritesForStorage = it.model.favorites
+ controller.replaceFavoritesForStructure(
+ StructureInfo(component!!, it.structureName, favoritesForStorage)
+ )
+ }
+ }
+
private fun openControlsOrigin() {
startActivity(
Intent(applicationContext, ControlsActivity::class.java),
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
index d65481a..3455e6d 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsModel.kt
@@ -71,6 +71,11 @@
* Use to notify that the model has changed for the first time
*/
fun onFirstChange()
+
+ /**
+ * Use to notify that the model has changed
+ */
+ fun onChange()
}
/**
@@ -132,7 +137,7 @@
controlInfo: ControlInfo,
favorite: Boolean,
customIconGetter: (ComponentName, String) -> Icon?
- ): this(component, controlInfo, favorite) {
+ ) : this(component, controlInfo, favorite) {
this.customIconGetter = customIconGetter
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
index 3808e73..92aff06 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -211,7 +211,10 @@
putExtra(ControlsFavoritingActivity.EXTRA_APP,
listingController.getAppLabel(it))
putExtra(Intent.EXTRA_COMPONENT_NAME, it)
- putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
+ putExtra(
+ ControlsFavoritingActivity.EXTRA_SOURCE,
+ ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+ )
}
startActivity(intent, ActivityOptions.makeSceneTransitionAnimation(this).toBundle())
animateExitAndFinish()
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
index 74a49a8..c954f98 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStatusBarViewController.java
@@ -201,8 +201,6 @@
mStatusBarItemsProvider.addCallback(mStatusBarItemsProviderCallback);
mDreamOverlayStateController.addCallback(mDreamOverlayStateCallback);
-
- mTouchInsetSession.addViewToTracking(mView);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
index 43e4c62..7f44463 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitor.java
@@ -101,6 +101,10 @@
completer.set(predecessor);
}
+
+ if (mActiveTouchSessions.isEmpty() && mStopMonitoringPending) {
+ stopMonitoring(false);
+ }
});
return "DreamOverlayTouchMonitor::pop";
@@ -214,7 +218,12 @@
@Override
public void onPause(@NonNull LifecycleOwner owner) {
- stopMonitoring();
+ stopMonitoring(false);
+ }
+
+ @Override
+ public void onDestroy(LifecycleOwner owner) {
+ stopMonitoring(true);
}
};
@@ -222,7 +231,7 @@
* When invoked, instantiates a new {@link InputSession} to monitor touch events.
*/
private void startMonitoring() {
- stopMonitoring();
+ stopMonitoring(true);
mCurrentInputSession = mInputSessionFactory.create(
"dreamOverlay",
mInputEventListener,
@@ -234,11 +243,16 @@
/**
* Destroys any active {@link InputSession}.
*/
- private void stopMonitoring() {
+ private void stopMonitoring(boolean force) {
if (mCurrentInputSession == null) {
return;
}
+ if (!mActiveTouchSessions.isEmpty() && !force) {
+ mStopMonitoringPending = true;
+ return;
+ }
+
// When we stop monitoring touches, we must ensure that all active touch sessions and
// descendants informed of the removal so any cleanup for active tracking can proceed.
mExecutor.execute(() -> mActiveTouchSessions.forEach(touchSession -> {
@@ -250,6 +264,7 @@
mCurrentInputSession.dispose();
mCurrentInputSession = null;
+ mStopMonitoringPending = false;
}
@@ -257,6 +272,8 @@
private final Collection<DreamTouchHandler> mHandlers;
private final DisplayHelper mDisplayHelper;
+ private boolean mStopMonitoringPending;
+
private InputChannelCompat.InputEventListener mInputEventListener =
new InputChannelCompat.InputEventListener() {
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
new file mode 100644
index 0000000..58b70b0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/ShadeTouchHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.dreams.touch;
+
+import static com.android.systemui.dreams.touch.dagger.ShadeModule.NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT;
+
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import java.util.Optional;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * {@link ShadeTouchHandler} is responsible for handling swipe down gestures over dream
+ * to bring down the shade.
+ */
+public class ShadeTouchHandler implements DreamTouchHandler {
+ private final Optional<CentralSurfaces> mSurfaces;
+ private final int mInitiationHeight;
+
+ @Inject
+ ShadeTouchHandler(Optional<CentralSurfaces> centralSurfaces,
+ @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT) int initiationHeight) {
+ mSurfaces = centralSurfaces;
+ mInitiationHeight = initiationHeight;
+ }
+
+ @Override
+ public void onSessionStart(TouchSession session) {
+ if (mSurfaces.map(CentralSurfaces::isBouncerShowing).orElse(false)) {
+ session.pop();
+ return;
+ }
+
+ session.registerInputListener(ev -> {
+ final NotificationPanelViewController viewController =
+ mSurfaces.map(CentralSurfaces::getNotificationPanelViewController).orElse(null);
+
+ if (viewController != null) {
+ viewController.handleExternalTouch((MotionEvent) ev);
+ }
+
+ if (ev instanceof MotionEvent) {
+ if (((MotionEvent) ev).getAction() == MotionEvent.ACTION_UP) {
+ session.pop();
+ }
+ }
+ });
+
+ session.registerGestureListener(new GestureDetector.SimpleOnGestureListener() {
+ @Override
+ public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
+ float distanceY) {
+ return true;
+ }
+
+ @Override
+ public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ float velocityY) {
+ return true;
+ }
+ });
+ }
+
+ @Override
+ public void getTouchInitiationRegion(Rect bounds, Region region) {
+ final Rect outBounds = new Rect(bounds);
+ outBounds.inset(0, 0, 0, outBounds.height() - mInitiationHeight);
+ region.op(outBounds, Region.Op.UNION);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
index dad0004..b719126 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/DreamTouchModule.java
@@ -23,6 +23,7 @@
*/
@Module(includes = {
BouncerSwipeModule.class,
+ ShadeModule.class,
}, subcomponents = {
InputSessionComponent.class,
})
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
new file mode 100644
index 0000000..9e0ae41
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/touch/dagger/ShadeModule.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.dreams.touch.dagger;
+
+import android.content.res.Resources;
+
+import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.dreams.touch.DreamTouchHandler;
+import com.android.systemui.dreams.touch.ShadeTouchHandler;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+
+import javax.inject.Named;
+
+/**
+ * Dependencies for swipe down to notification over dream.
+ */
+@Module
+public class ShadeModule {
+ /**
+ * The height, defined in pixels, of the gesture initiation region at the top of the screen for
+ * swiping down notifications.
+ */
+ public static final String NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT =
+ "notification_shade_gesture_initiation_height";
+
+ /**
+ * Provides {@link ShadeTouchHandler} to handle notification swipe down over dream.
+ */
+ @Provides
+ @IntoSet
+ public static DreamTouchHandler providesNotificationShadeTouchHandler(
+ ShadeTouchHandler touchHandler) {
+ return touchHandler;
+ }
+
+ /**
+ * Provides the height of the gesture area for notification swipe down.
+ */
+ @Provides
+ @Named(NOTIFICATION_SHADE_GESTURE_INITIATION_HEIGHT)
+ public static int providesNotificationShadeGestureRegionHeight(@Main Resources resources) {
+ return resources.getDimensionPixelSize(R.dimen.dream_overlay_status_bar_height);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 27f35db..43221a7 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -101,12 +101,12 @@
releasedFlag(174148361, "notification_inline_reply_animation")
val FILTER_UNSEEN_NOTIFS_ON_KEYGUARD =
- releasedFlag(254647461, "filter_unseen_notifs_on_keyguard", teamfood = true)
+ releasedFlag(254647461, "filter_unseen_notifs_on_keyguard")
// TODO(b/263414400): Tracking Bug
@JvmField
val NOTIFICATION_ANIMATE_BIG_PICTURE =
- releasedFlag(120, "notification_animate_big_picture", teamfood = true)
+ releasedFlag(120, "notification_animate_big_picture")
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
@@ -184,7 +184,7 @@
// flag for controlling auto pin confirmation and material u shapes in bouncer
@JvmField
val AUTO_PIN_CONFIRMATION =
- releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation", teamfood = true)
+ releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation")
// TODO(b/262859270): Tracking Bug
@JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded")
@@ -621,15 +621,15 @@
@JvmField val NOTE_TASKS = releasedFlag(1900, "keycode_flag")
// 2000 - device controls
- @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels", teamfood = true)
+ @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels")
@JvmField
val APP_PANELS_ALL_APPS_ALLOWED =
- releasedFlag(2001, "app_panels_all_apps_allowed", teamfood = true)
+ releasedFlag(2001, "app_panels_all_apps_allowed")
@JvmField
val CONTROLS_MANAGEMENT_NEW_FLOWS =
- releasedFlag(2002, "controls_management_new_flows", teamfood = true)
+ releasedFlag(2002, "controls_management_new_flows")
// Enables removing app from Home control panel as a part of a new flow
// TODO(b/269132640): Tracking Bug
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
new file mode 100644
index 0000000..801b165
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -0,0 +1,493 @@
+package com.android.systemui.graphics
+
+import android.annotation.AnyThread
+import android.annotation.DrawableRes
+import android.annotation.Px
+import android.annotation.SuppressLint
+import android.annotation.WorkerThread
+import android.content.Context
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import android.content.res.Resources.NotFoundException
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.ImageDecoder.DecodeException
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.util.Log
+import android.util.Size
+import androidx.core.content.res.ResourcesCompat
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Background
+import java.io.IOException
+import javax.inject.Inject
+import kotlin.math.min
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.withContext
+
+/**
+ * Helper class to load images for SystemUI. It allows for memory efficient image loading with size
+ * restriction and attempts to use hardware bitmaps when sensible.
+ */
+@SysUISingleton
+class ImageLoader
+@Inject
+constructor(
+ private val defaultContext: Context,
+ @Background private val backgroundDispatcher: CoroutineDispatcher
+) {
+
+ /** Source of the image data. */
+ sealed interface Source
+
+ /**
+ * Load image from a Resource ID. If the resource is part of another package or if it requires
+ * tinting, pass in a correct [Context].
+ */
+ data class Res(@DrawableRes val resId: Int, val context: Context?) : Source {
+ constructor(@DrawableRes resId: Int) : this(resId, null)
+ }
+
+ /** Load image from a Uri. */
+ data class Uri(val uri: android.net.Uri) : Source {
+ constructor(uri: String) : this(android.net.Uri.parse(uri))
+ }
+
+ /** Load image from a [File]. */
+ data class File(val file: java.io.File) : Source {
+ constructor(path: String) : this(java.io.File(path))
+ }
+
+ /** Load image from an [InputStream]. */
+ data class InputStream(val inputStream: java.io.InputStream, val context: Context?) : Source {
+ constructor(inputStream: java.io.InputStream) : this(inputStream, null)
+ }
+
+ /**
+ * Loads passed [Source] on a background thread and returns the [Bitmap].
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+ * ratio.
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Bitmap] or `null` if loading failed.
+ */
+ @AnyThread
+ suspend fun loadBitmap(
+ source: Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Bitmap? =
+ withContext(backgroundDispatcher) { loadBitmapSync(source, maxWidth, maxHeight, allocator) }
+
+ /**
+ * Loads passed [Source] synchronously and returns the [Bitmap].
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints while keeping aspect
+ * ratio.
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Bitmap] or `null` if loading failed.
+ */
+ @WorkerThread
+ fun loadBitmapSync(
+ source: Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Bitmap? {
+ return try {
+ loadBitmapSync(
+ toImageDecoderSource(source, defaultContext),
+ maxWidth,
+ maxHeight,
+ allocator
+ )
+ } catch (e: NotFoundException) {
+ Log.w(TAG, "Couldn't load resource $source", e)
+ null
+ }
+ }
+
+ /**
+ * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+ * ratio).
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Bitmap] or `null` if loading failed.
+ */
+ @WorkerThread
+ fun loadBitmapSync(
+ source: ImageDecoder.Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Bitmap? {
+ return try {
+ ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+ configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+ decoder.allocator = allocator
+ }
+ } catch (e: IOException) {
+ Log.w(TAG, "Failed to load source $source", e)
+ return null
+ } catch (e: DecodeException) {
+ Log.w(TAG, "Failed to decode source $source", e)
+ return null
+ }
+ }
+
+ /**
+ * Loads passed [Source] on a background thread and returns the [Drawable].
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+ * ratio).
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Drawable] or `null` if loading failed.
+ */
+ @AnyThread
+ suspend fun loadDrawable(
+ source: Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Drawable? =
+ withContext(backgroundDispatcher) {
+ loadDrawableSync(source, maxWidth, maxHeight, allocator)
+ }
+
+ /**
+ * Loads passed [Icon] on a background thread and returns the drawable.
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+ * ratio).
+ *
+ * @param context Alternate context to use for resource loading (for e.g. cross-process use)
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Drawable] or `null` if loading failed.
+ */
+ @AnyThread
+ suspend fun loadDrawable(
+ icon: Icon,
+ context: Context = defaultContext,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Drawable? =
+ withContext(backgroundDispatcher) {
+ loadDrawableSync(icon, context, maxWidth, maxHeight, allocator)
+ }
+
+ /**
+ * Loads passed [Source] synchronously and returns the drawable.
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+ * ratio).
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Drawable] or `null` if loading failed.
+ */
+ @WorkerThread
+ @SuppressLint("UseCompatLoadingForDrawables")
+ fun loadDrawableSync(
+ source: Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Drawable? {
+ return try {
+ loadDrawableSync(
+ toImageDecoderSource(source, defaultContext),
+ maxWidth,
+ maxHeight,
+ allocator
+ )
+ ?:
+ // If we have a resource, retry fallback using the "normal" Resource loading system.
+ // This will come into effect in cases like trying to load AnimatedVectorDrawable.
+ if (source is Res) {
+ val context = source.context ?: defaultContext
+ ResourcesCompat.getDrawable(context.resources, source.resId, context.theme)
+ } else {
+ null
+ }
+ } catch (e: NotFoundException) {
+ Log.w(TAG, "Couldn't load resource $source", e)
+ null
+ }
+ }
+
+ /**
+ * Loads passed [ImageDecoder.Source] synchronously and returns the drawable.
+ *
+ * Maximum height and width can be passed as optional parameters - the image decoder will make
+ * sure to keep the decoded drawable size within those passed constraints (while keeping aspect
+ * ratio).
+ *
+ * @param maxWidth Maximum width of the returned drawable (if able). 0 means no restriction. Set
+ * to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param maxHeight Maximum height of the returned drawable (if able). 0 means no restriction.
+ * Set to [DEFAULT_MAX_SAFE_BITMAP_SIZE_PX] by default.
+ * @param allocator Allocator to use for the loaded drawable - one of [ImageDecoder] allocator
+ * ints. Use [ImageDecoder.ALLOCATOR_SOFTWARE] to force software bitmap.
+ * @return loaded [Drawable] or `null` if loading failed.
+ */
+ @WorkerThread
+ fun loadDrawableSync(
+ source: ImageDecoder.Source,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Drawable? {
+ return try {
+ ImageDecoder.decodeDrawable(source) { decoder, info, _ ->
+ configureDecoderForMaximumSize(decoder, info.size, maxWidth, maxHeight)
+ decoder.allocator = allocator
+ }
+ } catch (e: IOException) {
+ Log.w(TAG, "Failed to load source $source", e)
+ return null
+ } catch (e: DecodeException) {
+ Log.w(TAG, "Failed to decode source $source", e)
+ return null
+ }
+ }
+
+ /** Loads icon drawable while attempting to size restrict the drawable. */
+ @WorkerThread
+ fun loadDrawableSync(
+ icon: Icon,
+ context: Context = defaultContext,
+ @Px maxWidth: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ @Px maxHeight: Int = DEFAULT_MAX_SAFE_BITMAP_SIZE_PX,
+ allocator: Int = ImageDecoder.ALLOCATOR_DEFAULT
+ ): Drawable? {
+ return when (icon.type) {
+ Icon.TYPE_URI,
+ Icon.TYPE_URI_ADAPTIVE_BITMAP -> {
+ val source = ImageDecoder.createSource(context.contentResolver, icon.uri)
+ loadDrawableSync(source, maxWidth, maxHeight, allocator)
+ }
+ Icon.TYPE_RESOURCE -> {
+ val resources = resolveResourcesForIcon(context, icon)
+ resources?.let {
+ loadDrawableSync(
+ ImageDecoder.createSource(it, icon.resId),
+ maxWidth,
+ maxHeight,
+ allocator
+ )
+ }
+ // Fallback to non-ImageDecoder load if the attempt failed (e.g. the resource
+ // is a Vector drawable which ImageDecoder doesn't support.)
+ ?: icon.loadDrawable(context)
+ }
+ Icon.TYPE_BITMAP -> {
+ BitmapDrawable(context.resources, icon.bitmap)
+ }
+ Icon.TYPE_ADAPTIVE_BITMAP -> {
+ AdaptiveIconDrawable(null, BitmapDrawable(context.resources, icon.bitmap))
+ }
+ Icon.TYPE_DATA -> {
+ loadDrawableSync(
+ ImageDecoder.createSource(icon.dataBytes, icon.dataOffset, icon.dataLength),
+ maxWidth,
+ maxHeight,
+ allocator
+ )
+ }
+ else -> {
+ // We don't recognize this icon, just fallback.
+ icon.loadDrawable(context)
+ }
+ }?.let { drawable ->
+ // Icons carry tint which we need to propagate down to a Drawable.
+ tintDrawable(icon, drawable)
+ drawable
+ }
+ }
+
+ companion object {
+ const val TAG = "ImageLoader"
+
+ // 4096 is a reasonable default - most devices will support 4096x4096 texture size for
+ // Canvas rendering and by default we SystemUI has no need to render larger bitmaps.
+ // This prevents exceptions and crashes if the code accidentally loads larger Bitmap
+ // and then attempts to render it on Canvas.
+ // It can always be overridden by the parameters.
+ const val DEFAULT_MAX_SAFE_BITMAP_SIZE_PX = 4096
+
+ /**
+ * This constant signals that ImageLoader shouldn't attempt to resize the passed bitmap in a
+ * given dimension.
+ *
+ * Set both maxWidth and maxHeight to [DO_NOT_RESIZE] if you wish to prevent resizing.
+ */
+ const val DO_NOT_RESIZE = 0
+
+ /** Maps [Source] to [ImageDecoder.Source]. */
+ private fun toImageDecoderSource(source: Source, defaultContext: Context) =
+ when (source) {
+ is Res -> {
+ val context = source.context ?: defaultContext
+ ImageDecoder.createSource(context.resources, source.resId)
+ }
+ is File -> ImageDecoder.createSource(source.file)
+ is Uri -> ImageDecoder.createSource(defaultContext.contentResolver, source.uri)
+ is InputStream -> {
+ val context = source.context ?: defaultContext
+ ImageDecoder.createSource(context.resources, source.inputStream)
+ }
+ }
+
+ /**
+ * This sets target size on the image decoder to conform to the maxWidth / maxHeight
+ * parameters. The parameters are chosen to keep the existing drawable aspect ratio.
+ */
+ @AnyThread
+ private fun configureDecoderForMaximumSize(
+ decoder: ImageDecoder,
+ imgSize: Size,
+ @Px maxWidth: Int,
+ @Px maxHeight: Int
+ ) {
+ if (maxWidth == DO_NOT_RESIZE && maxHeight == DO_NOT_RESIZE) {
+ return
+ }
+
+ if (imgSize.width <= maxWidth && imgSize.height <= maxHeight) {
+ return
+ }
+
+ // Determine the scale factor for each dimension so it fits within the set constraint
+ val wScale =
+ if (maxWidth <= 0) {
+ 1.0f
+ } else {
+ maxWidth.toFloat() / imgSize.width.toFloat()
+ }
+
+ val hScale =
+ if (maxHeight <= 0) {
+ 1.0f
+ } else {
+ maxHeight.toFloat() / imgSize.height.toFloat()
+ }
+
+ // Scale down to the dimension that demands larger scaling (smaller scale factor).
+ // Use the same scale for both dimensions to keep the aspect ratio.
+ val scale = min(wScale, hScale)
+ if (scale < 1.0f) {
+ val targetWidth = (imgSize.width * scale).toInt()
+ val targetHeight = (imgSize.height * scale).toInt()
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Configured image size to $targetWidth x $targetHeight")
+ }
+
+ decoder.setTargetSize(targetWidth, targetHeight)
+ }
+ }
+
+ /**
+ * Attempts to retrieve [Resources] class required to load the passed icon. Icons can
+ * originate from other processes so we need to make sure we load them from the right
+ * package source.
+ *
+ * @return [Resources] to load the icon drawble or null if icon doesn't carry a resource or
+ * the resource package couldn't be resolved.
+ */
+ @WorkerThread
+ private fun resolveResourcesForIcon(context: Context, icon: Icon): Resources? {
+ if (icon.type != Icon.TYPE_RESOURCE) {
+ return null
+ }
+
+ val resources = icon.resources
+ if (resources != null) {
+ return resources
+ }
+
+ val resPackage = icon.resPackage
+ if (
+ resPackage == null || resPackage.isEmpty() || context.packageName.equals(resPackage)
+ ) {
+ return context.resources
+ }
+
+ if ("android" == resPackage) {
+ return Resources.getSystem()
+ }
+
+ val pm = context.packageManager
+ try {
+ val ai =
+ pm.getApplicationInfo(
+ resPackage,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES or
+ PackageManager.GET_SHARED_LIBRARY_FILES
+ )
+ if (ai != null) {
+ return pm.getResourcesForApplication(ai)
+ } else {
+ Log.w(TAG, "Failed to resolve application info for $resPackage")
+ }
+ } catch (e: PackageManager.NameNotFoundException) {
+ Log.w(TAG, "Failed to resolve resource package", e)
+ return null
+ }
+ return null
+ }
+
+ /** Applies tinting from [Icon] to the passed [Drawable]. */
+ @AnyThread
+ private fun tintDrawable(icon: Icon, drawable: Drawable) {
+ if (icon.hasTint()) {
+ drawable.mutate()
+ drawable.setTintList(icon.tintList)
+ drawable.setTintBlendMode(icon.tintBlendMode)
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index eef7ccc..107e685 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -231,22 +231,20 @@
);
}
- public void mergeAnimation(IBinder transition, TransitionInfo info,
- SurfaceControl.Transaction t, IBinder mergeTarget,
- IRemoteTransitionFinishedCallback finishCallback) {
+ public void mergeAnimation(IBinder candidateTransition, TransitionInfo candidateInfo,
+ SurfaceControl.Transaction candidateT, IBinder currentTransition,
+ IRemoteTransitionFinishedCallback candidateFinishCallback) {
try {
- final IRemoteTransitionFinishedCallback origFinishCB;
+ final IRemoteTransitionFinishedCallback currentFinishCB;
synchronized (mFinishCallbacks) {
- origFinishCB = mFinishCallbacks.remove(transition);
+ currentFinishCB = mFinishCallbacks.remove(currentTransition);
}
- info.releaseAllSurfaces();
- t.close();
- if (origFinishCB == null) {
- // already finished (or not started yet), so do nothing.
+ if (currentFinishCB == null) {
+ Slog.e(TAG, "Called mergeAnimation, but finish callback is missing");
return;
}
runner.onAnimationCancelled(false /* isKeyguardOccluded */);
- origFinishCB.onTransitionFinished(null /* wct */, null /* t */);
+ currentFinishCB.onTransitionFinished(null /* wct */, null /* t */);
} catch (RemoteException e) {
// nothing, we'll just let it finish on its own I guess.
}
@@ -304,13 +302,13 @@
Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_GOING_AWAY");
TransitionFilter f = new TransitionFilter();
f.mFlags = TRANSIT_FLAG_KEYGUARD_GOING_AWAY;
- mShellTransitions.registerRemote(f,
- new RemoteTransition(wrap(mExitAnimationRunner), getIApplicationThread()));
+ mShellTransitions.registerRemote(f, new RemoteTransition(
+ wrap(mExitAnimationRunner), getIApplicationThread(), "ExitKeyguard"));
Slog.d(TAG, "KeyguardService registerRemote: TRANSIT_KEYGUARD_(UN)OCCLUDE");
// Register for occluding
final RemoteTransition occludeTransition = new RemoteTransition(
- mOccludeAnimation, getIApplicationThread());
+ mOccludeAnimation, getIApplicationThread(), "KeyguardOcclude");
f = new TransitionFilter();
f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
f.mRequirements = new TransitionFilter.Requirement[]{
@@ -329,7 +327,7 @@
// Now register for un-occlude.
final RemoteTransition unoccludeTransition = new RemoteTransition(
- mUnoccludeAnimation, getIApplicationThread());
+ mUnoccludeAnimation, getIApplicationThread(), "KeyguardUnocclude");
f = new TransitionFilter();
f.mFlags = TRANSIT_FLAG_KEYGUARD_LOCKED;
f.mRequirements = new TransitionFilter.Requirement[]{
@@ -384,7 +382,7 @@
f.mRequirements[1].mModes = new int[]{TRANSIT_CLOSE, TRANSIT_TO_BACK};
mShellTransitions.registerRemote(f, new RemoteTransition(
wrap(mKeyguardViewMediator.getOccludeByDreamAnimationRunner()),
- getIApplicationThread()));
+ getIApplicationThread(), "KeyguardOccludeByDream"));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 0825435..b1efdd7 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -953,10 +953,15 @@
@Override
public void onAnimationCancelled(boolean isKeyguardOccluded) {
- if (mOccludeByDreamAnimator != null) {
- mOccludeByDreamAnimator.cancel();
- }
- setOccluded(isKeyguardOccluded /* isOccluded */, false /* animate */);
+ mContext.getMainExecutor().execute(() -> {
+ if (mOccludeByDreamAnimator != null) {
+ mOccludeByDreamAnimator.cancel();
+ }
+ });
+ // The value of isKeyguardOccluded here may come from mergeAnimation, which
+ // isn't reliable. In all cases, after running or cancelling this animation,
+ // keyguard should be occluded.
+ setOccluded(true /* isOccluded */, false /* animate */);
if (DEBUG) {
Log.d(TAG, "Occlude by Dream animation cancelled. Occluded state is now: "
+ mOccluded);
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 cb89106..6ac51cd 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -43,6 +43,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.data.quickaffordance.KeyguardDataQuickAffordanceModule;
+import com.android.systemui.keyguard.data.repository.KeyguardFaceAuthModule;
import com.android.systemui.keyguard.data.repository.KeyguardRepositoryModule;
import com.android.systemui.keyguard.domain.interactor.StartKeyguardTransitionModule;
import com.android.systemui.keyguard.domain.quickaffordance.KeyguardQuickAffordanceModule;
@@ -66,8 +67,6 @@
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;
-import kotlinx.coroutines.CoroutineDispatcher;
-import kotlinx.coroutines.CoroutineScope;
/**
* Dagger Module providing keyguard.
@@ -82,6 +81,7 @@
KeyguardDataQuickAffordanceModule.class,
KeyguardQuickAffordanceModule.class,
KeyguardRepositoryModule.class,
+ KeyguardFaceAuthModule.class,
StartKeyguardTransitionModule.class,
})
public class KeyguardModule {
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 d5129a6..09002fd 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
@@ -87,6 +87,13 @@
*/
val isStrongBiometricAllowed: StateFlow<Boolean>
+ /**
+ * Whether the current user is allowed to use a convenience biometric for device entry based on
+ * Android Security policies. If false, the user may be able to use strong biometric or primary
+ * authentication for device entry.
+ */
+ val isNonStrongBiometricAllowed: StateFlow<Boolean>
+
/** Whether fingerprint feature is enabled for the current user by the DevicePolicy */
val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean>
@@ -276,6 +283,16 @@
)
)
+ override val isNonStrongBiometricAllowed: StateFlow<Boolean> =
+ strongAuthTracker.isNonStrongBiometricAllowed.stateIn(
+ scope,
+ SharingStarted.Eagerly,
+ strongAuthTracker.isBiometricAllowedForUser(
+ false,
+ userRepository.getSelectedUserInfo().id
+ )
+ )
+
override val isFingerprintEnabledByDevicePolicy: StateFlow<Boolean> =
selectedUserId
.flatMapLatest { userId ->
@@ -297,40 +314,62 @@
private class StrongAuthTracker(private val userRepository: UserRepository, context: Context?) :
LockPatternUtils.StrongAuthTracker(context) {
- private val _authFlags =
+ // Backing field for onStrongAuthRequiredChanged
+ private val _strongAuthFlags =
MutableStateFlow(
StrongAuthenticationFlags(currentUserId, getStrongAuthForUser(currentUserId))
)
+ // Backing field for onIsNonStrongBiometricAllowedChanged
+ private val _nonStrongBiometricAllowed =
+ MutableStateFlow(
+ Pair(currentUserId, isNonStrongBiometricAllowedAfterIdleTimeout(currentUserId))
+ )
+
val currentUserAuthFlags: Flow<StrongAuthenticationFlags> =
userRepository.selectedUserInfo
.map { it.id }
.distinctUntilChanged()
- .flatMapLatest { currUserId ->
- _authFlags
- .filter { it.userId == currUserId }
+ .flatMapLatest { userId ->
+ _strongAuthFlags
+ .filter { it.userId == userId }
.onEach { Log.d(TAG, "currentUser authFlags changed, new value: $it") }
.onStart {
- emit(
- StrongAuthenticationFlags(
- currentUserId,
- getStrongAuthForUser(currentUserId)
- )
- )
+ emit(StrongAuthenticationFlags(userId, getStrongAuthForUser(userId)))
}
}
+ /** isStrongBiometricAllowed for the current user. */
val isStrongBiometricAllowed: Flow<Boolean> =
currentUserAuthFlags.map { isBiometricAllowedForUser(true, it.userId) }
+ /** isNonStrongBiometricAllowed for the current user. */
+ val isNonStrongBiometricAllowed: Flow<Boolean> =
+ userRepository.selectedUserInfo
+ .map { it.id }
+ .distinctUntilChanged()
+ .flatMapLatest { userId ->
+ _nonStrongBiometricAllowed
+ .filter { it.first == userId }
+ .map { it.second }
+ .onEach { Log.d(TAG, "isNonStrongBiometricAllowed changed for current user") }
+ .onStart { emit(isNonStrongBiometricAllowedAfterIdleTimeout(userId)) }
+ }
+
private val currentUserId
get() = userRepository.getSelectedUserInfo().id
override fun onStrongAuthRequiredChanged(userId: Int) {
val newFlags = getStrongAuthForUser(userId)
- _authFlags.value = StrongAuthenticationFlags(userId, newFlags)
+ _strongAuthFlags.value = StrongAuthenticationFlags(userId, newFlags)
Log.d(TAG, "onStrongAuthRequiredChanged for userId: $userId, flag value: $newFlags")
}
+
+ override fun onIsNonStrongBiometricAllowedChanged(userId: Int) {
+ val allowed = isNonStrongBiometricAllowedAfterIdleTimeout(userId)
+ _nonStrongBiometricAllowed.value = Pair(userId, allowed)
+ Log.d(TAG, "onIsNonStrongBiometricAllowedChanged for userId: $userId, $allowed")
+ }
}
private fun DevicePolicyManager.isFaceDisabled(userId: Int): Boolean =
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
new file mode 100644
index 0000000..56e7398
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -0,0 +1,540 @@
+/*
+ * 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 android.app.StatusBarManager
+import android.content.Context
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.os.CancellationSignal
+import com.android.internal.logging.InstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.systemui.Dumpable
+import com.android.systemui.R
+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.dagger.qualifiers.Application
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.UserRepository
+import java.io.PrintWriter
+import java.util.Arrays
+import java.util.stream.Collectors
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineDispatcher
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.withContext
+
+/**
+ * API to run face authentication and detection for device entry / on keyguard (as opposed to the
+ * biometric prompt).
+ */
+interface DeviceEntryFaceAuthRepository {
+ /** Provide the current face authentication state for device entry. */
+ val isAuthenticated: Flow<Boolean>
+
+ /** Whether face auth can run at this point. */
+ val canRunFaceAuth: Flow<Boolean>
+
+ /** Provide the current status of face authentication. */
+ val authenticationStatus: Flow<AuthenticationStatus>
+
+ /** Provide the current status of face detection. */
+ val detectionStatus: Flow<DetectionStatus>
+
+ /** Current state of whether face authentication is locked out or not. */
+ val isLockedOut: Flow<Boolean>
+
+ /** Current state of whether face authentication is running. */
+ val isAuthRunning: Flow<Boolean>
+
+ /**
+ * Trigger face authentication.
+ *
+ * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
+ * ignored if face authentication is already running. Results should be propagated through
+ * [authenticationStatus]
+ *
+ * Run only face detection when [fallbackToDetection] is true and [canRunFaceAuth] is false.
+ */
+ suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean = false)
+
+ /** Stop currently running face authentication or detection. */
+ fun cancel()
+}
+
+@SysUISingleton
+class DeviceEntryFaceAuthRepositoryImpl
+@Inject
+constructor(
+ context: Context,
+ private val faceManager: FaceManager? = null,
+ private val userRepository: UserRepository,
+ private val keyguardBypassController: KeyguardBypassController? = null,
+ @Application private val applicationScope: CoroutineScope,
+ @Main private val mainDispatcher: CoroutineDispatcher,
+ private val sessionTracker: SessionTracker,
+ private val uiEventsLogger: UiEventLogger,
+ private val faceAuthLogger: FaceAuthenticationLogger,
+ private val biometricSettingsRepository: BiometricSettingsRepository,
+ private val deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository,
+ private val trustRepository: TrustRepository,
+ private val keyguardRepository: KeyguardRepository,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ dumpManager: DumpManager,
+) : DeviceEntryFaceAuthRepository, Dumpable {
+ private var authCancellationSignal: CancellationSignal? = null
+ private var detectCancellationSignal: CancellationSignal? = null
+ private var faceAcquiredInfoIgnoreList: Set<Int>
+
+ private var cancelNotReceivedHandlerJob: Job? = null
+
+ private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
+ MutableStateFlow(null)
+ override val authenticationStatus: Flow<AuthenticationStatus>
+ get() = _authenticationStatus.filterNotNull()
+
+ private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
+ override val detectionStatus: Flow<DetectionStatus>
+ get() = _detectionStatus.filterNotNull()
+
+ private val _isLockedOut = MutableStateFlow(false)
+ override val isLockedOut: StateFlow<Boolean> = _isLockedOut
+
+ val isDetectionSupported =
+ faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
+
+ private val _isAuthRunning = MutableStateFlow(false)
+ override val isAuthRunning: StateFlow<Boolean>
+ get() = _isAuthRunning
+
+ private val keyguardSessionId: InstanceId?
+ get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
+
+ private val _canRunFaceAuth = MutableStateFlow(true)
+ override val canRunFaceAuth: StateFlow<Boolean>
+ get() = _canRunFaceAuth
+
+ private val canRunDetection = MutableStateFlow(false)
+
+ private val _isAuthenticated = MutableStateFlow(false)
+ override val isAuthenticated: Flow<Boolean>
+ get() = _isAuthenticated
+
+ private val bypassEnabled: Flow<Boolean> =
+ keyguardBypassController?.let {
+ conflatedCallbackFlow {
+ val callback =
+ object : KeyguardBypassController.OnBypassStateChangedListener {
+ override fun onBypassStateChanged(isEnabled: Boolean) {
+ trySendWithFailureLogging(isEnabled, TAG, "BypassStateChanged")
+ }
+ }
+ it.registerOnBypassStateChangedListener(callback)
+ trySendWithFailureLogging(it.bypassEnabled, TAG, "BypassStateChanged")
+ awaitClose { it.unregisterOnBypassStateChangedListener(callback) }
+ }
+ }
+ ?: flowOf(false)
+
+ private val faceLockoutResetCallback =
+ object : FaceManager.LockoutResetCallback() {
+ override fun onLockoutReset(sensorId: Int) {
+ _isLockedOut.value = false
+ }
+ }
+
+ init {
+ faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
+ faceAcquiredInfoIgnoreList =
+ Arrays.stream(
+ context.resources.getIntArray(
+ R.array.config_face_acquire_device_entry_ignorelist
+ )
+ )
+ .boxed()
+ .collect(Collectors.toSet())
+ dumpManager.registerCriticalDumpable("DeviceEntryFaceAuthRepositoryImpl", this)
+
+ observeFaceAuthGatingChecks()
+ observeFaceDetectGatingChecks()
+ observeFaceAuthResettingConditions()
+ }
+
+ private fun observeFaceAuthResettingConditions() {
+ // Clear auth status when keyguard is going away or when the user is switching.
+ merge(keyguardRepository.isKeyguardGoingAway, userRepository.userSwitchingInProgress)
+ .onEach { goingAwayOrUserSwitchingInProgress ->
+ if (goingAwayOrUserSwitchingInProgress) {
+ _isAuthenticated.value = false
+ }
+ }
+ .launchIn(applicationScope)
+ }
+
+ private fun observeFaceDetectGatingChecks() {
+ // Face detection can run only when lockscreen bypass is enabled
+ // & detection is supported & biometric unlock is not allowed.
+ listOf(
+ canFaceAuthOrDetectRun(),
+ logAndObserve(bypassEnabled, "bypassEnabled"),
+ logAndObserve(
+ biometricSettingsRepository.isNonStrongBiometricAllowed.isFalse(),
+ "nonStrongBiometricIsNotAllowed"
+ ),
+ // We don't want to run face detect if it's not possible to authenticate with FP
+ // from the bouncer. UDFPS is the only fp sensor type that won't support this.
+ logAndObserve(
+ and(isUdfps(), deviceEntryFingerprintAuthRepository.isRunning).isFalse(),
+ "udfpsAuthIsNotPossibleAnymore"
+ )
+ )
+ .reduce(::and)
+ .distinctUntilChanged()
+ .onEach {
+ faceAuthLogger.canRunDetectionChanged(it)
+ canRunDetection.value = it
+ if (!it) {
+ cancelDetection()
+ }
+ }
+ .launchIn(applicationScope)
+ }
+
+ private fun isUdfps() =
+ deviceEntryFingerprintAuthRepository.availableFpSensorType.map {
+ it == BiometricType.UNDER_DISPLAY_FINGERPRINT
+ }
+
+ private fun canFaceAuthOrDetectRun(): Flow<Boolean> {
+ return listOf(
+ logAndObserve(biometricSettingsRepository.isFaceEnrolled, "isFaceEnrolled"),
+ logAndObserve(
+ biometricSettingsRepository.isFaceAuthenticationEnabled,
+ "isFaceAuthenticationEnabled"
+ ),
+ logAndObserve(
+ userRepository.userSwitchingInProgress.isFalse(),
+ "userSwitchingNotInProgress"
+ ),
+ logAndObserve(
+ keyguardRepository.isKeyguardGoingAway.isFalse(),
+ "keyguardNotGoingAway"
+ ),
+ logAndObserve(
+ keyguardRepository.wakefulness
+ .map { WakefulnessModel.isSleepingOrStartingToSleep(it) }
+ .isFalse(),
+ "deviceNotSleepingOrNotStartingToSleep"
+ ),
+ logAndObserve(
+ combine(
+ keyguardInteractor.isSecureCameraActive,
+ alternateBouncerInteractor.isVisible,
+ ) { a, b ->
+ !a || b
+ },
+ "secureCameraNotActiveOrAltBouncerIsShowing"
+ ),
+ logAndObserve(
+ biometricSettingsRepository.isFaceAuthSupportedInCurrentPosture,
+ "isFaceAuthSupportedInCurrentPosture"
+ ),
+ logAndObserve(
+ biometricSettingsRepository.isCurrentUserInLockdown.isFalse(),
+ "userHasNotLockedDownDevice"
+ )
+ )
+ .reduce(::and)
+ }
+
+ private fun observeFaceAuthGatingChecks() {
+ // Face auth can run only if all of the gating conditions are true.
+ listOf(
+ canFaceAuthOrDetectRun(),
+ logAndObserve(isLockedOut.isFalse(), "isNotLocked"),
+ logAndObserve(
+ deviceEntryFingerprintAuthRepository.isLockedOut.isFalse(),
+ "fpLockedOut"
+ ),
+ logAndObserve(trustRepository.isCurrentUserTrusted.isFalse(), "currentUserTrusted"),
+ logAndObserve(
+ biometricSettingsRepository.isNonStrongBiometricAllowed,
+ "nonStrongBiometricIsAllowed"
+ ),
+ logAndObserve(
+ userRepository.selectedUserInfo.map { it.isPrimary },
+ "userIsPrimaryUser"
+ ),
+ )
+ .reduce(::and)
+ .distinctUntilChanged()
+ .onEach {
+ faceAuthLogger.canFaceAuthRunChanged(it)
+ _canRunFaceAuth.value = it
+ if (!it) {
+ // Cancel currently running auth if any of the gating checks are false.
+ faceAuthLogger.cancellingFaceAuth()
+ cancel()
+ }
+ }
+ .launchIn(applicationScope)
+ }
+
+ private val faceAuthCallback =
+ object : FaceManager.AuthenticationCallback() {
+ override fun onAuthenticationFailed() {
+ _authenticationStatus.value = FailedAuthenticationStatus
+ _isAuthenticated.value = false
+ faceAuthLogger.authenticationFailed()
+ onFaceAuthRequestCompleted()
+ }
+
+ override fun onAuthenticationAcquired(acquireInfo: Int) {
+ _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
+ faceAuthLogger.authenticationAcquired(acquireInfo)
+ }
+
+ override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
+ val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
+ if (errorStatus.isLockoutError()) {
+ _isLockedOut.value = true
+ }
+ _authenticationStatus.value = errorStatus
+ _isAuthenticated.value = false
+ if (errorStatus.isCancellationError()) {
+ cancelNotReceivedHandlerJob?.cancel()
+ applicationScope.launch {
+ faceAuthLogger.launchingQueuedFaceAuthRequest(
+ faceAuthRequestedWhileCancellation
+ )
+ faceAuthRequestedWhileCancellation?.let { authenticate(it) }
+ faceAuthRequestedWhileCancellation = null
+ }
+ }
+ faceAuthLogger.authenticationError(
+ errorCode,
+ errString,
+ errorStatus.isLockoutError(),
+ errorStatus.isCancellationError()
+ )
+ onFaceAuthRequestCompleted()
+ }
+
+ override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
+ if (faceAcquiredInfoIgnoreList.contains(code)) {
+ return
+ }
+ _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
+ }
+
+ override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
+ _authenticationStatus.value = SuccessAuthenticationStatus(result)
+ _isAuthenticated.value = true
+ faceAuthLogger.faceAuthSuccess(result)
+ onFaceAuthRequestCompleted()
+ }
+ }
+
+ private fun onFaceAuthRequestCompleted() {
+ cancellationInProgress = false
+ _isAuthRunning.value = false
+ authCancellationSignal = null
+ }
+
+ private val detectionCallback =
+ FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
+ faceAuthLogger.faceDetected()
+ _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
+ }
+
+ private var cancellationInProgress = false
+ private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
+
+ override suspend fun authenticate(uiEvent: FaceAuthUiEvent, fallbackToDetection: Boolean) {
+ if (_isAuthRunning.value) {
+ faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
+ return
+ }
+
+ if (cancellationInProgress) {
+ faceAuthLogger.queuingRequestWhileCancelling(
+ faceAuthRequestedWhileCancellation,
+ uiEvent
+ )
+ faceAuthRequestedWhileCancellation = uiEvent
+ return
+ } else {
+ faceAuthRequestedWhileCancellation = null
+ }
+
+ if (canRunFaceAuth.value) {
+ withContext(mainDispatcher) {
+ // We always want to invoke face auth in the main thread.
+ authCancellationSignal = CancellationSignal()
+ _isAuthRunning.value = true
+ uiEventsLogger.logWithInstanceIdAndPosition(
+ uiEvent,
+ 0,
+ null,
+ keyguardSessionId,
+ uiEvent.extraInfo
+ )
+ faceAuthLogger.authenticating(uiEvent)
+ faceManager?.authenticate(
+ null,
+ authCancellationSignal,
+ faceAuthCallback,
+ null,
+ FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+ )
+ }
+ } else if (fallbackToDetection && canRunDetection.value) {
+ detect()
+ }
+ }
+
+ suspend fun detect() {
+ if (!isDetectionSupported) {
+ faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
+ return
+ }
+ if (_isAuthRunning.value || detectCancellationSignal != null) {
+ faceAuthLogger.skippingDetection(_isAuthRunning.value, detectCancellationSignal != null)
+ return
+ }
+
+ detectCancellationSignal = CancellationSignal()
+ withContext(mainDispatcher) {
+ // We always want to invoke face detect in the main thread.
+ faceAuthLogger.faceDetectionStarted()
+ faceManager?.detectFace(
+ detectCancellationSignal,
+ detectionCallback,
+ FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
+ )
+ }
+ }
+
+ private val currentUserId: Int
+ get() = userRepository.getSelectedUserInfo().id
+
+ fun cancelDetection() {
+ detectCancellationSignal?.cancel()
+ detectCancellationSignal = null
+ }
+
+ override fun cancel() {
+ if (authCancellationSignal == null) return
+
+ authCancellationSignal?.cancel()
+ cancelNotReceivedHandlerJob =
+ applicationScope.launch {
+ delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
+ faceAuthLogger.cancelSignalNotReceived(
+ _isAuthRunning.value,
+ _isLockedOut.value,
+ cancellationInProgress,
+ faceAuthRequestedWhileCancellation
+ )
+ onFaceAuthRequestCompleted()
+ }
+ cancellationInProgress = true
+ _isAuthRunning.value = false
+ }
+
+ private fun logAndObserve(cond: Flow<Boolean>, loggingContext: String): Flow<Boolean> {
+ return cond.distinctUntilChanged().onEach {
+ faceAuthLogger.observedConditionChanged(it, loggingContext)
+ }
+ }
+
+ companion object {
+ const val TAG = "DeviceEntryFaceAuthRepository"
+
+ /**
+ * If no cancel signal has been received after this amount of time, assume that it is
+ * cancelled.
+ */
+ const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("DeviceEntryFaceAuthRepositoryImpl state:")
+ pw.println(" cancellationInProgress: $cancellationInProgress")
+ pw.println(" _isLockedOut.value: ${_isLockedOut.value}")
+ pw.println(" _isAuthRunning.value: ${_isAuthRunning.value}")
+ pw.println(" isDetectionSupported: $isDetectionSupported")
+ pw.println(" FaceManager state:")
+ pw.println(" faceManager: $faceManager")
+ pw.println(" sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
+ pw.println(
+ " supportsFaceDetection: " +
+ "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
+ )
+ pw.println(
+ " faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
+ )
+ pw.println(" authCancellationSignal: $authCancellationSignal")
+ pw.println(" detectCancellationSignal: $detectCancellationSignal")
+ pw.println(" faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
+ pw.println(" _authenticationStatus: ${_authenticationStatus.value}")
+ pw.println(" _detectionStatus: ${_detectionStatus.value}")
+ pw.println(" currentUserId: $currentUserId")
+ pw.println(" keyguardSessionId: $keyguardSessionId")
+ pw.println(" lockscreenBypassEnabled: ${keyguardBypassController?.bypassEnabled ?: false}")
+ }
+}
+/** Combine two boolean flows by and-ing both of them */
+private fun and(flow: Flow<Boolean>, anotherFlow: Flow<Boolean>) =
+ flow.combine(anotherFlow) { a, b -> a && b }
+
+/** "Not" the given flow. The return [Flow] will be true when [this] flow is false. */
+private fun Flow<Boolean>.isFalse(): Flow<Boolean> {
+ return this.map { !it }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
index 4fa56ee..52234b3 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepository.kt
@@ -16,6 +16,8 @@
package com.android.systemui.keyguard.data.repository
+import android.hardware.biometrics.BiometricAuthenticator
+import android.hardware.biometrics.BiometricAuthenticator.Modality
import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
@@ -33,6 +35,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
/** Encapsulates state about device entry fingerprint auth mechanism. */
@@ -49,7 +52,7 @@
/**
* Fingerprint sensor type present on the device, null if fingerprint sensor is not available.
*/
- val availableFpSensorType: BiometricType?
+ val availableFpSensorType: Flow<BiometricType?>
}
/**
@@ -77,11 +80,39 @@
pw.println("isLockedOut=${isLockedOut.value}")
}
- override val availableFpSensorType: BiometricType?
- get() =
- if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
- else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
- else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+ override val availableFpSensorType: Flow<BiometricType?>
+ get() {
+ return if (authController.areAllFingerprintAuthenticatorsRegistered()) {
+ flowOf(getFpSensorType())
+ } else {
+ conflatedCallbackFlow {
+ val callback =
+ object : AuthController.Callback {
+ override fun onAllAuthenticatorsRegistered(@Modality modality: Int) {
+ if (modality == BiometricAuthenticator.TYPE_FINGERPRINT)
+ trySendWithFailureLogging(
+ getFpSensorType(),
+ TAG,
+ "onAllAuthenticatorsRegistered, emitting fpSensorType"
+ )
+ }
+ }
+ authController.addCallback(callback)
+ trySendWithFailureLogging(
+ getFpSensorType(),
+ TAG,
+ "initial value for fpSensorType"
+ )
+ awaitClose { authController.removeCallback(callback) }
+ }
+ }
+ }
+
+ private fun getFpSensorType(): BiometricType? {
+ return if (authController.isUdfpsSupported) BiometricType.UNDER_DISPLAY_FINGERPRINT
+ else if (authController.isSfpsSupported) BiometricType.SIDE_FINGERPRINT
+ else if (authController.isRearFpsSupported) BiometricType.REAR_FINGERPRINT else null
+ }
override val isLockedOut: StateFlow<Boolean> =
conflatedCallbackFlow {
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 64e2a2c..0b506cf 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
@@ -65,8 +65,6 @@
val keyguardAuthenticated: StateFlow<Boolean?>
val showMessage: StateFlow<BouncerShowMessageModel?>
val resourceUpdateRequests: StateFlow<Boolean>
- val bouncerPromptReason: Int
- val bouncerErrorMessage: CharSequence?
val alternateBouncerVisible: StateFlow<Boolean>
val alternateBouncerUIAvailable: StateFlow<Boolean>
val sideFpsShowing: StateFlow<Boolean>
@@ -145,11 +143,6 @@
override val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
- override val bouncerPromptReason: Int
- get() = viewMediatorCallback.bouncerPromptReason
- override val bouncerErrorMessage: CharSequence?
- get() = viewMediatorCallback.consumeCustomMessage()
-
/** Values associated with the AlternateBouncer */
private val _alternateBouncerVisible = MutableStateFlow(false)
override val alternateBouncerVisible = _alternateBouncerVisible.asStateFlow()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
deleted file mode 100644
index a326840..0000000
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManager.kt
+++ /dev/null
@@ -1,346 +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.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager
-import android.content.Context
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.os.CancellationSignal
-import com.android.internal.logging.InstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.systemui.Dumpable
-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.dump.DumpManager
-import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.FailedAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.UserRepository
-import java.io.PrintWriter
-import java.util.Arrays
-import java.util.stream.Collectors
-import javax.inject.Inject
-import kotlinx.coroutines.CoroutineDispatcher
-import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.flow.Flow
-import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.filterNotNull
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
-
-/**
- * API to run face authentication and detection for device entry / on keyguard (as opposed to the
- * biometric prompt).
- */
-interface KeyguardFaceAuthManager {
- /**
- * Trigger face authentication.
- *
- * [uiEvent] provided should be logged whenever face authentication runs. Invocation should be
- * ignored if face authentication is already running. Results should be propagated through
- * [authenticationStatus]
- */
- suspend fun authenticate(uiEvent: FaceAuthUiEvent)
-
- /**
- * Trigger face detection.
- *
- * Invocation should be ignored if face authentication is currently running.
- */
- suspend fun detect()
-
- /** Stop currently running face authentication or detection. */
- fun cancel()
-
- /** Provide the current status of face authentication. */
- val authenticationStatus: Flow<AuthenticationStatus>
-
- /** Provide the current status of face detection. */
- val detectionStatus: Flow<DetectionStatus>
-
- /** Current state of whether face authentication is locked out or not. */
- val isLockedOut: Flow<Boolean>
-
- /** Current state of whether face authentication is running. */
- val isAuthRunning: Flow<Boolean>
-
- /** Is face detection supported. */
- val isDetectionSupported: Boolean
-}
-
-@SysUISingleton
-class KeyguardFaceAuthManagerImpl
-@Inject
-constructor(
- context: Context,
- private val faceManager: FaceManager? = null,
- private val userRepository: UserRepository,
- private val keyguardBypassController: KeyguardBypassController? = null,
- @Application private val applicationScope: CoroutineScope,
- @Main private val mainDispatcher: CoroutineDispatcher,
- private val sessionTracker: SessionTracker,
- private val uiEventsLogger: UiEventLogger,
- private val faceAuthLogger: FaceAuthenticationLogger,
- dumpManager: DumpManager,
-) : KeyguardFaceAuthManager, Dumpable {
- private var cancellationSignal: CancellationSignal? = null
- private val lockscreenBypassEnabled: Boolean
- get() = keyguardBypassController?.bypassEnabled ?: false
- private var faceAcquiredInfoIgnoreList: Set<Int>
-
- private val faceLockoutResetCallback =
- object : FaceManager.LockoutResetCallback() {
- override fun onLockoutReset(sensorId: Int) {
- _isLockedOut.value = false
- }
- }
-
- init {
- faceManager?.addLockoutResetCallback(faceLockoutResetCallback)
- faceAcquiredInfoIgnoreList =
- Arrays.stream(
- context.resources.getIntArray(
- R.array.config_face_acquire_device_entry_ignorelist
- )
- )
- .boxed()
- .collect(Collectors.toSet())
- dumpManager.registerCriticalDumpable("KeyguardFaceAuthManagerImpl", this)
- }
-
- private val faceAuthCallback =
- object : FaceManager.AuthenticationCallback() {
- override fun onAuthenticationFailed() {
- _authenticationStatus.value = FailedAuthenticationStatus
- faceAuthLogger.authenticationFailed()
- onFaceAuthRequestCompleted()
- }
-
- override fun onAuthenticationAcquired(acquireInfo: Int) {
- _authenticationStatus.value = AcquiredAuthenticationStatus(acquireInfo)
- faceAuthLogger.authenticationAcquired(acquireInfo)
- }
-
- override fun onAuthenticationError(errorCode: Int, errString: CharSequence?) {
- val errorStatus = ErrorAuthenticationStatus(errorCode, errString.toString())
- if (errorStatus.isLockoutError()) {
- _isLockedOut.value = true
- }
- _authenticationStatus.value = errorStatus
- if (errorStatus.isCancellationError()) {
- cancelNotReceivedHandlerJob?.cancel()
- applicationScope.launch {
- faceAuthLogger.launchingQueuedFaceAuthRequest(
- faceAuthRequestedWhileCancellation
- )
- faceAuthRequestedWhileCancellation?.let { authenticate(it) }
- faceAuthRequestedWhileCancellation = null
- }
- }
- faceAuthLogger.authenticationError(
- errorCode,
- errString,
- errorStatus.isLockoutError(),
- errorStatus.isCancellationError()
- )
- onFaceAuthRequestCompleted()
- }
-
- override fun onAuthenticationHelp(code: Int, helpStr: CharSequence?) {
- if (faceAcquiredInfoIgnoreList.contains(code)) {
- return
- }
- _authenticationStatus.value = HelpAuthenticationStatus(code, helpStr.toString())
- }
-
- override fun onAuthenticationSucceeded(result: FaceManager.AuthenticationResult) {
- _authenticationStatus.value = SuccessAuthenticationStatus(result)
- faceAuthLogger.faceAuthSuccess(result)
- onFaceAuthRequestCompleted()
- }
- }
-
- private fun onFaceAuthRequestCompleted() {
- cancellationInProgress = false
- _isAuthRunning.value = false
- cancellationSignal = null
- }
-
- private val detectionCallback =
- FaceManager.FaceDetectionCallback { sensorId, userId, isStrong ->
- faceAuthLogger.faceDetected()
- _detectionStatus.value = DetectionStatus(sensorId, userId, isStrong)
- }
-
- private var cancellationInProgress = false
- private var faceAuthRequestedWhileCancellation: FaceAuthUiEvent? = null
-
- override suspend fun authenticate(uiEvent: FaceAuthUiEvent) {
- if (_isAuthRunning.value) {
- faceAuthLogger.ignoredFaceAuthTrigger(uiEvent)
- return
- }
-
- if (cancellationInProgress) {
- faceAuthLogger.queuingRequestWhileCancelling(
- faceAuthRequestedWhileCancellation,
- uiEvent
- )
- faceAuthRequestedWhileCancellation = uiEvent
- return
- } else {
- faceAuthRequestedWhileCancellation = null
- }
-
- withContext(mainDispatcher) {
- // We always want to invoke face auth in the main thread.
- cancellationSignal = CancellationSignal()
- _isAuthRunning.value = true
- uiEventsLogger.logWithInstanceIdAndPosition(
- uiEvent,
- 0,
- null,
- keyguardSessionId,
- uiEvent.extraInfo
- )
- faceAuthLogger.authenticating(uiEvent)
- faceManager?.authenticate(
- null,
- cancellationSignal,
- faceAuthCallback,
- null,
- FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
- )
- }
- }
-
- override suspend fun detect() {
- if (!isDetectionSupported) {
- faceAuthLogger.detectionNotSupported(faceManager, faceManager?.sensorPropertiesInternal)
- return
- }
- if (_isAuthRunning.value) {
- faceAuthLogger.skippingBecauseAlreadyRunning("detection")
- return
- }
-
- cancellationSignal = CancellationSignal()
- withContext(mainDispatcher) {
- // We always want to invoke face detect in the main thread.
- faceAuthLogger.faceDetectionStarted()
- faceManager?.detectFace(
- cancellationSignal,
- detectionCallback,
- FaceAuthenticateOptions.Builder().setUserId(currentUserId).build()
- )
- }
- }
-
- private val currentUserId: Int
- get() = userRepository.getSelectedUserInfo().id
-
- override fun cancel() {
- if (cancellationSignal == null) return
-
- cancellationSignal?.cancel()
- cancelNotReceivedHandlerJob =
- applicationScope.launch {
- delay(DEFAULT_CANCEL_SIGNAL_TIMEOUT)
- faceAuthLogger.cancelSignalNotReceived(
- _isAuthRunning.value,
- _isLockedOut.value,
- cancellationInProgress,
- faceAuthRequestedWhileCancellation
- )
- onFaceAuthRequestCompleted()
- }
- cancellationInProgress = true
- _isAuthRunning.value = false
- }
-
- private var cancelNotReceivedHandlerJob: Job? = null
-
- private val _authenticationStatus: MutableStateFlow<AuthenticationStatus?> =
- MutableStateFlow(null)
- override val authenticationStatus: Flow<AuthenticationStatus>
- get() = _authenticationStatus.filterNotNull()
-
- private val _detectionStatus = MutableStateFlow<DetectionStatus?>(null)
- override val detectionStatus: Flow<DetectionStatus>
- get() = _detectionStatus.filterNotNull()
-
- private val _isLockedOut = MutableStateFlow(false)
- override val isLockedOut: Flow<Boolean> = _isLockedOut
-
- override val isDetectionSupported =
- faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection ?: false
-
- private val _isAuthRunning = MutableStateFlow(false)
- override val isAuthRunning: Flow<Boolean>
- get() = _isAuthRunning
-
- private val keyguardSessionId: InstanceId?
- get() = sessionTracker.getSessionId(StatusBarManager.SESSION_KEYGUARD)
-
- companion object {
- const val TAG = "KeyguardFaceAuthManager"
-
- /**
- * If no cancel signal has been received after this amount of time, assume that it is
- * cancelled.
- */
- const val DEFAULT_CANCEL_SIGNAL_TIMEOUT = 3000L
- }
-
- override fun dump(pw: PrintWriter, args: Array<out String>) {
- pw.println("KeyguardFaceAuthManagerImpl state:")
- pw.println(" cancellationInProgress: $cancellationInProgress")
- pw.println(" _isLockedOut.value: ${_isLockedOut.value}")
- pw.println(" _isAuthRunning.value: ${_isAuthRunning.value}")
- pw.println(" isDetectionSupported: $isDetectionSupported")
- pw.println(" FaceManager state:")
- pw.println(" faceManager: $faceManager")
- pw.println(" sensorPropertiesInternal: ${faceManager?.sensorPropertiesInternal}")
- pw.println(
- " supportsFaceDetection: " +
- "${faceManager?.sensorPropertiesInternal?.firstOrNull()?.supportsFaceDetection}"
- )
- pw.println(
- " faceAuthRequestedWhileCancellation: ${faceAuthRequestedWhileCancellation?.reason}"
- )
- pw.println(" cancellationSignal: $cancellationSignal")
- pw.println(" faceAcquiredInfoIgnoreList: $faceAcquiredInfoIgnoreList")
- pw.println(" _authenticationStatus: ${_authenticationStatus.value}")
- pw.println(" _detectionStatus: ${_detectionStatus.value}")
- pw.println(" currentUserId: $currentUserId")
- pw.println(" keyguardSessionId: $keyguardSessionId")
- pw.println(" lockscreenBypassEnabled: $lockscreenBypassEnabled")
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.kt
new file mode 100644
index 0000000..3c66f24
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthModule.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 dagger.Binds
+import dagger.Module
+
+@Module
+interface KeyguardFaceAuthModule {
+ @Binds
+ fun deviceEntryFaceAuthRepository(
+ impl: DeviceEntryFaceAuthRepositoryImpl
+ ): DeviceEntryFaceAuthRepository
+
+ @Binds fun trustRepository(impl: TrustRepositoryImpl): TrustRepository
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
index 33f4e2e..9212aa1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/PrimaryBouncerInteractor.kt
@@ -27,7 +27,6 @@
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
-import com.android.settingslib.Utils
import com.android.systemui.DejankUtils
import com.android.systemui.R
import com.android.systemui.classifier.FalsingCollector
@@ -42,12 +41,12 @@
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
-import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
+import javax.inject.Inject
/**
* Encapsulates business logic for interacting with the lock-screen primary (pin/pattern/password)
@@ -82,12 +81,6 @@
/** Runnable to show the primary bouncer. */
val showRunnable = Runnable {
repository.setPrimaryShow(true)
- primaryBouncerView.delegate?.showPromptReason(repository.bouncerPromptReason)
- (repository.bouncerErrorMessage as? String)?.let {
- repository.setShowMessage(
- BouncerShowMessageModel(message = it, Utils.getColorError(context))
- )
- }
repository.setPrimaryShowingSoon(false)
primaryBouncerCallbackInteractor.dispatchVisibilityChanged(View.VISIBLE)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
index b1c5f8f..eded9c1 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/FaceAuthenticationModels.kt
@@ -18,7 +18,10 @@
import android.hardware.face.FaceManager
-/** Authentication status provided by [com.android.keyguard.faceauth.KeyguardFaceAuthManager] */
+/**
+ * Authentication status provided by
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
+ */
sealed class AuthenticationStatus
/** Success authentication status. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index 468a6b5..927df55 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -121,6 +121,7 @@
securityContainerController.showPrimarySecurityScreen(
/* turningOff= */ false
)
+ securityContainerController.setInitialMessage()
securityContainerController.appear()
securityContainerController.onResume(
KeyguardSecurityView.SCREEN_ON
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index 647e3a1..f7355d5 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -7,17 +7,17 @@
import com.android.systemui.log.dagger.FaceAuthLog
import com.android.systemui.plugins.log.LogBuffer
import com.android.systemui.plugins.log.LogLevel.DEBUG
-import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
-private const val TAG = "KeyguardFaceAuthManagerLog"
+private const val TAG = "DeviceEntryFaceAuthRepositoryLog"
/**
- * Helper class for logging for [com.android.keyguard.faceauth.KeyguardFaceAuthManager]
+ * Helper class for logging for
+ * [com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepository]
*
* To enable logcat echoing for an entire buffer:
* ```
- * adb shell settings put global systemui/buffer/KeyguardFaceAuthManagerLog <logLevel>
+ * adb shell settings put global systemui/buffer/DeviceEntryFaceAuthRepositoryLog <logLevel>
*
* ```
*/
@@ -82,8 +82,19 @@
)
}
- fun skippingBecauseAlreadyRunning(@CompileTimeConstant operation: String) {
- logBuffer.log(TAG, DEBUG, "isAuthRunning is true, skipping $operation")
+ fun skippingDetection(isAuthRunning: Boolean, detectCancellationNotNull: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = isAuthRunning
+ bool2 = detectCancellationNotNull
+ },
+ {
+ "Skipping running detection: isAuthRunning: $bool1, " +
+ "detectCancellationNotNull: $bool2"
+ }
+ )
}
fun faceDetectionStarted() {
@@ -177,4 +188,33 @@
{ "Face authenticated successfully: userId: $int1, isStrongBiometric: $bool1" }
)
}
+
+ fun observedConditionChanged(newValue: Boolean, context: String) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ {
+ bool1 = newValue
+ str1 = context
+ },
+ { "Observed condition changed: $str1, new value: $bool1" }
+ )
+ }
+
+ fun canFaceAuthRunChanged(canRun: Boolean) {
+ logBuffer.log(TAG, DEBUG, { bool1 = canRun }, { "canFaceAuthRun value changed to $bool1" })
+ }
+
+ fun canRunDetectionChanged(canRunDetection: Boolean) {
+ logBuffer.log(
+ TAG,
+ DEBUG,
+ { bool1 = canRunDetection },
+ { "canRunDetection value changed to $bool1" }
+ )
+ }
+
+ fun cancellingFaceAuth() {
+ logBuffer.log(TAG, DEBUG, "cancelling face auth because a gating condition became false")
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
index b98a92f..d848b43 100644
--- a/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
+++ b/packages/SystemUI/src/com/android/systemui/log/SessionTracker.java
@@ -28,6 +28,8 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.InstanceIdSequence;
+import com.android.internal.logging.UiEvent;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -60,6 +62,7 @@
private final AuthController mAuthController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final KeyguardStateController mKeyguardStateController;
+ private final UiEventLogger mUiEventLogger;
private final Map<Integer, InstanceId> mSessionToInstanceId = new HashMap<>();
private boolean mKeyguardSessionStarted;
@@ -69,12 +72,14 @@
IStatusBarService statusBarService,
AuthController authController,
KeyguardUpdateMonitor keyguardUpdateMonitor,
- KeyguardStateController keyguardStateController
+ KeyguardStateController keyguardStateController,
+ UiEventLogger uiEventLogger
) {
mStatusBarManagerService = statusBarService;
mAuthController = authController;
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mKeyguardStateController = keyguardStateController;
+ mUiEventLogger = uiEventLogger;
}
@Override
@@ -116,6 +121,10 @@
}
private void endSession(int type) {
+ endSession(type, null);
+ }
+
+ private void endSession(int type, @Nullable SessionUiEvent endSessionUiEvent) {
if (mSessionToInstanceId.getOrDefault(type, null) == null) {
Log.e(TAG, "session [" + getString(type) + "] was not started");
return;
@@ -127,6 +136,9 @@
if (DEBUG) {
Log.d(TAG, "Session end for [" + getString(type) + "] id=" + instanceId);
}
+ if (endSessionUiEvent != null) {
+ mUiEventLogger.log(endSessionUiEvent, instanceId);
+ }
mStatusBarManagerService.onSessionEnded(type, instanceId);
} catch (RemoteException e) {
Log.e(TAG, "Unable to send onSessionEnded for session="
@@ -139,7 +151,7 @@
@Override
public void onStartedGoingToSleep(int why) {
if (mKeyguardSessionStarted) {
- endSession(SESSION_KEYGUARD);
+ endSession(SESSION_KEYGUARD, SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP);
}
// Start a new session whenever the device goes to sleep
@@ -162,7 +174,8 @@
startSession(SESSION_KEYGUARD);
} else if (!keyguardShowing && wasSessionStarted) {
mKeyguardSessionStarted = false;
- endSession(SESSION_KEYGUARD);
+ endSession(SESSION_KEYGUARD,
+ SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY);
}
}
};
@@ -200,4 +213,22 @@
return "unknownType=" + sessionType;
}
+
+ enum SessionUiEvent implements UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "A keyguard session ended due to the keyguard going away.")
+ KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY(1354),
+
+ @UiEvent(doc = "A keyguard session ended due to display going to sleep.")
+ KEYGUARD_SESSION_END_GOING_TO_SLEEP(1355);
+
+ private final int mId;
+ SessionUiEvent(int id) {
+ mId = id;
+ }
+
+ @Override
+ public int getId() {
+ return mId;
+ }
+ }
}
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 e204def..3775e2c 100644
--- a/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
+++ b/packages/SystemUI/src/com/android/systemui/log/dagger/LogModule.java
@@ -367,13 +367,13 @@
/**
* Provides a {@link LogBuffer} for use by
- * {@link com.android.keyguard.faceauth.KeyguardFaceAuthManagerImpl}.
+ * {@link com.android.systemui.keyguard.data.repository.DeviceEntryFaceAuthRepositoryImpl}.
*/
@Provides
@SysUISingleton
@FaceAuthLog
public static LogBuffer provideFaceAuthLog(LogBufferFactory factory) {
- return factory.create("KeyguardFaceAuthManagerLog", 300);
+ return factory.create("DeviceEntryFaceAuthRepositoryLog", 300);
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
index f92a5ab..731bb2f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseAdapter.java
@@ -66,6 +66,8 @@
protected final MediaOutputController mController;
+ private static final int UNMUTE_DEFAULT_VOLUME = 2;
+
Context mContext;
View mHolderView;
boolean mIsDragging;
@@ -193,10 +195,6 @@
mTwoLineTitleText.setTextColor(mController.getColorItemContent());
if (mController.isAdvancedLayoutSupported()) {
mVolumeValueText.setTextColor(mController.getColorItemContent());
- mTitleIcon.setOnTouchListener(((v, event) -> {
- mSeekBar.dispatchTouchEvent(event);
- return false;
- }));
}
mSeekBar.setProgressTintList(
ColorStateList.valueOf(mController.getColorSeekbarProgress()));
@@ -546,13 +544,21 @@
private void enableSeekBar(MediaDevice device) {
mSeekBar.setEnabled(true);
mSeekBar.setOnTouchListener((v, event) -> false);
- if (mController.isAdvancedLayoutSupported()) {
- updateIconAreaClickListener((v) -> {
+ updateIconAreaClickListener((v) -> {
+ if (device.getCurrentVolume() == 0) {
+ mController.adjustVolume(device, UNMUTE_DEFAULT_VOLUME);
+ updateUnmutedVolumeIcon();
+ mTitleIcon.setOnTouchListener(((iconV, event) -> false));
+ } else {
mSeekBar.resetVolume();
mController.adjustVolume(device, 0);
updateMutedVolumeIcon();
- });
- }
+ mTitleIcon.setOnTouchListener(((iconV, event) -> {
+ mSeekBar.dispatchTouchEvent(event);
+ return false;
+ }));
+ }
+ });
}
protected void setUpDeviceIcon(MediaDevice device) {
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index f5c0a94..334c70b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -296,7 +296,8 @@
// EXTRA_USE_STYLUS_MODE does not mean a stylus is in-use, but a stylus entrypoint
// was used to start the note task.
- putExtra(Intent.EXTRA_USE_STYLUS_MODE, true)
+ val useStylusMode = info.entryPoint != NoteTaskEntryPoint.KEYBOARD_SHORTCUT
+ putExtra(Intent.EXTRA_USE_STYLUS_MODE, useStylusMode)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
// We should ensure the note experience can be opened both as a full screen (lockscreen)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
index 2fa8f9a..fae325c 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEntryPoint.kt
@@ -25,7 +25,8 @@
* 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 {
+enum class
+NoteTaskEntryPoint {
/** @see [LaunchNoteTaskActivity] */
WIDGET_PICKER_SHORTCUT,
@@ -38,4 +39,7 @@
/** @see [AppClipsTrampolineActivity] */
APP_CLIPS,
+
+ /** @see [NoteTaskInitializer.callbacks] */
+ KEYBOARD_SHORTCUT,
}
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
index 16dd16e..48a5933 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskEventLogger.kt
@@ -18,6 +18,7 @@
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.KEYBOARD_SHORTCUT
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
@@ -51,6 +52,7 @@
WIDGET_PICKER_SHORTCUT -> NOTE_OPENED_VIA_SHORTCUT
QUICK_AFFORDANCE -> NOTE_OPENED_VIA_KEYGUARD_QUICK_AFFORDANCE
APP_CLIPS -> return
+ KEYBOARD_SHORTCUT -> return
null -> return
}
uiEventLogger.log(event, info.uid, info.packageName)
@@ -70,6 +72,7 @@
WIDGET_PICKER_SHORTCUT -> return
QUICK_AFFORDANCE -> return
APP_CLIPS -> return
+ KEYBOARD_SHORTCUT -> return
null -> return
}
uiEventLogger.log(event, info.uid, info.packageName)
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 04ed08b..23ee13b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -41,9 +41,12 @@
@VisibleForTesting
val callbacks =
object : CommandQueue.Callbacks {
- override fun handleSystemKey(keyCode: Int) {
- if (keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
+ override fun handleSystemKey(key: KeyEvent) {
+ if (key.keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
+ } else if (key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed &&
+ key.isCtrlPressed) {
+ controller.showNoteTask(NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
index 5355865..0641eec 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -47,7 +47,6 @@
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
-import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_INFORM_JOB_SCHEDULER_OF_PENDING_APP_STOP
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_FOOTER_DOT
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS
@@ -80,8 +79,6 @@
/** A controller for the dealing with services running in the foreground. */
interface FgsManagerController {
- /** Whether the TaskManager (and therefore this controller) is actually available. */
- val isAvailable: StateFlow<Boolean>
/** The number of packages with a service running in the foreground. */
val numRunningPackages: Int
@@ -155,7 +152,6 @@
companion object {
private const val INTERACTION_JANK_TAG = "active_background_apps"
- private const val DEFAULT_TASK_MANAGER_ENABLED = true
private const val DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT = false
private const val DEFAULT_TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS = true
private const val DEFAULT_TASK_MANAGER_SHOW_USER_VISIBLE_JOBS = true
@@ -165,9 +161,6 @@
override var newChangesSinceDialogWasDismissed = false
private set
- val _isAvailable = MutableStateFlow(false)
- override val isAvailable: StateFlow<Boolean> = _isAvailable.asStateFlow()
-
val _showFooterDot = MutableStateFlow(false)
override val showFooterDot: StateFlow<Boolean> = _showFooterDot.asStateFlow()
@@ -264,7 +257,6 @@
NAMESPACE_SYSTEMUI,
backgroundExecutor
) {
- _isAvailable.value = it.getBoolean(TASK_MANAGER_ENABLED, _isAvailable.value)
_showFooterDot.value =
it.getBoolean(TASK_MANAGER_SHOW_FOOTER_DOT, _showFooterDot.value)
showStopBtnForUserAllowlistedApps = it.getBoolean(
@@ -280,11 +272,6 @@
TASK_MANAGER_SHOW_STOP_BUTTON_FOR_USER_ALLOWLISTED_APPS,
informJobSchedulerOfPendingAppStop)
}
-
- _isAvailable.value = deviceConfigProxy.getBoolean(
- NAMESPACE_SYSTEMUI,
- TASK_MANAGER_ENABLED, DEFAULT_TASK_MANAGER_ENABLED
- )
_showFooterDot.value = deviceConfigProxy.getBoolean(
NAMESPACE_SYSTEMUI,
TASK_MANAGER_SHOW_FOOTER_DOT, DEFAULT_TASK_MANAGER_SHOW_FOOTER_DOT
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 6be74a0..ce690e2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -26,6 +26,7 @@
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.qs.QSTileView;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.util.leak.GarbageMonitor;
@@ -34,7 +35,7 @@
import java.util.Collection;
import java.util.List;
-public interface QSHost extends PanelInteractor {
+public interface QSHost extends PanelInteractor, CustomTileAddedRepository {
String TILES_SETTING = Settings.Secure.QS_TILES;
int POSITION_AT_END = -1;
@@ -102,9 +103,6 @@
void removeTileByUser(ComponentName tile);
void changeTilesByUser(List<String> previousTiles, List<String> newTiles);
- boolean isTileAdded(ComponentName componentName, int userId);
- void setTileAdded(ComponentName componentName, int userId, boolean added);
-
int indexOf(String tileSpec);
InstanceId getNewInstanceId();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
index 958fa71..964fe71 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSHostModule.kt
@@ -20,6 +20,8 @@
import com.android.systemui.flags.Flags
import com.android.systemui.qs.QSHost
import com.android.systemui.qs.QSTileHost
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedSharedPrefsRepository
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractorImpl
import dagger.Binds
@@ -46,5 +48,19 @@
qsHost
}
}
+
+ @Provides
+ @JvmStatic
+ fun provideCustomTileAddedRepository(
+ featureFlags: FeatureFlags,
+ qsHost: QSHost,
+ customTileAddedRepository: CustomTileAddedSharedPrefsRepository
+ ): CustomTileAddedRepository {
+ return if (featureFlags.isEnabled(Flags.QS_PIPELINE_NEW_HOST)) {
+ customTileAddedRepository
+ } else {
+ qsHost
+ }
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index 9f93e49..7a10a27 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -33,6 +33,7 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.external.TileLifecycleManager.TileChangeListener;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.settings.UserTracker;
import java.util.List;
@@ -59,6 +60,7 @@
private final TileLifecycleManager mStateManager;
private final Handler mHandler;
private final UserTracker mUserTracker;
+ private final CustomTileAddedRepository mCustomTileAddedRepository;
private boolean mBindRequested;
private boolean mBindAllowed;
private boolean mBound;
@@ -72,9 +74,10 @@
private boolean mStarted = false;
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
- BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
- this(tileServices, handler, userTracker, new TileLifecycleManager(handler,
- tileServices.getContext(), tileServices,
+ BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
+ CustomTileAddedRepository customTileAddedRepository) {
+ this(tileServices, handler, userTracker, customTileAddedRepository,
+ new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
new Intent(TileService.ACTION_QS_TILE).setComponent(component),
userTracker.getUserHandle()));
@@ -82,11 +85,13 @@
@VisibleForTesting
TileServiceManager(TileServices tileServices, Handler handler, UserTracker userTracker,
+ CustomTileAddedRepository customTileAddedRepository,
TileLifecycleManager tileLifecycleManager) {
mServices = tileServices;
mHandler = handler;
mStateManager = tileLifecycleManager;
mUserTracker = userTracker;
+ mCustomTileAddedRepository = customTileAddedRepository;
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
@@ -111,8 +116,8 @@
mStarted = true;
ComponentName component = mStateManager.getComponent();
final int userId = mStateManager.getUserId();
- if (!mServices.getHost().isTileAdded(component, userId)) {
- mServices.getHost().setTileAdded(component, userId, true);
+ if (!mCustomTileAddedRepository.isTileAdded(component, userId)) {
+ mCustomTileAddedRepository.setTileAdded(component, userId, true);
mStateManager.onTileAdded();
mStateManager.flushMessagesAndUnbind();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 42536fe..121955c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -41,6 +41,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
@@ -77,6 +78,7 @@
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
private final PanelInteractor mPanelInteractor;
+ private final CustomTileAddedRepository mCustomTileAddedRepository;
private int mMaxBound = DEFAULT_MAX_BOUND;
@@ -89,7 +91,8 @@
KeyguardStateController keyguardStateController,
CommandQueue commandQueue,
StatusBarIconController statusBarIconController,
- PanelInteractor panelInteractor) {
+ PanelInteractor panelInteractor,
+ CustomTileAddedRepository customTileAddedRepository) {
mHost = host;
mKeyguardStateController = keyguardStateController;
mContext = mHost.getContext();
@@ -101,6 +104,7 @@
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
mPanelInteractor = panelInteractor;
+ mCustomTileAddedRepository = customTileAddedRepository;
}
public Context getContext() {
@@ -128,7 +132,7 @@
protected TileServiceManager onCreateTileService(ComponentName component,
BroadcastDispatcher broadcastDispatcher) {
return new TileServiceManager(this, mHandlerProvider.get(), component,
- broadcastDispatcher, mUserTracker);
+ broadcastDispatcher, mUserTracker, mCustomTileAddedRepository);
}
public void freeService(CustomTile tile, TileServiceManager service) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
index 37a9c40..bd9d70c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/data/repository/ForegroundServicesRepository.kt
@@ -32,8 +32,6 @@
interface ForegroundServicesRepository {
/**
* The number of packages with a service running in the foreground.
- *
- * Note that this will be equal to 0 if [FgsManagerController.isAvailable] is false.
*/
val foregroundServicesCount: Flow<Int>
@@ -52,32 +50,24 @@
fgsManagerController: FgsManagerController,
) : ForegroundServicesRepository {
override val foregroundServicesCount: Flow<Int> =
- fgsManagerController.isAvailable
- .flatMapLatest { isAvailable ->
- if (!isAvailable) {
- return@flatMapLatest flowOf(0)
+ conflatedCallbackFlow<Int> {
+ fun updateState(numberOfPackages: Int) {
+ trySendWithFailureLogging(numberOfPackages, TAG)
}
- conflatedCallbackFlow {
- fun updateState(numberOfPackages: Int) {
- trySendWithFailureLogging(numberOfPackages, TAG)
- }
-
- val listener =
+ val listener =
object : FgsManagerController.OnNumberOfPackagesChangedListener {
override fun onNumberOfPackagesChanged(numberOfPackages: Int) {
updateState(numberOfPackages)
}
}
- fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
- updateState(fgsManagerController.numRunningPackages)
- awaitClose {
- fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
- }
+ fgsManagerController.addOnNumberOfPackagesChangedListener(listener)
+ updateState(fgsManagerController.numRunningPackages)
+ awaitClose {
+ fgsManagerController.removeOnNumberOfPackagesChangedListener(listener)
}
- }
- .distinctUntilChanged()
+ }.distinctUntilChanged()
override val hasNewChanges: Flow<Boolean> =
fgsManagerController.showFooterDot.flatMapLatest { showFooterDot ->
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
new file mode 100644
index 0000000..7fc906b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedRepository.kt
@@ -0,0 +1,65 @@
+/*
+ * 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.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.settings.UserFileManager
+import javax.inject.Inject
+
+/**
+ * Repository for keeping track of whether a given [CustomTile] [ComponentName] has been added to
+ * the set of current tiles for a user. This is used to determine when lifecycle methods in
+ * `TileService` about the tile being added/removed need to be called.
+ */
+interface CustomTileAddedRepository {
+ /**
+ * Check if a particular [CustomTile] associated with [componentName] has been added for
+ * [userId] and has not been removed since.
+ */
+ fun isTileAdded(componentName: ComponentName, userId: Int): Boolean
+
+ /**
+ * Persists whether a particular [CustomTile] associated with [componentName] has been added and
+ * it's currently in the set of selected tiles for [userId].
+ */
+ fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean)
+}
+
+@SysUISingleton
+class CustomTileAddedSharedPrefsRepository
+@Inject
+constructor(private val userFileManager: UserFileManager) : CustomTileAddedRepository {
+
+ override fun isTileAdded(componentName: ComponentName, userId: Int): Boolean {
+ return userFileManager
+ .getSharedPreferences(TILES, 0, userId)
+ .getBoolean(componentName.flattenToString(), false)
+ }
+
+ override fun setTileAdded(componentName: ComponentName, userId: Int, added: Boolean) {
+ userFileManager
+ .getSharedPreferences(TILES, 0, userId)
+ .edit()
+ .putBoolean(componentName.flattenToString(), added)
+ .apply()
+ }
+
+ companion object {
+ private const val TILES = "tiles_prefs"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 3090b79..4a31998 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -496,7 +496,7 @@
}
// Colors
- if (state.state != lastState || state.disabledByPolicy || lastDisabledByPolicy) {
+ if (state.state != lastState || state.disabledByPolicy != lastDisabledByPolicy) {
singleAnimator.cancel()
mQsLogger?.logTileBackgroundColorUpdateIfInternetTile(
state.spec,
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 1b83397..90e31af 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -336,7 +336,8 @@
@Override
public void expandNotificationPanel() {
verifyCallerAndClearCallingIdentity("expandNotificationPanel",
- () -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+ () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index a9af1a2..6f85c45 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -44,7 +44,6 @@
import android.app.Notification;
import android.app.assist.AssistContent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -525,39 +524,6 @@
mWindowManager.getCurrentWindowMetrics().getWindowInsets());
}
- @MainThread
- void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
- RequestCallback requestCallback) {
- Assert.isMainThread();
- mCurrentRequestCallback = requestCallback;
- takeScreenshotInternal(topComponent, finisher, getFullScreenRect());
- }
-
- @MainThread
- void handleImageAsScreenshot(Bitmap screenshot, Rect screenshotScreenBounds,
- Insets visibleInsets, int taskId, int userId, ComponentName topComponent,
- Consumer<Uri> finisher, RequestCallback requestCallback) {
- Assert.isMainThread();
- if (screenshot == null) {
- Log.e(TAG, "Got null bitmap from screenshot message");
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- requestCallback.reportError();
- return;
- }
-
- boolean showFlash = false;
- if (screenshotScreenBounds == null
- || !aspectRatiosMatch(screenshot, visibleInsets, screenshotScreenBounds)) {
- showFlash = true;
- visibleInsets = Insets.NONE;
- screenshotScreenBounds = new Rect(0, 0, screenshot.getWidth(), screenshot.getHeight());
- }
- mCurrentRequestCallback = requestCallback;
- saveScreenshot(screenshot, finisher, screenshotScreenBounds, visibleInsets, topComponent,
- showFlash, UserHandle.of(userId));
- }
-
/**
* Clears current screenshot
*/
@@ -695,103 +661,6 @@
setContentView(mScreenshotView);
}
- /**
- * Takes a screenshot of the current display and shows an animation.
- */
- private void takeScreenshotInternal(ComponentName topComponent, Consumer<Uri> finisher,
- Rect crop) {
- mScreenshotTakenInPortrait =
- mContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
-
- // copy the input Rect, since SurfaceControl.screenshot can mutate it
- Rect screenRect = new Rect(crop);
- Bitmap screenshot = mImageCapture.captureDisplay(mDisplayTracker.getDefaultDisplayId(),
- crop);
-
- if (screenshot == null) {
- Log.e(TAG, "takeScreenshotInternal: Screenshot bitmap was null");
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- if (mCurrentRequestCallback != null) {
- mCurrentRequestCallback.reportError();
- }
- return;
- }
-
- saveScreenshot(screenshot, finisher, screenRect, Insets.NONE, topComponent, true,
- Process.myUserHandle());
-
- mBroadcastSender.sendBroadcast(new Intent(ClipboardOverlayController.SCREENSHOT_ACTION),
- ClipboardOverlayController.SELF_PERMISSION);
- }
-
- private void saveScreenshot(Bitmap screenshot, Consumer<Uri> finisher, Rect screenRect,
- Insets screenInsets, ComponentName topComponent, boolean showFlash, UserHandle owner) {
- withWindowAttached(() -> {
- if (mUserManager.isManagedProfile(owner.getIdentifier())) {
- mScreenshotView.announceForAccessibility(mContext.getResources().getString(
- R.string.screenshot_saving_work_profile_title));
- } else {
- mScreenshotView.announceForAccessibility(
- mContext.getResources().getString(R.string.screenshot_saving_title));
- }
- });
-
- mScreenshotView.reset();
-
- if (mScreenshotView.isAttachedToWindow()) {
- // if we didn't already dismiss for another reason
- if (!mScreenshotView.isDismissing()) {
- mUiEventLogger.log(ScreenshotEvent.SCREENSHOT_REENTERED, 0, mPackageName);
- }
- if (DEBUG_WINDOW) {
- Log.d(TAG, "saveScreenshot: screenshotView is already attached, resetting. "
- + "(dismissing=" + mScreenshotView.isDismissing() + ")");
- }
- }
- mPackageName = topComponent == null ? "" : topComponent.getPackageName();
- mScreenshotView.setPackageName(mPackageName);
-
- mScreenshotView.updateOrientation(
- mWindowManager.getCurrentWindowMetrics().getWindowInsets());
-
- mScreenBitmap = screenshot;
-
- if (!isUserSetupComplete(owner)) {
- Log.w(TAG, "User setup not complete, displaying toast only");
- // User setup isn't complete, so we don't want to show any UI beyond a toast, as editing
- // and sharing shouldn't be exposed to the user.
- saveScreenshotAndToast(owner, finisher);
- return;
- }
-
- // Optimizations
- mScreenBitmap.setHasAlpha(false);
- mScreenBitmap.prepareToDraw();
-
- saveScreenshotInWorkerThread(owner, finisher, this::showUiOnActionsReady,
- this::showUiOnQuickShareActionReady);
-
- // The window is focusable by default
- setWindowFocusable(true);
- mScreenshotView.requestFocus();
-
- enqueueScrollCaptureRequest(owner);
-
- attachWindow();
- prepareAnimation(screenRect, showFlash, () -> {
- mMessageContainerController.onScreenshotTaken(owner);
- });
-
- mScreenshotView.badgeScreenshot(mContext.getPackageManager().getUserBadgedIcon(
- mContext.getDrawable(R.drawable.overlay_badge_background), owner));
- mScreenshotView.setScreenshot(mScreenBitmap, screenInsets);
- // ignore system bar insets for the purpose of window layout
- mWindow.getDecorView().setOnApplyWindowInsetsListener(
- (v, insets) -> WindowInsets.CONSUMED);
- mScreenshotHandler.cancelTimeout(); // restarted after animation
- }
-
private void prepareAnimation(Rect screenRect, boolean showFlash,
Runnable onAnimationComplete) {
mScreenshotView.getViewTreeObserver().addOnPreDrawListener(
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
index f3d2828..1cdad83 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/TakeScreenshotService.java
@@ -36,9 +36,6 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Bitmap;
-import android.graphics.Insets;
-import android.graphics.Rect;
import android.net.Uri;
import android.os.Handler;
import android.os.IBinder;
@@ -49,7 +46,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
-import android.view.WindowManager;
import android.widget.Toast;
import com.android.internal.annotations.VisibleForTesting;
@@ -58,7 +54,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
@@ -222,30 +217,17 @@
return;
}
- if (mFeatureFlags.isEnabled(Flags.SCREENSHOT_METADATA_REFACTOR)) {
- Log.d(TAG, "Processing screenshot data");
- ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
- try {
- mProcessor.processAsync(screenshotData,
- (data) -> dispatchToController(data, onSaved, callback));
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to process screenshot request!", e);
- logFailedRequest(request);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- callback.reportError();
- }
- } else {
- try {
- mProcessor.processAsync(request,
- (r) -> dispatchToController(r, onSaved, callback));
- } catch (IllegalStateException e) {
- Log.e(TAG, "Failed to process screenshot request!", e);
- logFailedRequest(request);
- mNotificationsController.notifyScreenshotError(
- R.string.screenshot_failed_to_capture_text);
- callback.reportError();
- }
+ Log.d(TAG, "Processing screenshot data");
+ ScreenshotData screenshotData = ScreenshotData.fromRequest(request);
+ try {
+ mProcessor.processAsync(screenshotData,
+ (data) -> dispatchToController(data, onSaved, callback));
+ } catch (IllegalStateException e) {
+ Log.e(TAG, "Failed to process screenshot request!", e);
+ logFailedRequest(request);
+ mNotificationsController.notifyScreenshotError(
+ R.string.screenshot_failed_to_capture_text);
+ callback.reportError();
}
}
@@ -257,38 +239,6 @@
mScreenshot.handleScreenshot(screenshot, uriConsumer, callback);
}
- private void dispatchToController(ScreenshotRequest request,
- Consumer<Uri> uriConsumer, RequestCallback callback) {
- ComponentName topComponent = request.getTopComponent();
- String packageName = topComponent == null ? "" : topComponent.getPackageName();
- mUiEventLogger.log(
- ScreenshotEvent.getScreenshotSource(request.getSource()), 0, packageName);
-
- switch (request.getType()) {
- case WindowManager.TAKE_SCREENSHOT_FULLSCREEN:
- if (DEBUG_SERVICE) {
- Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_FULLSCREEN");
- }
- mScreenshot.takeScreenshotFullscreen(topComponent, uriConsumer, callback);
- break;
- case WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE:
- if (DEBUG_SERVICE) {
- Log.d(TAG, "handleMessage: TAKE_SCREENSHOT_PROVIDED_IMAGE");
- }
- Bitmap screenshot = request.getBitmap();
- Rect screenBounds = request.getBoundsInScreen();
- Insets insets = request.getInsets();
- int taskId = request.getTaskId();
- int userId = request.getUserId();
-
- mScreenshot.handleImageAsScreenshot(screenshot, screenBounds, insets,
- taskId, userId, topComponent, uriConsumer, callback);
- break;
- default:
- Log.wtf(TAG, "Invalid screenshot option: " + request.getType());
- }
- }
-
private void logFailedRequest(ScreenshotRequest request) {
ComponentName topComponent = request.getTopComponent();
String packageName = topComponent == null ? "" : topComponent.getPackageName();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index c435799..fb4feb8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -53,6 +53,7 @@
import android.os.RemoteException;
import android.util.Pair;
import android.util.SparseArray;
+import android.view.KeyEvent;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
import android.view.WindowInsetsController.Behavior;
@@ -302,7 +303,7 @@
default void remQsTile(ComponentName tile) { }
default void clickTile(ComponentName tile) { }
- default void handleSystemKey(int arg1) { }
+ default void handleSystemKey(KeyEvent arg1) { }
default void showPinningEnterExitToast(boolean entering) { }
default void showPinningEscapeToast() { }
default void handleShowGlobalActionsMenu() { }
@@ -891,9 +892,9 @@
}
@Override
- public void handleSystemKey(int key) {
+ public void handleSystemKey(KeyEvent key) {
synchronized (mLock) {
- mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key, 0).sendToTarget();
+ mHandler.obtainMessage(MSG_HANDLE_SYSTEM_KEY, key).sendToTarget();
}
}
@@ -1534,7 +1535,7 @@
break;
case MSG_HANDLE_SYSTEM_KEY:
for (int i = 0; i < mCallbacks.size(); i++) {
- mCallbacks.get(i).handleSystemKey(msg.arg1);
+ mCallbacks.get(i).handleSystemKey((KeyEvent) msg.obj);
}
break;
case MSG_SHOW_GLOBAL_ACTIONS:
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 9272f06..0195d45 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -157,7 +157,8 @@
if (animationAdapter != null) {
if (ENABLE_SHELL_TRANSITIONS) {
options = ActivityOptions.makeRemoteTransition(
- RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter));
+ RemoteTransitionAdapter.adaptRemoteAnimation(animationAdapter,
+ "SysUILaunch"));
} else {
options = ActivityOptions.makeRemoteAnimation(animationAdapter);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
index e776015..8b6617b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -308,7 +308,7 @@
* settings. Down action closes the entire panel.
*/
@Override
- public void handleSystemKey(int key) {
+ public void handleSystemKey(KeyEvent key) {
if (CentralSurfaces.SPEW) {
Log.d(CentralSurfaces.TAG, "handleNavigationKey: " + key);
}
@@ -320,11 +320,11 @@
// Panels are not available in setup
if (!mDeviceProvisionedController.isCurrentUserSetup()) return;
- if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key) {
+ if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_UP == key.getKeyCode()) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_UP);
mNotificationPanelViewController.collapse(
false /* delayed */, 1.0f /* speedUpFactor */);
- } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key) {
+ } else if (KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN == key.getKeyCode()) {
mMetricsLogger.action(MetricsEvent.ACTION_SYSTEM_NAVIGATION_KEY_DOWN);
if (mNotificationPanelViewController.isFullyCollapsed()) {
if (mVibrateOnOpening) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index c817466..b303151 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -85,16 +85,17 @@
/**
* Called when keyguard is about to be displayed and allows to perform custom animation
+ *
+ * @return A handle that can be used for cancelling the animation, if necessary
*/
- fun animateInKeyguard(keyguardView: View, after: Runnable) =
- animations.firstOrNull {
+ fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+ animations.forEach {
if (it.shouldAnimateInKeyguard()) {
- it.animateInKeyguard(keyguardView, after)
- true
- } else {
- false
+ return@animateInKeyguard it.animateInKeyguard(keyguardView, after)
}
}
+ return null
+ }
/**
* If returns true it will disable propagating touches to apps and keyguard
@@ -211,7 +212,10 @@
fun onAlwaysOnChanged(alwaysOn: Boolean) {}
fun shouldAnimateInKeyguard(): Boolean = false
- fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle? {
+ after.run()
+ return null
+ }
fun shouldDelayKeyguardShow(): Boolean = false
fun isKeyguardShowDelayed(): Boolean = false
@@ -224,3 +228,7 @@
fun shouldAnimateDozingChange(): Boolean = true
fun shouldAnimateClockChange(): Boolean = true
}
+
+interface AnimatorHandle {
+ fun cancel()
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index 118bfc5..deb0414 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -160,7 +160,7 @@
* Animates in the provided keyguard view, ending in the same position that it will be in on
* AOD.
*/
- override fun animateInKeyguard(keyguardView: View, after: Runnable) {
+ override fun animateInKeyguard(keyguardView: View, after: Runnable): AnimatorHandle {
shouldAnimateInKeyguard = false
keyguardView.alpha = 0f
keyguardView.visibility = View.VISIBLE
@@ -175,11 +175,36 @@
// We animate the Y properly separately using the PropertyAnimator, as the panel
// view also needs to update the end position.
PropertyAnimator.cancelAnimation(keyguardView, AnimatableProperty.Y)
- PropertyAnimator.setProperty<View>(keyguardView, AnimatableProperty.Y, currentY,
- AnimationProperties().setDuration(duration.toLong()),
- true /* animate */)
- keyguardView.animate()
+ // Start the animation on the next frame using Choreographer APIs. animateInKeyguard() is
+ // called while the system is busy processing lots of requests, so delaying the animation a
+ // frame will mitigate jank. In the event the animation is cancelled before the next frame
+ // is called, this callback will be removed
+ val keyguardAnimator = keyguardView.animate()
+ val nextFrameCallback = TraceUtils.namedRunnable("startAnimateInKeyguard") {
+ PropertyAnimator.setProperty(keyguardView, AnimatableProperty.Y, currentY,
+ AnimationProperties().setDuration(duration.toLong()),
+ true /* animate */)
+ keyguardAnimator.start()
+ }
+ DejankUtils.postAfterTraversal(nextFrameCallback)
+ val animatorHandle = object : AnimatorHandle {
+ private var hasCancelled = false
+ override fun cancel() {
+ if (!hasCancelled) {
+ DejankUtils.removeCallbacks(nextFrameCallback)
+ // If we're cancelled, reset state flags/listeners. The end action above
+ // will not be called, which is what we want since that will finish the
+ // screen off animation and show the lockscreen, which we don't want if we
+ // were cancelled.
+ aodUiAnimationPlaying = false
+ decidedToAnimateGoingToSleep = null
+ keyguardView.animate().setListener(null)
+ hasCancelled = true
+ }
+ }
+ }
+ keyguardAnimator
.setDuration(duration.toLong())
.setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
.alpha(1f)
@@ -205,14 +230,7 @@
}
.setListener(object : AnimatorListenerAdapter() {
override fun onAnimationCancel(animation: Animator?) {
- // If we're cancelled, reset state flags/listeners. The end action above
- // will not be called, which is what we want since that will finish the
- // screen off animation and show the lockscreen, which we don't want if we
- // were cancelled.
- aodUiAnimationPlaying = false
- decidedToAnimateGoingToSleep = null
- keyguardView.animate().setListener(null)
-
+ animatorHandle.cancel()
interactionJankMonitor.cancel(CUJ_SCREEN_OFF_SHOW_AOD)
}
@@ -222,7 +240,7 @@
CUJ_SCREEN_OFF_SHOW_AOD)
}
})
- .start()
+ return animatorHandle
}
override fun onStartedWakingUp() {
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index bd60401..e492534 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -295,8 +295,8 @@
@Override
public void notifyExpandNotification() {
mSysUiMainExecutor.execute(
- () -> mCommandQueue.handleSystemKey(
- KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
+ () -> mCommandQueue.handleSystemKey(new KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN)));
}
});
diff --git a/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
new file mode 100644
index 0000000..68473ba
--- /dev/null
+++ b/packages/SystemUI/tests/res/drawable-nodpi/romainguy_rockaway.jpg
Binary files differ
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
index 7ce2b1c..1ba9931 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardAbsKeyInputViewControllerTest.java
@@ -152,19 +152,16 @@
false);
}
+
@Test
public void testReset() {
mKeyguardAbsKeyInputViewController.reset();
verify(mKeyguardMessageAreaController).setMessage("", false);
- verify(mAbsKeyInputView).resetPasswordText(false, false);
- verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
}
@Test
- public void onResume_Reset() {
+ public void testResume() {
mKeyguardAbsKeyInputViewController.onResume(KeyguardSecurityView.VIEW_REVEALED);
- verify(mKeyguardMessageAreaController).setMessage("", false);
- verify(mAbsKeyInputView).resetPasswordText(false, false);
verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt());
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
index 6ae28b7..a8d5569 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPatternViewControllerTest.kt
@@ -122,22 +122,8 @@
}
@Test
- fun reset() {
- mKeyguardPatternViewController.reset()
- verify(mLockPatternView).setInStealthMode(anyBoolean())
- verify(mLockPatternView).enableInput()
- verify(mLockPatternView).setEnabled(true)
- verify(mLockPatternView).clearPattern()
- verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
- }
-
- @Test
fun resume() {
mKeyguardPatternViewController.onResume(KeyguardSecurityView.VIEW_REVEALED)
- verify(mLockPatternView).setInStealthMode(anyBoolean())
- verify(mLockPatternView).enableInput()
- verify(mLockPatternView).setEnabled(true)
- verify(mLockPatternView).clearPattern()
verify(mLockPatternUtils).getLockoutAttemptDeadline(anyInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
index 33f0ae5..b6287598 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerTest.java
@@ -141,27 +141,6 @@
}
@Test
- public void testUnlockIconShows_biometricUnlockedTrue() {
- // GIVEN UDFPS sensor location is available
- setupUdfps();
-
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- reset(mLockIconView);
-
- // WHEN face auth's biometric running state changes
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
-
- // THEN the unlock icon is shown
- verify(mLockIconView).setContentDescription(UNLOCKED_LABEL);
- }
-
- @Test
public void testLockIconStartState() {
// GIVEN lock icon state
setupShowLockIcon();
@@ -268,27 +247,6 @@
}
@Test
- public void lockIconShows_afterBiometricsCleared() {
- // GIVEN lock icon controller is initialized and view is attached
- init(/* useMigrationFlag= */false);
- captureKeyguardUpdateMonitorCallback();
-
- // GIVEN user has unlocked with a biometric auth (ie: face auth)
- // and biometric running state changes
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(true);
- mKeyguardUpdateMonitorCallback.onBiometricRunningStateChanged(false,
- BiometricSourceType.FACE);
- reset(mLockIconView);
-
- // WHEN biometrics are cleared
- when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(anyInt())).thenReturn(false);
- mKeyguardUpdateMonitorCallback.onBiometricsCleared();
-
- // THEN the lock icon is shown
- verify(mLockIconView).setContentDescription(LOCKED_LABEL);
- }
-
- @Test
public void lockIconShows_afterUnlockStateChanges() {
// GIVEN lock icon controller is initialized and view is attached
init(/* useMigrationFlag= */false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
index 236384b..4ea9616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/AllModelTest.kt
@@ -32,6 +32,7 @@
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.Mockito.never
+import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -161,6 +162,7 @@
}
verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback).onChange()
}
@Test
@@ -176,6 +178,7 @@
)
verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback).onChange()
}
@Test
@@ -191,6 +194,7 @@
}
verify(controlsModelCallback, never()).onFirstChange()
+ verify(controlsModelCallback, never()).onChange()
}
@Test
@@ -207,6 +211,7 @@
}
verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback).onChange()
}
@Test
@@ -222,6 +227,7 @@
)
verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback).onChange()
}
@Test
@@ -236,5 +242,24 @@
}
verify(controlsModelCallback, never()).onFirstChange()
+ verify(controlsModelCallback, never()).onChange()
+ }
+
+ @Test
+ fun testAddSecondChange_callbacks() {
+ model.changeFavoriteStatus("${idPrefix}4", true)
+ model.changeFavoriteStatus("${idPrefix}5", true)
+
+ verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback, times(2)).onChange()
+ }
+
+ @Test
+ fun testRemoveSecondChange_callbacks() {
+ model.changeFavoriteStatus("${idPrefix}1", false)
+ model.changeFavoriteStatus("${idPrefix}3", false)
+
+ verify(controlsModelCallback).onFirstChange()
+ verify(controlsModelCallback, times(2)).onChange()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
index 3b6f7d1..4210675 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsEditingActivityTest.kt
@@ -2,27 +2,33 @@
import android.content.ComponentName
import android.content.Intent
+import android.os.Bundle
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.controls.CustomIconCache
import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
import java.util.concurrent.CountDownLatch
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
+import org.mockito.ArgumentMatchers.eq
import org.mockito.Captor
import org.mockito.Mock
import org.mockito.Mockito.verify
@@ -32,7 +38,15 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ControlsEditingActivityTest : SysuiTestCase() {
+
+ private companion object {
+ val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+ val TEST_STRUCTURE: CharSequence = "TestStructure"
+ val TEST_APP: CharSequence = "TestApp"
+ }
+
private val uiExecutor = FakeExecutor(FakeSystemClock())
+ private val featureFlags = FakeFeatureFlags()
@Mock lateinit var controller: ControlsControllerImpl
@@ -40,9 +54,6 @@
@Mock lateinit var customIconCache: CustomIconCache
- @Mock lateinit var uiController: ControlsUiController
-
- private lateinit var controlsEditingActivity: ControlsEditingActivity_Factory
private var latch: CountDownLatch = CountDownLatch(1)
@Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@@ -58,11 +69,11 @@
) {
override fun create(intent: Intent?): TestableControlsEditingActivity {
return TestableControlsEditingActivity(
+ featureFlags,
uiExecutor,
controller,
userTracker,
customIconCache,
- uiController,
mockDispatcher,
latch
)
@@ -75,19 +86,17 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val intent = Intent()
- intent.putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, "TestTitle")
- val cname = ComponentName("TestPackageName", "TestClassName")
- intent.putExtra(Intent.EXTRA_COMPONENT_NAME, cname)
- activityRule.launchActivity(intent)
+
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
}
@Test
fun testBackCallbackRegistrationAndUnregistration() {
+ launchActivity()
// 1. ensure that launching the activity results in it registering a callback
verify(mockDispatcher)
.registerOnBackInvokedCallback(
- ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
captureCallback.capture()
)
activityRule.finishActivity()
@@ -96,15 +105,102 @@
verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
}
- public class TestableControlsEditingActivity(
- private val executor: FakeExecutor,
- private val controller: ControlsControllerImpl,
- private val userTracker: UserTracker,
- private val customIconCache: CustomIconCache,
- private val uiController: ControlsUiController,
+ @Test
+ fun testNewFlowDisabled_addControlsButton_gone() {
+ with(launchActivity()) {
+ val addControlsButton = requireViewById<Button>(R.id.addControls)
+ assertThat(addControlsButton.visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun testNewFlowEnabled_addControlsButton_visible() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity()) {
+ val addControlsButton = requireViewById<Button>(R.id.addControls)
+ assertThat(addControlsButton.visibility).isEqualTo(View.VISIBLE)
+ assertThat(addControlsButton.isEnabled).isTrue()
+ }
+ }
+
+ @Test
+ fun testNotLaunchFromFavoriting_saveButton_disabled() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity(isFromFavoriting = false)) {
+ val saveButton = requireViewById<Button>(R.id.done)
+ assertThat(saveButton.isEnabled).isFalse()
+ }
+ }
+
+ @Test
+ fun testLaunchFromFavoriting_saveButton_enabled() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity(isFromFavoriting = true)) {
+ val saveButton = requireViewById<Button>(R.id.done)
+ assertThat(saveButton.isEnabled).isTrue()
+ }
+ }
+
+ @Test
+ fun testNotFromFavoriting_addControlsPressed_launchesFavouriting() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity(isFromFavoriting = false)) {
+ val addControls = requireViewById<Button>(R.id.addControls)
+
+ activityRule.runOnUiThread { addControls.performClick() }
+
+ with(startActivityData!!.intent) {
+ assertThat(component)
+ .isEqualTo(ComponentName(context, ControlsFavoritingActivity::class.java))
+ assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE))
+ .isEqualTo(TEST_STRUCTURE)
+ assertThat(
+ getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+ )
+ .isEqualTo(TEST_COMPONENT)
+ assertThat(getCharSequenceExtra(ControlsFavoritingActivity.EXTRA_APP))
+ .isEqualTo(TEST_APP)
+ assertThat(getByteExtra(ControlsFavoritingActivity.EXTRA_SOURCE, -1))
+ .isEqualTo(ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_EDITING)
+ }
+ }
+ }
+
+ private fun launchActivity(
+ componentName: ComponentName = TEST_COMPONENT,
+ structure: CharSequence = TEST_STRUCTURE,
+ isFromFavoriting: Boolean = false,
+ app: CharSequence = TEST_APP,
+ ): TestableControlsEditingActivity =
+ activityRule.launchActivity(
+ Intent().apply {
+ putExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, isFromFavoriting)
+ putExtra(ControlsEditingActivity.EXTRA_STRUCTURE, structure)
+ putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+ putExtra(ControlsEditingActivity.EXTRA_APP, app)
+ }
+ )
+
+ class TestableControlsEditingActivity(
+ featureFlags: FakeFeatureFlags,
+ executor: FakeExecutor,
+ controller: ControlsControllerImpl,
+ userTracker: UserTracker,
+ customIconCache: CustomIconCache,
private val mockDispatcher: OnBackInvokedDispatcher,
private val latch: CountDownLatch
- ) : ControlsEditingActivity(executor, controller, userTracker, customIconCache, uiController) {
+ ) :
+ ControlsEditingActivity(
+ featureFlags,
+ executor,
+ controller,
+ userTracker,
+ customIconCache,
+ ) {
+
+ var startActivityData: StartActivityData? = null
+ private set
+
override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
return mockDispatcher
}
@@ -114,5 +210,13 @@
// ensures that test runner thread does not proceed until ui thread is done
latch.countDown()
}
+
+ override fun startActivity(intent: Intent) {
+ startActivityData = StartActivityData(intent, null)
+ }
+
+ override fun startActivity(intent: Intent, options: Bundle?) {
+ startActivityData = StartActivityData(intent, options)
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
index 3655232..6884616 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/ControlsFavoritingActivityTest.kt
@@ -1,30 +1,49 @@
package com.android.systemui.controls.management
+import android.content.ComponentName
import android.content.Intent
+import android.os.Bundle
+import android.service.controls.Control
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.View
+import android.widget.Button
import android.window.OnBackInvokedCallback
import android.window.OnBackInvokedDispatcher
import androidx.test.filters.FlakyTest
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import androidx.test.runner.intercepting.SingleActivityFactory
+import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.controls.ControlStatus
+import com.android.systemui.controls.ControlsServiceInfo
+import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsControllerImpl
-import com.android.systemui.controls.ui.ControlsUiController
+import com.android.systemui.controls.controller.createLoadDataObject
import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.settings.UserTracker
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
import com.google.common.util.concurrent.MoreExecutors
import java.util.concurrent.CountDownLatch
import java.util.concurrent.Executor
+import java.util.function.Consumer
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
-import org.mockito.ArgumentMatchers
import org.mockito.Captor
import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -32,7 +51,19 @@
@RunWith(AndroidTestingRunner::class)
@TestableLooper.RunWithLooper
class ControlsFavoritingActivityTest : SysuiTestCase() {
+
+ private companion object {
+ val TEST_COMPONENT = ComponentName("TestPackageName", "TestClassName")
+ val TEST_CONTROL =
+ mock(Control::class.java, Answers.RETURNS_MOCKS)!!.apply {
+ whenever(structure).thenReturn(TEST_STRUCTURE)
+ }
+ val TEST_STRUCTURE: CharSequence = "TestStructure"
+ val TEST_APP: CharSequence = "TestApp"
+ }
+
@Main private val executor: Executor = MoreExecutors.directExecutor()
+ private val featureFlags = FakeFeatureFlags()
@Mock lateinit var controller: ControlsControllerImpl
@@ -40,13 +71,15 @@
@Mock lateinit var userTracker: UserTracker
- @Mock lateinit var uiController: ControlsUiController
-
- private lateinit var controlsFavoritingActivity: ControlsFavoritingActivity_Factory
private var latch: CountDownLatch = CountDownLatch(1)
@Mock private lateinit var mockDispatcher: OnBackInvokedDispatcher
@Captor private lateinit var captureCallback: ArgumentCaptor<OnBackInvokedCallback>
+ @Captor
+ private lateinit var listingCallback:
+ ArgumentCaptor<ControlsListingController.ControlsListingCallback>
+ @Captor
+ private lateinit var controlsCallback: ArgumentCaptor<Consumer<ControlsController.LoadData>>
@Rule
@JvmField
@@ -58,11 +91,11 @@
) {
override fun create(intent: Intent?): TestableControlsFavoritingActivity {
return TestableControlsFavoritingActivity(
+ featureFlags,
executor,
controller,
listingController,
userTracker,
- uiController,
mockDispatcher,
latch
)
@@ -75,19 +108,18 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- val intent = Intent()
- intent.putExtra(ControlsFavoritingActivity.EXTRA_FROM_PROVIDER_SELECTOR, true)
- activityRule.launchActivity(intent)
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, false)
}
// b/259549854 to root-cause and fix
@FlakyTest
@Test
fun testBackCallbackRegistrationAndUnregistration() {
+ launchActivity()
// 1. ensure that launching the activity results in it registering a callback
verify(mockDispatcher)
.registerOnBackInvokedCallback(
- ArgumentMatchers.eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
+ eq(OnBackInvokedDispatcher.PRIORITY_DEFAULT),
captureCallback.capture()
)
activityRule.finishActivity()
@@ -96,22 +128,116 @@
verify(mockDispatcher).unregisterOnBackInvokedCallback(captureCallback.value)
}
- public class TestableControlsFavoritingActivity(
+ @Test
+ fun testNewFlowEnabled_buttons() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity()) {
+ verify(listingController).addCallback(listingCallback.capture())
+ listingCallback.value.onServicesUpdated(
+ listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+ )
+
+ val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+ assertThat(rearrangeButton.visibility).isEqualTo(View.VISIBLE)
+ assertThat(rearrangeButton.isEnabled).isFalse()
+ assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.GONE)
+ }
+ }
+
+ @Test
+ fun testNewFlowDisabled_buttons() {
+ with(launchActivity()) {
+ verify(listingController).addCallback(listingCallback.capture())
+ activityRule.runOnUiThread {
+ listingCallback.value.onServicesUpdated(
+ listOf(
+ mock(ControlsServiceInfo::class.java),
+ mock(ControlsServiceInfo::class.java)
+ )
+ )
+ }
+
+ val rearrangeButton = requireViewById<Button>(R.id.rearrange)
+ assertThat(rearrangeButton.visibility).isEqualTo(View.GONE)
+ assertThat(rearrangeButton.isEnabled).isFalse()
+ assertThat(requireViewById<Button>(R.id.other_apps).visibility).isEqualTo(View.VISIBLE)
+ }
+ }
+
+ @Test
+ fun testNewFlowEnabled_rearrangePressed_savesAndlaunchesActivity() {
+ featureFlags.set(Flags.CONTROLS_MANAGEMENT_NEW_FLOWS, true)
+ with(launchActivity()) {
+ verify(listingController).addCallback(capture(listingCallback))
+ listingCallback.value.onServicesUpdated(
+ listOf(mock(ControlsServiceInfo::class.java), mock(ControlsServiceInfo::class.java))
+ )
+ verify(controller).loadForComponent(any(), capture(controlsCallback), any())
+ activityRule.runOnUiThread {
+ controlsCallback.value.accept(
+ createLoadDataObject(
+ listOf(ControlStatus(TEST_CONTROL, TEST_COMPONENT, true)),
+ emptyList(),
+ )
+ )
+ requireViewById<Button>(R.id.rearrange).performClick()
+ }
+
+ verify(controller).replaceFavoritesForStructure(any())
+ with(startActivityData!!.intent) {
+ assertThat(component)
+ .isEqualTo(ComponentName(context, ControlsEditingActivity::class.java))
+ assertThat(
+ getParcelableExtra(Intent.EXTRA_COMPONENT_NAME, ComponentName::class.java)
+ )
+ .isEqualTo(TEST_COMPONENT)
+ assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_APP))
+ .isEqualTo(TEST_APP)
+ assertThat(getBooleanExtra(ControlsEditingActivity.EXTRA_FROM_FAVORITING, false))
+ .isTrue()
+ assertThat(getCharSequenceExtra(ControlsEditingActivity.EXTRA_STRUCTURE))
+ .isEqualTo("")
+ }
+ }
+ }
+
+ private fun launchActivity(
+ componentName: ComponentName = TEST_COMPONENT,
+ structure: CharSequence = TEST_STRUCTURE,
+ app: CharSequence = TEST_APP,
+ source: Byte = ControlsFavoritingActivity.EXTRA_SOURCE_VALUE_FROM_PROVIDER_SELECTOR,
+ ): TestableControlsFavoritingActivity =
+ activityRule.launchActivity(
+ Intent().apply {
+ putExtra(Intent.EXTRA_COMPONENT_NAME, componentName)
+ putExtra(ControlsFavoritingActivity.EXTRA_STRUCTURE, structure)
+ putExtra(ControlsFavoritingActivity.EXTRA_APP, app)
+ putExtra(ControlsFavoritingActivity.EXTRA_SOURCE, source)
+ }
+ )
+
+ class TestableControlsFavoritingActivity(
+ featureFlags: FeatureFlags,
executor: Executor,
controller: ControlsControllerImpl,
listingController: ControlsListingController,
userTracker: UserTracker,
- uiController: ControlsUiController,
private val mockDispatcher: OnBackInvokedDispatcher,
private val latch: CountDownLatch
) :
ControlsFavoritingActivity(
+ featureFlags,
executor,
controller,
listingController,
userTracker,
- uiController
) {
+
+ var triedToFinish = false
+
+ var startActivityData: StartActivityData? = null
+ private set
+
override fun getOnBackInvokedDispatcher(): OnBackInvokedDispatcher {
return mockDispatcher
}
@@ -121,5 +247,17 @@
// ensures that test runner thread does not proceed until ui thread is done
latch.countDown()
}
+
+ override fun startActivity(intent: Intent) {
+ startActivityData = StartActivityData(intent, null)
+ }
+
+ override fun startActivity(intent: Intent, options: Bundle?) {
+ startActivityData = StartActivityData(intent, options)
+ }
+
+ override fun animateExitAndFinish() {
+ triedToFinish = true
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
new file mode 100644
index 0000000..977e3ba
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/management/StartActivityData.kt
@@ -0,0 +1,6 @@
+package com.android.systemui.controls.management
+
+import android.content.Intent
+import android.os.Bundle
+
+data class StartActivityData(val intent: Intent, val options: Bundle?)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
index 7f6e2ba..08427da 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/DreamOverlayTouchMonitorTest.java
@@ -399,7 +399,21 @@
}
@Test
- public void testPause() {
+ public void testPauseWithNoActiveSessions() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ environment.updateLifecycle(observerOwnerPair -> {
+ observerOwnerPair.first.onPause(observerOwnerPair.second);
+ });
+
+ environment.verifyInputSessionDispose();
+ }
+
+ @Test
+ public void testDeferredPauseWithActiveSessions() {
final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
final Environment environment = new Environment(Stream.of(touchHandler)
@@ -417,14 +431,59 @@
environment.publishInputEvent(event);
verify(eventListener).onInputEvent(eq(event));
+ final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+ ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+ verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
environment.updateLifecycle(observerOwnerPair -> {
observerOwnerPair.first.onPause(observerOwnerPair.second);
});
+ verify(environment.mInputSession, never()).dispose();
+
+ // End session
+ touchSessionArgumentCaptor.getValue().pop();
+ environment.executeAll();
+
+ // Check to make sure the input session is now disposed.
environment.verifyInputSessionDispose();
}
@Test
+ public void testDestroyWithActiveSessions() {
+ final DreamTouchHandler touchHandler = Mockito.mock(DreamTouchHandler.class);
+
+ final Environment environment = new Environment(Stream.of(touchHandler)
+ .collect(Collectors.toCollection(HashSet::new)));
+
+ final InputEvent initialEvent = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(initialEvent);
+
+ // Ensure session started
+ final InputChannelCompat.InputEventListener eventListener =
+ registerInputEventListener(touchHandler);
+
+ // First event will be missed since we register after the execution loop,
+ final InputEvent event = Mockito.mock(InputEvent.class);
+ environment.publishInputEvent(event);
+ verify(eventListener).onInputEvent(eq(event));
+
+ final ArgumentCaptor<DreamTouchHandler.TouchSession> touchSessionArgumentCaptor =
+ ArgumentCaptor.forClass(DreamTouchHandler.TouchSession.class);
+
+ verify(touchHandler).onSessionStart(touchSessionArgumentCaptor.capture());
+
+ environment.updateLifecycle(observerOwnerPair -> {
+ observerOwnerPair.first.onDestroy(observerOwnerPair.second);
+ });
+
+ // Check to make sure the input session is now disposed.
+ environment.verifyInputSessionDispose();
+ }
+
+
+ @Test
public void testPilfering() {
final DreamTouchHandler touchHandler1 = Mockito.mock(DreamTouchHandler.class);
final DreamTouchHandler touchHandler2 = Mockito.mock(DreamTouchHandler.class);
@@ -476,7 +535,7 @@
environment.executeAll();
environment.updateLifecycle(observerOwnerPair -> {
- observerOwnerPair.first.onPause(observerOwnerPair.second);
+ observerOwnerPair.first.onDestroy(observerOwnerPair.second);
});
environment.executeAll();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
new file mode 100644
index 0000000..5704ef3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/ShadeTouchHandlerTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.dreams.touch;
+
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.testing.AndroidTestingRunner;
+import android.view.GestureDetector;
+import android.view.MotionEvent;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shared.system.InputChannelCompat;
+import com.android.systemui.statusbar.phone.CentralSurfaces;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Optional;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class ShadeTouchHandlerTest extends SysuiTestCase {
+ @Mock
+ CentralSurfaces mCentralSurfaces;
+
+ @Mock
+ NotificationPanelViewController mNotificationPanelViewController;
+
+ @Mock
+ DreamTouchHandler.TouchSession mTouchSession;
+
+ ShadeTouchHandler mTouchHandler;
+
+ private static final int TOUCH_HEIGHT = 20;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mTouchHandler = new ShadeTouchHandler(Optional.of(mCentralSurfaces),
+ TOUCH_HEIGHT);
+ when(mCentralSurfaces.getNotificationPanelViewController())
+ .thenReturn(mNotificationPanelViewController);
+ }
+
+ /**
+ * Verify that touches aren't handled when the bouncer is showing.
+ */
+ @Test
+ public void testInactiveOnBouncer() {
+ when(mCentralSurfaces.isBouncerShowing()).thenReturn(true);
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).pop();
+ }
+
+ /**
+ * Make sure {@link ShadeTouchHandler}
+ */
+ @Test
+ public void testTouchPilferingOnScroll() {
+ final MotionEvent motionEvent1 = Mockito.mock(MotionEvent.class);
+ final MotionEvent motionEvent2 = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<GestureDetector.OnGestureListener> gestureListenerArgumentCaptor =
+ ArgumentCaptor.forClass(GestureDetector.OnGestureListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerGestureListener(gestureListenerArgumentCaptor.capture());
+
+ assertThat(gestureListenerArgumentCaptor.getValue()
+ .onScroll(motionEvent1, motionEvent2, 1, 1))
+ .isTrue();
+ }
+
+ /**
+ * Ensure touches are propagated to the {@link NotificationPanelViewController}.
+ */
+ @Test
+ public void testEventPropagation() {
+ final MotionEvent motionEvent = Mockito.mock(MotionEvent.class);
+
+ final ArgumentCaptor<InputChannelCompat.InputEventListener>
+ inputEventListenerArgumentCaptor =
+ ArgumentCaptor.forClass(InputChannelCompat.InputEventListener.class);
+
+ mTouchHandler.onSessionStart(mTouchSession);
+ verify(mTouchSession).registerInputListener(inputEventListenerArgumentCaptor.capture());
+ inputEventListenerArgumentCaptor.getValue().onInputEvent(motionEvent);
+ verify(mNotificationPanelViewController).handleExternalTouch(motionEvent);
+ }
+
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
new file mode 100644
index 0000000..ccd631e
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/graphics/ImageLoaderTest.kt
@@ -0,0 +1,346 @@
+package com.android.systemui.graphics
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.ImageDecoder
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.Icon
+import android.graphics.drawable.VectorDrawable
+import android.net.Uri
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import java.io.File
+import java.io.FileInputStream
+import java.io.FileOutputStream
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+@RunWith(AndroidJUnit4::class)
+class ImageLoaderTest : SysuiTestCase() {
+
+ private val testDispatcher = UnconfinedTestDispatcher()
+ private val testScope = TestScope(testDispatcher)
+ private val imageLoader = ImageLoader(context, testDispatcher)
+
+ private lateinit var imgFile: File
+
+ @Before
+ fun setUp() {
+ val context = context.createPackageContext("com.android.systemui.tests", 0)
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ com.android.systemui.tests.R.drawable.romainguy_rockaway
+ )
+
+ imgFile = File.createTempFile("image", ".png", context.cacheDir)
+ imgFile.deleteOnExit()
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, FileOutputStream(imgFile))
+ }
+
+ @After
+ fun tearDown() {
+ imgFile.delete()
+ }
+
+ @Test
+ fun invalidResource_drawable_returnsNull() =
+ testScope.runTest { assertThat(imageLoader.loadDrawable(ImageLoader.Res(-1))).isNull() }
+
+ @Test
+ fun invalidResource_bitmap_returnsNull() =
+ testScope.runTest { assertThat(imageLoader.loadBitmap(ImageLoader.Res(-1))).isNull() }
+
+ @Test
+ fun invalidUri_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadBitmap(ImageLoader.Uri("this.is/bogus"))).isNull()
+ }
+
+ @Test
+ fun invalidFile_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadBitmap(ImageLoader.File("this is broken!"))).isNull()
+ }
+
+ @Test
+ fun invalidIcon_returnsNull() =
+ testScope.runTest {
+ assertThat(imageLoader.loadDrawable(Icon.createWithFilePath("this is broken"))).isNull()
+ }
+
+ @Test
+ fun invalidIS_returnsNull() =
+ testScope.runTest {
+ assertThat(
+ imageLoader.loadDrawable(
+ ImageLoader.InputStream(ByteArrayInputStream(ByteArray(0)))
+ )
+ )
+ .isNull()
+ }
+
+ @Test
+ fun validBitmapResource_loadDrawable_returnsBitmapDrawable() =
+ testScope.runTest {
+ val context = context.createPackageContext("com.android.systemui.tests", 0)
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ com.android.systemui.tests.R.drawable.romainguy_rockaway
+ )
+ assertThat(bitmap).isNotNull()
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ ImageLoader.Res(
+ com.android.systemui.tests.R.drawable.romainguy_rockaway,
+ context
+ )
+ )
+ assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+ }
+
+ @Test
+ fun validBitmapResource_loadBitmap_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val loadedBitmap =
+ imageLoader.loadBitmap(ImageLoader.Res(R.drawable.dessert_zombiegingerbread))
+ assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+ }
+
+ @Test
+ fun validBitmapUri_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+
+ val uri =
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Uri(uri))
+ assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+ }
+
+ @Test
+ fun validBitmapFile_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+ val loadedBitmap = imageLoader.loadBitmap(ImageLoader.File(imgFile))
+ assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+ }
+
+ @Test
+ fun validInputStream_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap = BitmapFactory.decodeFile(imgFile.absolutePath)
+ val loadedBitmap =
+ imageLoader.loadBitmap(ImageLoader.InputStream(FileInputStream(imgFile)))
+ assertBitmapEqualToBitmap(loadedBitmap, bitmap)
+ }
+
+ @Test
+ fun validBitmapIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val loadedDrawable = imageLoader.loadDrawable(Icon.createWithBitmap(bitmap))
+ assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+ }
+
+ @Test
+ fun validUriIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val uri =
+ "android.resource://${context.packageName}/${R.drawable.dessert_zombiegingerbread}"
+ val loadedDrawable = imageLoader.loadDrawable(Icon.createWithContentUri(Uri.parse(uri)))
+ assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+ }
+
+ @Test
+ fun validDataIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ BitmapFactory.decodeResource(
+ context.resources,
+ R.drawable.dessert_zombiegingerbread
+ )
+ val bos =
+ ByteArrayOutputStream(
+ bitmap.byteCount * 2
+ ) // Compressed bitmap should be smaller than its source.
+ bitmap.compress(Bitmap.CompressFormat.PNG, 100, bos)
+
+ val array = bos.toByteArray()
+ val loadedDrawable = imageLoader.loadDrawable(Icon.createWithData(array, 0, array.size))
+ assertBitmapEqualToDrawable(loadedDrawable, bitmap)
+ }
+
+ @Test
+ fun validSystemResourceIcon_returnsBitmapDrawable() =
+ testScope.runTest {
+ val bitmap =
+ Resources.getSystem().getDrawable(android.R.drawable.ic_dialog_alert, context.theme)
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ Icon.createWithResource("android", android.R.drawable.ic_dialog_alert)
+ )
+ assertBitmapEqualToDrawable(loadedDrawable, (bitmap as BitmapDrawable).bitmap)
+ }
+
+ @Test
+ fun invalidDifferentPackageResourceIcon_returnsNull() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ Icon.createWithResource(
+ "noooope.wrong.package",
+ R.drawable.dessert_zombiegingerbread
+ )
+ )
+ assertThat(loadedDrawable).isNull()
+ }
+
+ @Test
+ fun validBitmapResource_widthMoreRestricted_downsizesKeepingAspectRatio() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 160)
+ val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+ assertThat(loadedBitmap.width).isEqualTo(160)
+ assertThat(loadedBitmap.height).isEqualTo(106)
+ }
+
+ @Test
+ fun validBitmapResource_heightMoreRestricted_downsizesKeepingAspectRatio() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(ImageLoader.File(imgFile), maxWidth = 160, maxHeight = 50)
+ val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+ assertThat(loadedBitmap.width).isEqualTo(74)
+ assertThat(loadedBitmap.height).isEqualTo(50)
+ }
+
+ @Test
+ fun validBitmapResource_onlyWidthRestricted_downsizesKeepingAspectRatio() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ ImageLoader.File(imgFile),
+ maxWidth = 160,
+ maxHeight = ImageLoader.DO_NOT_RESIZE
+ )
+ val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+ assertThat(loadedBitmap.width).isEqualTo(160)
+ assertThat(loadedBitmap.height).isEqualTo(106)
+ }
+
+ @Test
+ fun validBitmapResource_onlyHeightRestricted_downsizesKeepingAspectRatio() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ ImageLoader.Res(R.drawable.bubble_thumbnail),
+ maxWidth = ImageLoader.DO_NOT_RESIZE,
+ maxHeight = 120
+ )
+ val loadedBitmap = assertBitmapInDrawable(loadedDrawable)
+ assertThat(loadedBitmap.width).isEqualTo(123)
+ assertThat(loadedBitmap.height).isEqualTo(120)
+ }
+
+ @Test
+ fun validVectorDrawable_loadDrawable_successfullyLoaded() =
+ testScope.runTest {
+ val loadedDrawable = imageLoader.loadDrawable(ImageLoader.Res(R.drawable.ic_settings))
+ assertThat(loadedDrawable).isNotNull()
+ assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+ }
+
+ @Test
+ fun validVectorDrawable_loadBitmap_returnsNull() =
+ testScope.runTest {
+ val loadedBitmap = imageLoader.loadBitmap(ImageLoader.Res(R.drawable.ic_settings))
+ assertThat(loadedBitmap).isNull()
+ }
+
+ @Test
+ fun validVectorDrawableIcon_loadDrawable_successfullyLoaded() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(Icon.createWithResource(context, R.drawable.ic_settings))
+ assertThat(loadedDrawable).isNotNull()
+ assertThat(loadedDrawable).isInstanceOf(VectorDrawable::class.java)
+ }
+
+ @Test
+ fun hardwareAllocator_returnsHardwareBitmap() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ ImageLoader.File(imgFile),
+ allocator = ImageDecoder.ALLOCATOR_HARDWARE
+ )
+ assertThat(loadedDrawable).isNotNull()
+ assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+ .isEqualTo(Bitmap.Config.HARDWARE)
+ }
+
+ @Test
+ fun softwareAllocator_returnsSoftwareBitmap() =
+ testScope.runTest {
+ val loadedDrawable =
+ imageLoader.loadDrawable(
+ ImageLoader.File(imgFile),
+ allocator = ImageDecoder.ALLOCATOR_SOFTWARE
+ )
+ assertThat(loadedDrawable).isNotNull()
+ assertThat((loadedDrawable as BitmapDrawable).bitmap.config)
+ .isNotEqualTo(Bitmap.Config.HARDWARE)
+ }
+
+ private fun assertBitmapInDrawable(drawable: Drawable?): Bitmap {
+ assertThat(drawable).isNotNull()
+ assertThat(drawable).isInstanceOf(BitmapDrawable::class.java)
+ return (drawable as BitmapDrawable).bitmap
+ }
+
+ private fun assertBitmapEqualToDrawable(actual: Drawable?, expected: Bitmap) {
+ val actualBitmap = assertBitmapInDrawable(actual)
+ assertBitmapEqualToBitmap(actualBitmap, expected)
+ }
+
+ private fun assertBitmapEqualToBitmap(actual: Bitmap?, expected: Bitmap) {
+ assertThat(actual).isNotNull()
+ assertThat(actual?.width).isEqualTo(expected.width)
+ assertThat(actual?.height).isEqualTo(expected.height)
+ }
+}
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 fb7d379..5d83f56 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
@@ -157,11 +157,34 @@
assertThat(strongBiometricAllowed()).isFalse()
}
+ @Test
+ fun convenienceBiometricAllowedChange() =
+ testScope.runTest {
+ createBiometricSettingsRepository()
+ val convenienceBiometricAllowed =
+ collectLastValue(underTest.isNonStrongBiometricAllowed)
+ runCurrent()
+
+ onNonStrongAuthChanged(true, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isTrue()
+
+ onNonStrongAuthChanged(false, ANOTHER_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isTrue()
+
+ onNonStrongAuthChanged(false, PRIMARY_USER_ID)
+ assertThat(convenienceBiometricAllowed()).isFalse()
+ }
+
private fun onStrongAuthChanged(flags: Int, userId: Int) {
strongAuthTracker.value.stub.onStrongAuthRequiredChanged(flags, userId)
testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
}
+ private fun onNonStrongAuthChanged(allowed: Boolean, userId: Int) {
+ strongAuthTracker.value.stub.onIsNonStrongBiometricAllowedChanged(allowed, userId)
+ testableLooper?.processAllMessages() // StrongAuthTracker uses the TestableLooper
+ }
+
@Test
fun fingerprintDisabledByDpmChange() =
testScope.runTest {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
new file mode 100644
index 0000000..6e002f5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -0,0 +1,920 @@
+/*
+ * 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 android.app.StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP
+import android.app.StatusBarManager.SESSION_KEYGUARD
+import android.content.pm.UserInfo
+import android.content.pm.UserInfo.FLAG_PRIMARY
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
+import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
+import android.hardware.biometrics.ComponentInfoInternal
+import android.hardware.face.FaceAuthenticateOptions
+import android.hardware.face.FaceManager
+import android.hardware.face.FaceSensorProperties
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.os.CancellationSignal
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.InstanceId.fakeInstanceId
+import com.android.internal.logging.UiEventLogger
+import com.android.keyguard.FaceAuthUiEvent
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
+import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.FlowValue
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.dump.logcatLogBuffer
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.shared.model.AuthenticationStatus
+import com.android.systemui.keyguard.shared.model.DetectionStatus
+import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
+import com.android.systemui.log.FaceAuthenticationLogger
+import com.android.systemui.log.SessionTracker
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.FakeKeyguardStateController
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.mockito.KotlinArgumentCaptor
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.SystemClock
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import java.io.StringWriter
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.advanceTimeBy
+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.junit.runners.JUnit4
+import org.mockito.ArgumentCaptor
+import org.mockito.ArgumentMatchers.any
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Captor
+import org.mockito.Mock
+import org.mockito.Mockito.clearInvocations
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.MockitoAnnotations
+
+@OptIn(ExperimentalCoroutinesApi::class)
+@SmallTest
+@RunWith(JUnit4::class)
+class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
+ private lateinit var underTest: DeviceEntryFaceAuthRepositoryImpl
+
+ @Mock private lateinit var faceManager: FaceManager
+ @Mock private lateinit var bypassController: KeyguardBypassController
+ @Mock private lateinit var sessionTracker: SessionTracker
+ @Mock private lateinit var uiEventLogger: UiEventLogger
+ @Mock private lateinit var dumpManager: DumpManager
+
+ @Captor
+ private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
+
+ @Captor
+ private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
+ @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
+
+ private lateinit var bypassStateChangedListener:
+ KotlinArgumentCaptor<KeyguardBypassController.OnBypassStateChangedListener>
+
+ @Captor
+ private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
+ private lateinit var testDispatcher: TestDispatcher
+
+ private lateinit var testScope: TestScope
+ private lateinit var fakeUserRepository: FakeUserRepository
+ private lateinit var authStatus: FlowValue<AuthenticationStatus?>
+ private lateinit var detectStatus: FlowValue<DetectionStatus?>
+ private lateinit var authRunning: FlowValue<Boolean?>
+ private lateinit var lockedOut: FlowValue<Boolean?>
+ private lateinit var canFaceAuthRun: FlowValue<Boolean?>
+ private lateinit var authenticated: FlowValue<Boolean?>
+ private lateinit var biometricSettingsRepository: FakeBiometricSettingsRepository
+ private lateinit var deviceEntryFingerprintAuthRepository:
+ FakeDeviceEntryFingerprintAuthRepository
+ private lateinit var trustRepository: FakeTrustRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var keyguardInteractor: KeyguardInteractor
+ private lateinit var alternateBouncerInteractor: AlternateBouncerInteractor
+ private lateinit var bouncerRepository: FakeKeyguardBouncerRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+
+ private var wasAuthCancelled = false
+ private var wasDetectCancelled = false
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ fakeUserRepository = FakeUserRepository()
+ fakeUserRepository.setUserInfos(listOf(primaryUser, secondaryUser))
+ testDispatcher = StandardTestDispatcher()
+ biometricSettingsRepository = FakeBiometricSettingsRepository()
+ deviceEntryFingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository()
+ trustRepository = FakeTrustRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
+ fakeCommandQueue = FakeCommandQueue()
+ keyguardInteractor =
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository
+ )
+ alternateBouncerInteractor =
+ AlternateBouncerInteractor(
+ bouncerRepository = bouncerRepository,
+ biometricSettingsRepository = biometricSettingsRepository,
+ deviceEntryFingerprintAuthRepository = deviceEntryFingerprintAuthRepository,
+ systemClock = mock(SystemClock::class.java),
+ keyguardStateController = FakeKeyguardStateController(),
+ statusBarStateController = mock(StatusBarStateController::class.java),
+ )
+
+ bypassStateChangedListener =
+ KotlinArgumentCaptor(KeyguardBypassController.OnBypassStateChangedListener::class.java)
+ testScope = TestScope(testDispatcher)
+ whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+ whenever(bypassController.bypassEnabled).thenReturn(true)
+ underTest = createDeviceEntryFaceAuthRepositoryImpl(faceManager, bypassController)
+ }
+
+ private fun createDeviceEntryFaceAuthRepositoryImpl(
+ fmOverride: FaceManager? = faceManager,
+ bypassControllerOverride: KeyguardBypassController? = bypassController
+ ) =
+ DeviceEntryFaceAuthRepositoryImpl(
+ mContext,
+ fmOverride,
+ fakeUserRepository,
+ bypassControllerOverride,
+ testScope.backgroundScope,
+ testDispatcher,
+ sessionTracker,
+ uiEventLogger,
+ FaceAuthenticationLogger(logcatLogBuffer("DeviceEntryFaceAuthRepositoryLog")),
+ biometricSettingsRepository,
+ deviceEntryFingerprintAuthRepository,
+ trustRepository,
+ keyguardRepository,
+ keyguardInteractor,
+ alternateBouncerInteractor,
+ dumpManager,
+ )
+
+ @Test
+ fun faceAuthRunsAndProvidesAuthStatusUpdates() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+ uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+ assertThat(authRunning()).isTrue()
+
+ val successResult = successResult()
+ authenticationCallback.value.onAuthenticationSucceeded(successResult)
+
+ assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
+ assertThat(authenticated()).isTrue()
+ assertThat(authRunning()).isFalse()
+ }
+
+ private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
+ verify(uiEventLogger)
+ .logWithInstanceIdAndPosition(
+ faceAuthUiEvent,
+ 0,
+ null,
+ keyguardSessionId,
+ faceAuthUiEvent.extraInfo
+ )
+ }
+
+ @Test
+ fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
+ testScope.runTest {
+ initCollectors()
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+ clearInvocations(faceManager)
+ clearInvocations(uiEventLogger)
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ verifyNoMoreInteractions(faceManager)
+ verifyNoMoreInteractions(uiEventLogger)
+ }
+
+ @Test
+ fun faceLockoutStatusIsPropagated() =
+ testScope.runTest {
+ initCollectors()
+ verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+
+ authenticationCallback.value.onAuthenticationError(
+ FACE_ERROR_LOCKOUT_PERMANENT,
+ "face locked out"
+ )
+
+ assertThat(lockedOut()).isTrue()
+
+ faceLockoutResetCallback.value.onLockoutReset(0)
+ assertThat(lockedOut()).isFalse()
+ }
+
+ @Test
+ fun faceDetectionSupportIsTheCorrectValue() =
+ testScope.runTest {
+ assertThat(
+ createDeviceEntryFaceAuthRepositoryImpl(fmOverride = null).isDetectionSupported
+ )
+ .isFalse()
+
+ whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
+ assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+ whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
+ assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+ assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(
+ listOf(
+ createFaceSensorProperties(supportsFaceDetection = false),
+ createFaceSensorProperties(supportsFaceDetection = true)
+ )
+ )
+ assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isFalse()
+
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(
+ listOf(
+ createFaceSensorProperties(supportsFaceDetection = true),
+ createFaceSensorProperties(supportsFaceDetection = false)
+ )
+ )
+ assertThat(createDeviceEntryFaceAuthRepositoryImpl().isDetectionSupported).isTrue()
+ }
+
+ @Test
+ fun cancelStopsFaceAuthentication() =
+ testScope.runTest {
+ initCollectors()
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+
+ var wasAuthCancelled = false
+ cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+
+ underTest.cancel()
+ assertThat(wasAuthCancelled).isTrue()
+ assertThat(authRunning()).isFalse()
+ }
+
+ @Test
+ fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
+
+ @Test
+ fun faceDetectionRunsAndPropagatesDetectionStatus() =
+ testScope.runTest {
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+
+ underTest.detect()
+ faceDetectIsCalled()
+
+ detectionCallback.value.onFaceDetected(1, 1, true)
+
+ assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
+ }
+
+ @Test
+ fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
+ testScope.runTest {
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+ clearInvocations(faceManager)
+
+ underTest.detect()
+
+ verify(faceManager, never())
+ .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+ }
+
+ @Test
+ fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
+ testScope.runTest {
+ initCollectors()
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+
+ // Enter cancelling state
+ underTest.cancel()
+ clearInvocations(faceManager)
+
+ // Auth is while cancelling.
+ underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ // Auth is not started
+ verifyNoMoreInteractions(faceManager)
+
+ // Auth is done cancelling.
+ authenticationCallback.value.onAuthenticationError(
+ FACE_ERROR_CANCELED,
+ "First auth attempt cancellation completed"
+ )
+ assertThat(authStatus())
+ .isEqualTo(
+ ErrorAuthenticationStatus(
+ FACE_ERROR_CANCELED,
+ "First auth attempt cancellation completed"
+ )
+ )
+
+ faceAuthenticateIsCalled()
+ uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
+ }
+
+ @Test
+ fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+
+ clearInvocations(faceManager)
+ underTest.cancel()
+ advanceTimeBy(DeviceEntryFaceAuthRepositoryImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+ }
+
+ @Test
+ fun faceHelpMessagesAreIgnoredBasedOnConfig() =
+ testScope.runTest {
+ overrideResource(
+ R.array.config_face_acquire_device_entry_ignorelist,
+ intArrayOf(10, 11)
+ )
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+ faceAuthenticateIsCalled()
+
+ authenticationCallback.value.onAuthenticationHelp(9, "help msg")
+ authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
+ authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
+
+ assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
+ }
+
+ @Test
+ fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
+ testScope.runTest {
+ fakeUserRepository.setSelectedUserInfo(primaryUser)
+ underTest.dump(PrintWriter(StringWriter()), emptyArray())
+
+ underTest =
+ createDeviceEntryFaceAuthRepositoryImpl(
+ fmOverride = null,
+ bypassControllerOverride = null
+ )
+ fakeUserRepository.setSelectedUserInfo(primaryUser)
+
+ underTest.dump(PrintWriter(StringWriter()), emptyArray())
+ }
+
+ @Test
+ fun authenticateDoesNotRunIfFaceIsNotEnrolled() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { biometricSettingsRepository.setFaceEnrolled(false) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunIfFaceIsNotEnabled() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunIfUserIsInLockdown() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { biometricSettingsRepository.setIsUserInLockdown(true) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunIfUserIsCurrentlySwitching() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { fakeUserRepository.setUserSwitching(true) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenFpIsLockedOut() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { deviceEntryFingerprintAuthRepository.setLockedOut(true) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenUserIsCurrentlyTrusted() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { trustRepository.setCurrentUserTrusted(true) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenKeyguardIsGoingAway() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth { keyguardRepository.setKeyguardGoingAway(true) }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenDeviceIsGoingToSleep() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ state = WakefulnessState.STARTING_TO_SLEEP,
+ isWakingUpOrAwake = false,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ )
+ }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenDeviceIsSleeping() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ state = WakefulnessState.ASLEEP,
+ isWakingUpOrAwake = false,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ )
+ }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenNonStrongBiometricIsNotAllowed() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenCurrentUserIsNotPrimary() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ launch { fakeUserRepository.setSelectedUserInfo(secondaryUser) }
+ }
+ }
+
+ @Test
+ fun authenticateDoesNotRunWhenSecureCameraIsActive() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ bouncerRepository.setAlternateVisible(false)
+ fakeCommandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+ }
+ }
+ }
+
+ @Test
+ fun authenticateDoesNotRunOnUnsupportedPosture() =
+ testScope.runTest {
+ testGatingCheckForFaceAuth {
+ biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+ }
+ }
+
+ @Test
+ fun authenticateFallbacksToDetectionWhenItCannotRun() =
+ testScope.runTest {
+ whenever(faceManager.sensorPropertiesInternal)
+ .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
+ whenever(bypassController.bypassEnabled).thenReturn(true)
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ // Flip one precondition to false.
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ assertThat(canFaceAuthRun()).isFalse()
+ underTest.authenticate(
+ FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
+ fallbackToDetection = true
+ )
+ faceAuthenticateIsNotCalled()
+
+ faceDetectIsCalled()
+ }
+
+ @Test
+ fun everythingWorksWithFaceAuthRefactorFlagDisabled() =
+ testScope.runTest {
+ featureFlags.set(FACE_AUTH_REFACTOR, false)
+
+ underTest = createDeviceEntryFaceAuthRepositoryImpl()
+ initCollectors()
+
+ // Collecting any flows exposed in the public API doesn't throw any error
+ authStatus()
+ detectStatus()
+ authRunning()
+ lockedOut()
+ canFaceAuthRun()
+ authenticated()
+ }
+
+ @Test
+ fun isAuthenticatedIsFalseWhenFaceAuthFails() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationFailed()
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticatedIsFalseWhenFaceAuthErrorsOut() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationError(-1, "some error")
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticatedIsResetToFalseWhenKeyguardIsGoingAway() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationSucceeded(
+ mock(FaceManager.AuthenticationResult::class.java)
+ )
+
+ assertThat(authenticated()).isTrue()
+
+ keyguardRepository.setKeyguardGoingAway(true)
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
+ fun isAuthenticatedIsResetToFalseWhenUserIsSwitching() =
+ testScope.runTest {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ authenticationCallback.value.onAuthenticationSucceeded(
+ mock(FaceManager.AuthenticationResult::class.java)
+ )
+
+ assertThat(authenticated()).isTrue()
+
+ fakeUserRepository.setUserSwitching(true)
+
+ assertThat(authenticated()).isFalse()
+ }
+
+ @Test
+ fun detectDoesNotRunWhenFaceIsNotEnrolled() =
+ testScope.runTest {
+ testGatingCheckForDetect { biometricSettingsRepository.setFaceEnrolled(false) }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenFaceIsNotEnabled() =
+ testScope.runTest {
+ testGatingCheckForDetect { biometricSettingsRepository.setIsFaceAuthEnabled(false) }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenUserSwitchingInProgress() =
+ testScope.runTest { testGatingCheckForDetect { fakeUserRepository.setUserSwitching(true) } }
+
+ @Test
+ fun detectDoesNotRunWhenKeyguardGoingAway() =
+ testScope.runTest {
+ testGatingCheckForDetect { keyguardRepository.setKeyguardGoingAway(true) }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenDeviceSleepingStartingToSleep() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ state = WakefulnessState.STARTING_TO_SLEEP,
+ isWakingUpOrAwake = false,
+ lastWakeReason = WakeSleepReason.OTHER,
+ lastSleepReason = WakeSleepReason.OTHER,
+ )
+ )
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenSecureCameraIsActive() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ bouncerRepository.setAlternateVisible(false)
+ fakeCommandQueue.doForEachCallback {
+ it.onCameraLaunchGestureDetected(CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP)
+ }
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenFaceAuthNotSupportedInCurrentPosture() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(false)
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenCurrentUserInLockdown() =
+ testScope.runTest {
+ testGatingCheckForDetect { biometricSettingsRepository.setIsUserInLockdown(true) }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenBypassIsNotEnabled() =
+ testScope.runTest {
+ runCurrent()
+ verify(bypassController)
+ .registerOnBypassStateChangedListener(bypassStateChangedListener.capture())
+
+ testGatingCheckForDetect {
+ bypassStateChangedListener.value.onBypassStateChanged(false)
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunWhenNonStrongBiometricIsAllowed() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+ }
+ }
+
+ @Test
+ fun detectDoesNotRunIfUdfpsIsRunning() =
+ testScope.runTest {
+ testGatingCheckForDetect {
+ deviceEntryFingerprintAuthRepository.setAvailableFpSensorType(
+ BiometricType.UNDER_DISPLAY_FINGERPRINT
+ )
+ deviceEntryFingerprintAuthRepository.setIsRunning(true)
+ }
+ }
+
+ private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ gatingCheckModifier()
+ runCurrent()
+
+ // gating check doesn't allow face auth to run.
+ assertThat(underTest.canRunFaceAuth.value).isFalse()
+
+ // flip the gating check back on.
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ triggerFaceAuth(false)
+
+ // Flip gating check off
+ gatingCheckModifier()
+ runCurrent()
+
+ // Stops currently running auth
+ assertThat(wasAuthCancelled).isTrue()
+ clearInvocations(faceManager)
+
+ // Try auth again
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
+
+ // Auth can't run again
+ faceAuthenticateIsNotCalled()
+ }
+
+ private suspend fun TestScope.testGatingCheckForDetect(gatingCheckModifier: () -> Unit) {
+ initCollectors()
+ allPreconditionsToRunFaceAuthAreTrue()
+
+ // This will stop face auth from running but is required to be false for detect.
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(false)
+ runCurrent()
+
+ assertThat(canFaceAuthRun()).isFalse()
+
+ // Trigger authenticate with detection fallback
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+ faceAuthenticateIsNotCalled()
+ faceDetectIsCalled()
+ cancellationSignal.value.setOnCancelListener { wasDetectCancelled = true }
+
+ // Flip gating check
+ gatingCheckModifier()
+ runCurrent()
+
+ // Stops currently running detect
+ assertThat(wasDetectCancelled).isTrue()
+ clearInvocations(faceManager)
+
+ // Try to run detect again
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetection = true)
+
+ // Detect won't run because preconditions are not true anymore.
+ faceDetectIsNotCalled()
+ }
+
+ private suspend fun triggerFaceAuth(fallbackToDetect: Boolean) {
+ assertThat(canFaceAuthRun()).isTrue()
+ underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER, fallbackToDetect)
+ faceAuthenticateIsCalled()
+ assertThat(authRunning()).isTrue()
+ cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
+ }
+
+ private suspend fun TestScope.allPreconditionsToRunFaceAuthAreTrue() {
+ biometricSettingsRepository.setFaceEnrolled(true)
+ biometricSettingsRepository.setIsFaceAuthEnabled(true)
+ fakeUserRepository.setUserSwitching(false)
+ deviceEntryFingerprintAuthRepository.setLockedOut(false)
+ trustRepository.setCurrentUserTrusted(false)
+ keyguardRepository.setKeyguardGoingAway(false)
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.STARTING_TO_WAKE,
+ true,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER
+ )
+ )
+ biometricSettingsRepository.setIsNonStrongBiometricAllowed(true)
+ biometricSettingsRepository.setIsUserInLockdown(false)
+ fakeUserRepository.setSelectedUserInfo(primaryUser)
+ biometricSettingsRepository.setIsFaceAuthSupportedInCurrentPosture(true)
+ bouncerRepository.setAlternateVisible(true)
+ runCurrent()
+ }
+
+ private suspend fun TestScope.initCollectors() {
+ authStatus = collectLastValue(underTest.authenticationStatus)
+ detectStatus = collectLastValue(underTest.detectionStatus)
+ authRunning = collectLastValue(underTest.isAuthRunning)
+ lockedOut = collectLastValue(underTest.isLockedOut)
+ canFaceAuthRun = collectLastValue(underTest.canRunFaceAuth)
+ authenticated = collectLastValue(underTest.isAuthenticated)
+ fakeUserRepository.setSelectedUserInfo(primaryUser)
+ }
+
+ private fun successResult() = FaceManager.AuthenticationResult(null, null, primaryUserId, false)
+
+ private fun faceDetectIsCalled() {
+ verify(faceManager)
+ .detectFace(
+ cancellationSignal.capture(),
+ detectionCallback.capture(),
+ eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+ )
+ }
+
+ private fun faceAuthenticateIsCalled() {
+ verify(faceManager)
+ .authenticate(
+ isNull(),
+ cancellationSignal.capture(),
+ authenticationCallback.capture(),
+ isNull(),
+ eq(FaceAuthenticateOptions.Builder().setUserId(primaryUserId).build())
+ )
+ }
+
+ private fun faceAuthenticateIsNotCalled() {
+ verify(faceManager, never())
+ .authenticate(
+ isNull(),
+ any(),
+ any(),
+ isNull(),
+ any(FaceAuthenticateOptions::class.java)
+ )
+ }
+
+ private fun faceDetectIsNotCalled() {
+ verify(faceManager, never())
+ .detectFace(any(), any(), any(FaceAuthenticateOptions::class.java))
+ }
+
+ private fun createFaceSensorProperties(
+ supportsFaceDetection: Boolean
+ ): FaceSensorPropertiesInternal {
+ val componentInfo =
+ listOf(
+ ComponentInfoInternal(
+ "faceSensor" /* componentId */,
+ "vendor/model/revision" /* hardwareVersion */,
+ "1.01" /* firmwareVersion */,
+ "00000001" /* serialNumber */,
+ "" /* softwareVersion */
+ )
+ )
+ return FaceSensorPropertiesInternal(
+ 0 /* id */,
+ FaceSensorProperties.STRENGTH_STRONG,
+ 1 /* maxTemplatesAllowed */,
+ componentInfo,
+ FaceSensorProperties.TYPE_UNKNOWN,
+ supportsFaceDetection /* supportsFaceDetection */,
+ true /* supportsSelfIllumination */,
+ false /* resetLockoutRequiresChallenge */
+ )
+ }
+
+ companion object {
+ const val primaryUserId = 1
+ val keyguardSessionId = fakeInstanceId(10)!!
+ val primaryUser = UserInfo(primaryUserId, "test user", FLAG_PRIMARY)
+
+ val secondaryUser = UserInfo(2, "secondary user", 0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
index 70f766f..e57b044 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFingerprintAuthRepositoryTest.kt
@@ -16,6 +16,7 @@
package com.android.systemui.keyguard.data.repository
+import android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT
import android.hardware.biometrics.BiometricSourceType
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
@@ -30,7 +31,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -70,11 +70,6 @@
)
}
- @After
- fun tearDown() {
- // verify(keyguardUpdateMonitor).removeCallback(updateMonitorCallback.value)
- }
-
@Test
fun isLockedOut_whenFingerprintLockoutStateChanges_emitsNewValue() =
testScope.runTest {
@@ -129,29 +124,55 @@
}
@Test
- fun enabledFingerprintTypeProvidesTheCorrectOutput() =
+ fun enabledFingerprintTypeProvidesTheCorrectOutputForSpfs() =
testScope.runTest {
whenever(authController.isSfpsSupported).thenReturn(true)
whenever(authController.isUdfpsSupported).thenReturn(false)
whenever(authController.isRearFpsSupported).thenReturn(false)
- assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+ val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+ assertThat(availableFpSensorType()).isEqualTo(BiometricType.SIDE_FINGERPRINT)
+ }
+ @Test
+ fun enabledFingerprintTypeProvidesTheCorrectOutputForUdfps() =
+ testScope.runTest {
whenever(authController.isSfpsSupported).thenReturn(false)
whenever(authController.isUdfpsSupported).thenReturn(true)
whenever(authController.isRearFpsSupported).thenReturn(false)
+ val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+ assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
+ }
- assertThat(underTest.availableFpSensorType)
- .isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
-
+ @Test
+ fun enabledFingerprintTypeProvidesTheCorrectOutputForRearFps() =
+ testScope.runTest {
whenever(authController.isSfpsSupported).thenReturn(false)
whenever(authController.isUdfpsSupported).thenReturn(false)
whenever(authController.isRearFpsSupported).thenReturn(true)
- assertThat(underTest.availableFpSensorType).isEqualTo(BiometricType.REAR_FINGERPRINT)
+ val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+ assertThat(availableFpSensorType()).isEqualTo(BiometricType.REAR_FINGERPRINT)
+ }
+
+ @Test
+ fun enabledFingerprintTypeProvidesTheCorrectOutputAfterAllAuthenticatorsAreRegistered() =
+ testScope.runTest {
+ whenever(authController.isSfpsSupported).thenReturn(false)
+ whenever(authController.isUdfpsSupported).thenReturn(false)
whenever(authController.isRearFpsSupported).thenReturn(false)
+ whenever(authController.areAllFingerprintAuthenticatorsRegistered()).thenReturn(false)
- assertThat(underTest.availableFpSensorType).isNull()
+ val availableFpSensorType = collectLastValue(underTest.availableFpSensorType)
+ runCurrent()
+
+ val callback = ArgumentCaptor.forClass(AuthController.Callback::class.java)
+ verify(authController).addCallback(callback.capture())
+ assertThat(availableFpSensorType()).isNull()
+
+ whenever(authController.isUdfpsSupported).thenReturn(true)
+ callback.value.onAllAuthenticatorsRegistered(TYPE_FINGERPRINT)
+ assertThat(availableFpSensorType()).isEqualTo(BiometricType.UNDER_DISPLAY_FINGERPRINT)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
deleted file mode 100644
index d55370b..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardFaceAuthManagerTest.kt
+++ /dev/null
@@ -1,427 +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.systemui.keyguard.data.repository
-
-import android.app.StatusBarManager.SESSION_KEYGUARD
-import android.content.pm.UserInfo
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_CANCELED
-import android.hardware.biometrics.BiometricFaceConstants.FACE_ERROR_LOCKOUT_PERMANENT
-import android.hardware.biometrics.ComponentInfoInternal
-import android.hardware.face.FaceAuthenticateOptions
-import android.hardware.face.FaceManager
-import android.hardware.face.FaceSensorProperties
-import android.hardware.face.FaceSensorPropertiesInternal
-import android.os.CancellationSignal
-import androidx.test.filters.SmallTest
-import com.android.internal.logging.InstanceId.fakeInstanceId
-import com.android.internal.logging.UiEventLogger
-import com.android.keyguard.FaceAuthUiEvent
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN
-import com.android.keyguard.FaceAuthUiEvent.FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER
-import com.android.systemui.R
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.coroutines.FlowValue
-import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.dump.logcatLogBuffer
-import com.android.systemui.keyguard.shared.model.AuthenticationStatus
-import com.android.systemui.keyguard.shared.model.DetectionStatus
-import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
-import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
-import com.android.systemui.log.FaceAuthenticationLogger
-import com.android.systemui.log.SessionTracker
-import com.android.systemui.statusbar.phone.KeyguardBypassController
-import com.android.systemui.user.data.repository.FakeUserRepository
-import com.android.systemui.util.mockito.whenever
-import com.google.common.truth.Truth.assertThat
-import java.io.PrintWriter
-import java.io.StringWriter
-import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.test.StandardTestDispatcher
-import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestScope
-import kotlinx.coroutines.test.advanceTimeBy
-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.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Captor
-import org.mockito.Mock
-import org.mockito.Mockito.clearInvocations
-import org.mockito.Mockito.isNull
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-import org.mockito.Mockito.verifyNoMoreInteractions
-import org.mockito.MockitoAnnotations
-
-@OptIn(ExperimentalCoroutinesApi::class)
-@SmallTest
-@RunWith(JUnit4::class)
-class KeyguardFaceAuthManagerTest : SysuiTestCase() {
- private lateinit var underTest: KeyguardFaceAuthManagerImpl
-
- @Mock private lateinit var faceManager: FaceManager
- @Mock private lateinit var bypassController: KeyguardBypassController
- @Mock private lateinit var sessionTracker: SessionTracker
- @Mock private lateinit var uiEventLogger: UiEventLogger
- @Mock private lateinit var dumpManager: DumpManager
-
- @Captor
- private lateinit var authenticationCallback: ArgumentCaptor<FaceManager.AuthenticationCallback>
- @Captor
- private lateinit var detectionCallback: ArgumentCaptor<FaceManager.FaceDetectionCallback>
- @Captor private lateinit var cancellationSignal: ArgumentCaptor<CancellationSignal>
- @Captor
- private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
- private lateinit var testDispatcher: TestDispatcher
-
- private lateinit var testScope: TestScope
- private lateinit var fakeUserRepository: FakeUserRepository
- private lateinit var authStatus: FlowValue<AuthenticationStatus?>
- private lateinit var detectStatus: FlowValue<DetectionStatus?>
- private lateinit var authRunning: FlowValue<Boolean?>
- private lateinit var lockedOut: FlowValue<Boolean?>
-
- @Before
- fun setup() {
- MockitoAnnotations.initMocks(this)
- fakeUserRepository = FakeUserRepository()
- fakeUserRepository.setUserInfos(listOf(currentUser))
- testDispatcher = StandardTestDispatcher()
- testScope = TestScope(testDispatcher)
- whenever(sessionTracker.getSessionId(SESSION_KEYGUARD)).thenReturn(keyguardSessionId)
- whenever(bypassController.bypassEnabled).thenReturn(true)
- underTest = createFaceAuthManagerImpl(faceManager)
- }
-
- private fun createFaceAuthManagerImpl(
- fmOverride: FaceManager? = faceManager,
- bypassControllerOverride: KeyguardBypassController? = bypassController
- ) =
- KeyguardFaceAuthManagerImpl(
- mContext,
- fmOverride,
- fakeUserRepository,
- bypassControllerOverride,
- testScope.backgroundScope,
- testDispatcher,
- sessionTracker,
- uiEventLogger,
- FaceAuthenticationLogger(logcatLogBuffer("KeyguardFaceAuthManagerLog")),
- dumpManager,
- )
-
- @Test
- fun faceAuthRunsAndProvidesAuthStatusUpdates() =
- testScope.runTest {
- testSetup(this)
-
- FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER.extraInfo = 10
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
- uiEventIsLogged(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
-
- assertThat(authRunning()).isTrue()
-
- val successResult = successResult()
- authenticationCallback.value.onAuthenticationSucceeded(successResult)
-
- assertThat(authStatus()).isEqualTo(SuccessAuthenticationStatus(successResult))
-
- assertThat(authRunning()).isFalse()
- }
-
- private fun uiEventIsLogged(faceAuthUiEvent: FaceAuthUiEvent) {
- verify(uiEventLogger)
- .logWithInstanceIdAndPosition(
- faceAuthUiEvent,
- 0,
- null,
- keyguardSessionId,
- faceAuthUiEvent.extraInfo
- )
- }
-
- @Test
- fun faceAuthDoesNotRunWhileItIsAlreadyRunning() =
- testScope.runTest {
- testSetup(this)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
- clearInvocations(faceManager)
- clearInvocations(uiEventLogger)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- verifyNoMoreInteractions(faceManager)
- verifyNoMoreInteractions(uiEventLogger)
- }
-
- @Test
- fun faceLockoutStatusIsPropagated() =
- testScope.runTest {
- testSetup(this)
- verify(faceManager).addLockoutResetCallback(faceLockoutResetCallback.capture())
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
-
- authenticationCallback.value.onAuthenticationError(
- FACE_ERROR_LOCKOUT_PERMANENT,
- "face locked out"
- )
-
- assertThat(lockedOut()).isTrue()
-
- faceLockoutResetCallback.value.onLockoutReset(0)
- assertThat(lockedOut()).isFalse()
- }
-
- @Test
- fun faceDetectionSupportIsTheCorrectValue() =
- testScope.runTest {
- assertThat(createFaceAuthManagerImpl(fmOverride = null).isDetectionSupported).isFalse()
-
- whenever(faceManager.sensorPropertiesInternal).thenReturn(null)
- assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
- whenever(faceManager.sensorPropertiesInternal).thenReturn(listOf())
- assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
- whenever(faceManager.sensorPropertiesInternal)
- .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
- assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
- whenever(faceManager.sensorPropertiesInternal)
- .thenReturn(
- listOf(
- createFaceSensorProperties(supportsFaceDetection = false),
- createFaceSensorProperties(supportsFaceDetection = true)
- )
- )
- assertThat(createFaceAuthManagerImpl().isDetectionSupported).isFalse()
-
- whenever(faceManager.sensorPropertiesInternal)
- .thenReturn(
- listOf(
- createFaceSensorProperties(supportsFaceDetection = true),
- createFaceSensorProperties(supportsFaceDetection = false)
- )
- )
- assertThat(createFaceAuthManagerImpl().isDetectionSupported).isTrue()
- }
-
- @Test
- fun cancelStopsFaceAuthentication() =
- testScope.runTest {
- testSetup(this)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
-
- var wasAuthCancelled = false
- cancellationSignal.value.setOnCancelListener { wasAuthCancelled = true }
-
- underTest.cancel()
- assertThat(wasAuthCancelled).isTrue()
- assertThat(authRunning()).isFalse()
- }
-
- @Test
- fun cancelInvokedWithoutFaceAuthRunningIsANoop() = testScope.runTest { underTest.cancel() }
-
- @Test
- fun faceDetectionRunsAndPropagatesDetectionStatus() =
- testScope.runTest {
- whenever(faceManager.sensorPropertiesInternal)
- .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
- underTest = createFaceAuthManagerImpl()
- testSetup(this)
-
- underTest.detect()
- faceDetectIsCalled()
-
- detectionCallback.value.onFaceDetected(1, 1, true)
-
- assertThat(detectStatus()).isEqualTo(DetectionStatus(1, 1, true))
- }
-
- @Test
- fun faceDetectDoesNotRunIfDetectionIsNotSupported() =
- testScope.runTest {
- whenever(faceManager.sensorPropertiesInternal)
- .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = false)))
- underTest = createFaceAuthManagerImpl()
- testSetup(this)
- clearInvocations(faceManager)
-
- underTest.detect()
-
- verify(faceManager, never()).detectFace(any(), any(), any())
- }
-
- @Test
- fun faceAuthShouldWaitAndRunIfTriggeredWhileCancelling() =
- testScope.runTest {
- testSetup(this)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
-
- // Enter cancelling state
- underTest.cancel()
- clearInvocations(faceManager)
-
- // Auth is while cancelling.
- underTest.authenticate(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
- // Auth is not started
- verifyNoMoreInteractions(faceManager)
-
- // Auth is done cancelling.
- authenticationCallback.value.onAuthenticationError(
- FACE_ERROR_CANCELED,
- "First auth attempt cancellation completed"
- )
- assertThat(authStatus())
- .isEqualTo(
- ErrorAuthenticationStatus(
- FACE_ERROR_CANCELED,
- "First auth attempt cancellation completed"
- )
- )
-
- faceAuthenticateIsCalled()
- uiEventIsLogged(FACE_AUTH_TRIGGERED_ALTERNATE_BIOMETRIC_BOUNCER_SHOWN)
- }
-
- @Test
- fun faceAuthAutoCancelsAfterDefaultCancellationTimeout() =
- testScope.runTest {
- testSetup(this)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
-
- clearInvocations(faceManager)
- underTest.cancel()
- advanceTimeBy(KeyguardFaceAuthManagerImpl.DEFAULT_CANCEL_SIGNAL_TIMEOUT + 1)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
- }
-
- @Test
- fun faceHelpMessagesAreIgnoredBasedOnConfig() =
- testScope.runTest {
- overrideResource(
- R.array.config_face_acquire_device_entry_ignorelist,
- intArrayOf(10, 11)
- )
- underTest = createFaceAuthManagerImpl()
- testSetup(this)
-
- underTest.authenticate(FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER)
- faceAuthenticateIsCalled()
-
- authenticationCallback.value.onAuthenticationHelp(9, "help msg")
- authenticationCallback.value.onAuthenticationHelp(10, "Ignored help msg")
- authenticationCallback.value.onAuthenticationHelp(11, "Ignored help msg")
-
- assertThat(authStatus()).isEqualTo(HelpAuthenticationStatus(9, "help msg"))
- }
-
- @Test
- fun dumpDoesNotErrorOutWhenFaceManagerOrBypassControllerIsNull() =
- testScope.runTest {
- fakeUserRepository.setSelectedUserInfo(currentUser)
- underTest.dump(PrintWriter(StringWriter()), emptyArray())
-
- underTest =
- createFaceAuthManagerImpl(fmOverride = null, bypassControllerOverride = null)
- fakeUserRepository.setSelectedUserInfo(currentUser)
-
- underTest.dump(PrintWriter(StringWriter()), emptyArray())
- }
-
- private suspend fun testSetup(testScope: TestScope) {
- with(testScope) {
- authStatus = collectLastValue(underTest.authenticationStatus)
- detectStatus = collectLastValue(underTest.detectionStatus)
- authRunning = collectLastValue(underTest.isAuthRunning)
- lockedOut = collectLastValue(underTest.isLockedOut)
- fakeUserRepository.setSelectedUserInfo(currentUser)
- }
- }
-
- private fun successResult() = FaceManager.AuthenticationResult(null, null, currentUserId, false)
-
- private fun faceDetectIsCalled() {
- verify(faceManager)
- .detectFace(
- cancellationSignal.capture(),
- detectionCallback.capture(),
- eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
- )
- }
-
- private fun faceAuthenticateIsCalled() {
- verify(faceManager)
- .authenticate(
- isNull(),
- cancellationSignal.capture(),
- authenticationCallback.capture(),
- isNull(),
- eq(FaceAuthenticateOptions.Builder().setUserId(currentUserId).build())
- )
- }
-
- private fun createFaceSensorProperties(
- supportsFaceDetection: Boolean
- ): FaceSensorPropertiesInternal {
- val componentInfo =
- listOf(
- ComponentInfoInternal(
- "faceSensor" /* componentId */,
- "vendor/model/revision" /* hardwareVersion */,
- "1.01" /* firmwareVersion */,
- "00000001" /* serialNumber */,
- "" /* softwareVersion */
- )
- )
- return FaceSensorPropertiesInternal(
- 0 /* id */,
- FaceSensorProperties.STRENGTH_STRONG,
- 1 /* maxTemplatesAllowed */,
- componentInfo,
- FaceSensorProperties.TYPE_UNKNOWN,
- supportsFaceDetection /* supportsFaceDetection */,
- true /* supportsSelfIllumination */,
- false /* resetLockoutRequiresChallenge */
- )
- }
-
- companion object {
- const val currentUserId = 1
- val keyguardSessionId = fakeInstanceId(10)!!
- val currentUser = UserInfo(currentUserId, "test user", 0)
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
index 7f30162..68d694a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractorTest.kt
@@ -18,18 +18,16 @@
package com.android.systemui.keyguard.domain.interactor
import android.app.StatusBarManager
-import android.content.Context
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
import com.android.systemui.keyguard.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel
-import com.android.systemui.settings.DisplayTracker
-import com.android.systemui.statusbar.CommandQueue
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.test.TestScope
@@ -38,7 +36,6 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
-import org.mockito.Mockito.mock
import org.mockito.MockitoAnnotations
@SmallTest
@@ -56,7 +53,7 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
featureFlags = FakeFeatureFlags().apply { set(FACE_AUTH_REFACTOR, true) }
- commandQueue = FakeCommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java))
+ commandQueue = FakeCommandQueue()
testScope = TestScope()
repository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
@@ -174,22 +171,3 @@
assertThat(secureCameraActive()).isFalse()
}
}
-
-class FakeCommandQueue(val context: Context, val displayTracker: DisplayTracker) :
- CommandQueue(context, displayTracker) {
- private val callbacks = mutableListOf<Callbacks>()
-
- override fun addCallback(callback: Callbacks) {
- callbacks.add(callback)
- }
-
- override fun removeCallback(callback: Callbacks) {
- callbacks.remove(callback)
- }
-
- fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
- callbacks.forEach { func(it) }
- }
-
- fun callbackCount(): Int = callbacks.size
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
index aa54a1c..447b333 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/SessionTrackerTest.java
@@ -26,6 +26,7 @@
import static org.junit.Assert.assertNotEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,6 +38,7 @@
import androidx.test.filters.SmallTest;
import com.android.internal.logging.InstanceId;
+import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
@@ -64,6 +66,8 @@
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private UiEventLogger mUiEventLogger;
@Captor
ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallbackCaptor;
@@ -87,7 +91,8 @@
mStatusBarService,
mAuthController,
mKeyguardUpdateMonitor,
- mKeyguardStateController
+ mKeyguardStateController,
+ mUiEventLogger
);
}
@@ -238,6 +243,62 @@
eq(SESSION_KEYGUARD), any(InstanceId.class));
}
+ @Test
+ public void uiEventLoggedOnEndSessionWhenDeviceStartsSleeping() throws RemoteException {
+ // GIVEN session tracker start
+ mSessionTracker.start();
+ captureKeyguardUpdateMonitorCallback();
+ captureKeyguardStateControllerCallback();
+
+ // GIVEN keyguard becomes visible (ie: from lockdown), so there's a valid keyguard
+ // session running
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ mKeyguardStateCallback.onKeyguardShowingChanged();
+
+ // WHEN device starts going to sleep
+ mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+ // THEN UI event is logged
+ verify(mUiEventLogger).log(
+ eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+ any(InstanceId.class));
+ }
+
+ @Test
+ public void noUiEventLoggedOnEndSessionWhenDeviceStartsSleepingWithoutStartSession()
+ throws RemoteException {
+ // GIVEN session tracker start without any valid sessions
+ mSessionTracker.start();
+ captureKeyguardUpdateMonitorCallback();
+
+ // WHEN device starts going to sleep when there was no started sessions
+ mKeyguardUpdateMonitorCallback.onStartedGoingToSleep(0);
+
+ // THEN UI event is never logged
+ verify(mUiEventLogger, never()).log(
+ eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_GOING_TO_SLEEP),
+ any(InstanceId.class));
+ }
+
+ @Test
+ public void uiEventLoggedOnEndSessionWhenKeyguardGoingAway() throws RemoteException {
+ // GIVEN session tracker started w/o any sessions
+ mSessionTracker.start();
+ captureKeyguardUpdateMonitorCallback();
+ captureKeyguardStateControllerCallback();
+
+ // WHEN keyguard was showing and now it's not
+ when(mKeyguardStateController.isShowing()).thenReturn(true);
+ mKeyguardStateCallback.onKeyguardShowingChanged();
+ when(mKeyguardStateController.isShowing()).thenReturn(false);
+ mKeyguardStateCallback.onKeyguardShowingChanged();
+
+ // THEN UI event is logged
+ verify(mUiEventLogger).log(
+ eq(SessionTracker.SessionUiEvent.KEYGUARD_SESSION_END_KEYGUARD_GOING_AWAY),
+ any(InstanceId.class));
+ }
+
void captureKeyguardUpdateMonitorCallback() {
verify(mKeyguardUpdateMonitor).registerCallback(
mKeyguardUpdateMonitorCallbackCaptor.capture());
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 fbe089a..ba29ca5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -369,6 +369,39 @@
verifyZeroInteractions(context, bubbles, eventLogger)
}
+
+ @Test
+ fun showNoteTask_keyboardShortcut_shouldStartActivity() {
+ val expectedInfo =
+ NOTE_TASK_INFO.copy(
+ entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
+ isKeyguardLocked = true,
+ )
+ whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
+ whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
+
+ createNoteTaskController()
+ .showNoteTask(
+ entryPoint = expectedInfo.entryPoint!!,
+ )
+
+ val intentCaptor = argumentCaptor<Intent>()
+ val userCaptor = argumentCaptor<UserHandle>()
+ verify(context).startActivityAsUser(capture(intentCaptor), capture(userCaptor))
+ intentCaptor.value.let { intent ->
+ assertThat(intent.action).isEqualTo(Intent.ACTION_CREATE_NOTE)
+ assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
+ assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
+ assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
+ .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+ assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
+ .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
+ assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
+ }
+ assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
+ verify(eventLogger).logNoteTaskOpened(expectedInfo)
+ verifyZeroInteractions(bubbles)
+ }
// endregion
// region setNoteTaskShortcutEnabled
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 cd67e8d..ec4daee 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -98,14 +98,24 @@
// region handleSystemKey
@Test
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL)
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
}
@Test
+ fun handleSystemKey_receiveKeyboardShortcut_shouldShowNoteTask() {
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_N, 0, KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON))
+
+ verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
+ }
+
+ @Test
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent.KEYCODE_UNKNOWN)
+ createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_UNKNOWN))
verifyZeroInteractions(controller)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
index 6f54f62..f5a3bec 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FgsManagerControllerTest.java
@@ -111,8 +111,6 @@
MockitoAnnotations.initMocks(this);
mDeviceConfigProxyFake = new DeviceConfigProxyFake();
- mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, "true", false);
mSystemClock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(mSystemClock);
mBackgroundExecutor = new FakeExecutor(mSystemClock);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
index 46af89e..9ca7a85 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServiceManagerTest.java
@@ -40,6 +40,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.settings.UserTracker;
import org.junit.After;
@@ -64,6 +65,8 @@
private QSHost mQSHost;
@Mock
private Context mMockContext;
+ @Mock
+ private CustomTileAddedRepository mCustomTileAddedRepository;
private HandlerThread mThread;
private Handler mHandler;
@@ -86,8 +89,9 @@
mComponentName = new ComponentName(mContext, TileServiceManagerTest.class);
when(mTileLifecycle.getComponent()).thenReturn(mComponentName);
+
mTileServiceManager = new TileServiceManager(mTileServices, mHandler, mUserTracker,
- mTileLifecycle);
+ mCustomTileAddedRepository, mTileLifecycle);
}
@After
@@ -98,28 +102,34 @@
@Test
public void testSetTileAddedIfNotAdded() {
- when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+ when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+ .thenReturn(false);
mTileServiceManager.startLifecycleManagerAndAddTile();
- verify(mQSHost).setTileAdded(mComponentName, mUserTracker.getUserId(), true);
+ verify(mCustomTileAddedRepository)
+ .setTileAdded(mComponentName, mUserTracker.getUserId(), true);
}
@Test
public void testNotSetTileAddedIfAdded() {
- when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(true);
+ when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+ .thenReturn(true);
mTileServiceManager.startLifecycleManagerAndAddTile();
- verify(mQSHost, never()).setTileAdded(eq(mComponentName), anyInt(), eq(true));
+ verify(mCustomTileAddedRepository, never())
+ .setTileAdded(eq(mComponentName), anyInt(), eq(true));
}
@Test
public void testSetTileAddedCorrectUser() {
int user = 10;
when(mUserTracker.getUserId()).thenReturn(user);
- when(mQSHost.isTileAdded(eq(mComponentName), anyInt())).thenReturn(false);
+ when(mCustomTileAddedRepository.isTileAdded(eq(mComponentName), anyInt()))
+ .thenReturn(false);
mTileServiceManager.startLifecycleManagerAndAddTile();
- verify(mQSHost).setTileAdded(mComponentName, user, true);
+ verify(mCustomTileAddedRepository)
+ .setTileAdded(mComponentName, user, true);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index fb93367..12b5656 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.qs.QSHost;
+import com.android.systemui.qs.pipeline.data.repository.CustomTileAddedRepository;
import com.android.systemui.qs.pipeline.domain.interactor.PanelInteractor;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.CommandQueue;
@@ -98,6 +99,8 @@
private PanelInteractor mPanelInteractor;
@Captor
private ArgumentCaptor<CommandQueue.Callbacks> mCallbacksArgumentCaptor;
+ @Mock
+ private CustomTileAddedRepository mCustomTileAddedRepository;
@Before
public void setUp() throws Exception {
@@ -115,7 +118,7 @@
mTileService = new TestTileServices(mQSHost, provider, mBroadcastDispatcher,
mUserTracker, mKeyguardStateController, mCommandQueue, mStatusBarIconController,
- mPanelInteractor);
+ mPanelInteractor, mCustomTileAddedRepository);
}
@After
@@ -293,9 +296,11 @@
TestTileServices(QSHost host, Provider<Handler> handlerProvider,
BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
KeyguardStateController keyguardStateController, CommandQueue commandQueue,
- StatusBarIconController statusBarIconController, PanelInteractor panelInteractor) {
+ StatusBarIconController statusBarIconController, PanelInteractor panelInteractor,
+ CustomTileAddedRepository customTileAddedRepository) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
- commandQueue, statusBarIconController, panelInteractor);
+ commandQueue, statusBarIconController, panelInteractor,
+ customTileAddedRepository);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
index 0b9fbd9..59f0d96 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModelTest.kt
@@ -259,7 +259,6 @@
val securityController = FakeSecurityController()
val fgsManagerController =
FakeFgsManagerController(
- isAvailable = true,
showFooterDot = false,
numRunningPackages = 0,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
new file mode 100644
index 0000000..d7ab903
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/CustomTileAddedSharedPreferencesRepositoryTest.kt
@@ -0,0 +1,156 @@
+/*
+ * 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.qs.pipeline.data.repository
+
+import android.content.ComponentName
+import android.content.SharedPreferences
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.UserFileManager
+import com.android.systemui.util.FakeSharedPreferences
+import com.google.common.truth.Truth.assertThat
+import java.io.File
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class CustomTileAddedSharedPreferencesRepositoryTest : SysuiTestCase() {
+
+ private lateinit var underTest: CustomTileAddedSharedPrefsRepository
+
+ @Test
+ fun setTileAdded_inSharedPreferences() {
+ val userId = 0
+ val sharedPrefs = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+ assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+
+ underTest.setTileAdded(TEST_COMPONENT, userId, added = false)
+ assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isFalse()
+ }
+
+ @Test
+ fun setTileAdded_differentComponents() {
+ val userId = 0
+ val sharedPrefs = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ underTest.setTileAdded(TEST_COMPONENT, userId, added = true)
+
+ assertThat(sharedPrefs.getForComponentName(TEST_COMPONENT)).isTrue()
+ assertThat(sharedPrefs.getForComponentName(OTHER_TEST_COMPONENT)).isFalse()
+ }
+
+ @Test
+ fun setTileAdded_differentUsers() {
+ val sharedPrefs0 = FakeSharedPreferences()
+ val sharedPrefs1 = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ underTest.setTileAdded(TEST_COMPONENT, userId = 1, added = true)
+
+ assertThat(sharedPrefs0.getForComponentName(TEST_COMPONENT)).isFalse()
+ assertThat(sharedPrefs1.getForComponentName(TEST_COMPONENT)).isTrue()
+ }
+
+ @Test
+ fun isTileAdded_fromSharedPreferences() {
+ val userId = 0
+ val sharedPrefs = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+
+ sharedPrefs.setForComponentName(TEST_COMPONENT, true)
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isTrue()
+
+ sharedPrefs.setForComponentName(TEST_COMPONENT, false)
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+ }
+
+ @Test
+ fun isTileAdded_differentComponents() {
+ val userId = 0
+ val sharedPrefs = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(userId to sharedPrefs))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ sharedPrefs.setForComponentName(OTHER_TEST_COMPONENT, true)
+
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId)).isFalse()
+ assertThat(underTest.isTileAdded(OTHER_TEST_COMPONENT, userId)).isTrue()
+ }
+
+ @Test
+ fun isTileAdded_differentUsers() {
+ val sharedPrefs0 = FakeSharedPreferences()
+ val sharedPrefs1 = FakeSharedPreferences()
+ val userFileManager = FakeUserFileManager(mapOf(0 to sharedPrefs0, 1 to sharedPrefs1))
+
+ underTest = CustomTileAddedSharedPrefsRepository(userFileManager)
+
+ sharedPrefs1.setForComponentName(TEST_COMPONENT, true)
+
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 0)).isFalse()
+ assertThat(underTest.isTileAdded(TEST_COMPONENT, userId = 1)).isTrue()
+ }
+
+ private fun SharedPreferences.getForComponentName(componentName: ComponentName): Boolean {
+ return getBoolean(componentName.flattenToString(), false)
+ }
+
+ private fun SharedPreferences.setForComponentName(
+ componentName: ComponentName,
+ value: Boolean
+ ) {
+ edit().putBoolean(componentName.flattenToString(), value).commit()
+ }
+
+ companion object {
+ private val TEST_COMPONENT = ComponentName("pkg", "cls")
+ private val OTHER_TEST_COMPONENT = ComponentName("pkg", "other")
+ }
+}
+
+private const val FILE_NAME = "tiles_prefs"
+
+private class FakeUserFileManager(private val sharedPrefs: Map<Int, SharedPreferences>) :
+ UserFileManager {
+ override fun getFile(fileName: String, userId: Int): File {
+ throw UnsupportedOperationException()
+ }
+
+ override fun getSharedPreferences(fileName: String, mode: Int, userId: Int): SharedPreferences {
+ if (fileName != FILE_NAME) {
+ throw IllegalArgumentException("Preference files must be $FILE_NAME")
+ }
+ return sharedPrefs.getValue(userId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
index d3ec1dd..28aeba4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileViewImplTest.kt
@@ -317,6 +317,26 @@
}
@Test
+ fun testDisableByPolicyThenRemoved_changesColor() {
+ val stateActive = QSTile.State()
+ stateActive.state = Tile.STATE_ACTIVE
+
+ val stateDisabledByPolicy = stateActive.copy()
+ stateDisabledByPolicy.disabledByPolicy = true
+
+ tileView.changeState(stateActive)
+ val activeColors = tileView.getCurrentColors()
+
+ tileView.changeState(stateDisabledByPolicy)
+ // It has unavailable colors
+ assertThat(tileView.getCurrentColors()).isNotEqualTo(activeColors)
+
+ // When we get back to not disabled by policy tile, it should go back to active colors
+ tileView.changeState(stateActive)
+ assertThat(tileView.getCurrentColors()).containsExactlyElementsIn(activeColors)
+ }
+
+ @Test
fun testDisabledByPolicy_secondaryLabelText() {
val testA11yLabel = "TEST_LABEL"
context.orCreateTestableResources
diff --git a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
index 47d88a5..77f7426 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/screenshot/TakeScreenshotServiceTest.kt
@@ -23,28 +23,21 @@
import android.graphics.Bitmap
import android.graphics.Bitmap.Config.HARDWARE
import android.graphics.ColorSpace
-import android.graphics.Insets
-import android.graphics.Rect
import android.hardware.HardwareBuffer
import android.os.UserHandle
import android.os.UserManager
import android.testing.AndroidTestingRunner
import android.view.WindowManager.ScreenshotSource.SCREENSHOT_KEY_OTHER
-import android.view.WindowManager.ScreenshotSource.SCREENSHOT_OVERVIEW
import android.view.WindowManager.TAKE_SCREENSHOT_FULLSCREEN
-import android.view.WindowManager.TAKE_SCREENSHOT_PROVIDED_IMAGE
import androidx.test.filters.SmallTest
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.internal.util.ScreenshotRequest
import com.android.systemui.SysuiTestCase
import com.android.systemui.flags.FakeFeatureFlags
-import com.android.systemui.flags.Flags.SCREENSHOT_METADATA_REFACTOR
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_CAPTURE_FAILED
import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_KEY_OTHER
-import com.android.systemui.screenshot.ScreenshotEvent.SCREENSHOT_REQUESTED_OVERVIEW
import com.android.systemui.screenshot.TakeScreenshotService.RequestCallback
import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argThat
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
@@ -61,9 +54,6 @@
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
-private const val USER_ID = 1
-private const val TASK_ID = 11
-
@RunWith(AndroidTestingRunner::class)
@SmallTest
class TakeScreenshotServiceTest : SysuiTestCase() {
@@ -123,9 +113,6 @@
.whenever(requestProcessor)
.processAsync(/* screenshot= */ any(ScreenshotData::class.java), /* callback= */ any())
- // Flipped in selected test cases
- flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
service.attach(
mContext,
/* thread = */ null,
@@ -158,39 +145,6 @@
service.handleRequest(request, { /* onSaved */}, callback)
verify(controller, times(1))
- .takeScreenshotFullscreen(
- eq(topComponent),
- /* onSavedListener = */ any(),
- /* requestCallback = */ any()
- )
-
- assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
- val logEvent = eventLogger.get(0)
-
- assertEquals(
- "Expected SCREENSHOT_REQUESTED UiEvent",
- logEvent.eventId,
- SCREENSHOT_REQUESTED_KEY_OTHER.id
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- eventLogger.get(0).packageName
- )
- }
-
- @Test
- fun takeScreenshotFullscreen_screenshotDataEnabled() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
- .setTopComponent(topComponent)
- .build()
-
- service.handleRequest(request, { /* onSaved */}, callback)
-
- verify(controller, times(1))
.handleScreenshot(
eq(ScreenshotData.fromRequest(request)),
/* onSavedListener = */ any(),
@@ -213,53 +167,7 @@
}
@Test
- fun takeScreenshotProvidedImage() {
- val bounds = Rect(50, 50, 150, 150)
- val bitmap = makeHardwareBitmap(100, 100)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_PROVIDED_IMAGE, SCREENSHOT_OVERVIEW)
- .setTopComponent(topComponent)
- .setTaskId(TASK_ID)
- .setUserId(USER_ID)
- .setBitmap(bitmap)
- .setBoundsOnScreen(bounds)
- .setInsets(Insets.NONE)
- .build()
-
- service.handleRequest(request, { /* onSaved */}, callback)
-
- verify(controller, times(1))
- .handleImageAsScreenshot(
- argThat { b -> b.equalsHardwareBitmap(bitmap) },
- eq(bounds),
- eq(Insets.NONE),
- eq(TASK_ID),
- eq(USER_ID),
- eq(topComponent),
- /* onSavedListener = */ any(),
- /* requestCallback = */ any()
- )
-
- assertEquals("Expected one UiEvent", 1, eventLogger.numLogs())
- val logEvent = eventLogger.get(0)
-
- assertEquals(
- "Expected SCREENSHOT_REQUESTED_* UiEvent",
- logEvent.eventId,
- SCREENSHOT_REQUESTED_OVERVIEW.id
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- eventLogger.get(0).packageName
- )
- }
-
- @Test
fun takeScreenshotFullscreen_userLocked() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
whenever(userManager.isUserUnlocked).thenReturn(false)
val request =
@@ -300,8 +208,6 @@
@Test
fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
.thenReturn(true)
@@ -350,143 +256,7 @@
}
@Test
- fun takeScreenshotFullscreen_userLocked_metadataDisabled() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, false)
- whenever(userManager.isUserUnlocked).thenReturn(false)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
- .setTopComponent(topComponent)
- .build()
-
- service.handleRequest(request, { /* onSaved */}, callback)
-
- verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
- verify(callback, times(1)).reportError()
- verifyZeroInteractions(controller)
-
- assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
- val requestEvent = eventLogger.get(0)
- assertEquals(
- "Expected SCREENSHOT_REQUESTED_* UiEvent",
- SCREENSHOT_REQUESTED_KEY_OTHER.id,
- requestEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- requestEvent.packageName
- )
- val failureEvent = eventLogger.get(1)
- assertEquals(
- "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
- SCREENSHOT_CAPTURE_FAILED.id,
- failureEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- failureEvent.packageName
- )
- }
-
- @Test
- fun takeScreenshotFullscreen_screenCaptureDisabled_allUsers_metadataDisabled() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
- whenever(devicePolicyManager.getScreenCaptureDisabled(isNull(), eq(UserHandle.USER_ALL)))
- .thenReturn(true)
-
- whenever(
- devicePolicyResourcesManager.getString(
- eq(SCREENSHOT_BLOCKED_BY_ADMIN),
- /* Supplier<String> */
- any(),
- )
- )
- .thenReturn("SCREENSHOT_BLOCKED_BY_ADMIN")
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
- .setTopComponent(topComponent)
- .build()
-
- service.handleRequest(request, { /* onSaved */}, callback)
-
- // error shown: Toast.makeText(...).show(), untestable
- verify(callback, times(1)).reportError()
- verifyZeroInteractions(controller)
- assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
- val requestEvent = eventLogger.get(0)
- assertEquals(
- "Expected SCREENSHOT_REQUESTED_* UiEvent",
- SCREENSHOT_REQUESTED_KEY_OTHER.id,
- requestEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- requestEvent.packageName
- )
- val failureEvent = eventLogger.get(1)
- assertEquals(
- "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
- SCREENSHOT_CAPTURE_FAILED.id,
- failureEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- failureEvent.packageName
- )
- }
-
- @Test
- fun takeScreenshot_workProfile_nullBitmap_metadataDisabled() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, false)
-
- val request =
- ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
- .setTopComponent(topComponent)
- .build()
-
- doThrow(IllegalStateException::class.java)
- .whenever(requestProcessor)
- .processAsync(any(ScreenshotRequest::class.java), any())
-
- service.handleRequest(request, { /* onSaved */}, callback)
-
- verify(callback, times(1)).reportError()
- verify(notificationsController, times(1)).notifyScreenshotError(anyInt())
- verifyZeroInteractions(controller)
- assertEquals("Expected two UiEvents", 2, eventLogger.numLogs())
- val requestEvent = eventLogger.get(0)
- assertEquals(
- "Expected SCREENSHOT_REQUESTED_* UiEvent",
- SCREENSHOT_REQUESTED_KEY_OTHER.id,
- requestEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- requestEvent.packageName
- )
- val failureEvent = eventLogger.get(1)
- assertEquals(
- "Expected SCREENSHOT_CAPTURE_FAILED UiEvent",
- SCREENSHOT_CAPTURE_FAILED.id,
- failureEvent.eventId
- )
- assertEquals(
- "Expected supplied package name",
- topComponent.packageName,
- failureEvent.packageName
- )
- }
- @Test
fun takeScreenshot_workProfile_nullBitmap() {
- flags.set(SCREENSHOT_METADATA_REFACTOR, true)
-
val request =
ScreenshotRequest.Builder(TAKE_SCREENSHOT_FULLSCREEN, SCREENSHOT_KEY_OTHER)
.setTopComponent(topComponent)
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 374aae1..78f5bf2 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
@@ -122,7 +122,7 @@
isEnabled = true,
handleAllUsers = true,
defaultClockProvider = fakeDefaultProvider,
- keepAllLoaded = true,
+ keepAllLoaded = false,
subTag = "Test",
) {
override fun querySettings() { }
@@ -154,8 +154,8 @@
pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle)
val list = registry.getClocks()
assertEquals(
- list,
- listOf(
+ list.toSet(),
+ setOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2"),
@@ -187,8 +187,8 @@
pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
val list = registry.getClocks()
assertEquals(
- list,
- listOf(
+ list.toSet(),
+ setOf(
ClockMetadata(DEFAULT_CLOCK_ID, DEFAULT_CLOCK_NAME),
ClockMetadata("clock_1", "clock 1"),
ClockMetadata("clock_2", "clock 2")
@@ -293,6 +293,53 @@
assertEquals(4, listChangeCallCount)
}
+ @Test
+ fun pluginAddRemove_concurrentModification() {
+ val mockPluginLifecycle1 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ val mockPluginLifecycle2 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ val mockPluginLifecycle3 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ val mockPluginLifecycle4 = mock<PluginLifecycleManager<ClockProviderPlugin>>()
+ val plugin1 = FakeClockPlugin().addClock("clock_1", "clock 1")
+ val plugin2 = FakeClockPlugin().addClock("clock_2", "clock 2")
+ val plugin3 = FakeClockPlugin().addClock("clock_3", "clock 3")
+ val plugin4 = FakeClockPlugin().addClock("clock_4", "clock 4")
+ whenever(mockPluginLifecycle1.isLoaded).thenReturn(true)
+ whenever(mockPluginLifecycle2.isLoaded).thenReturn(true)
+ whenever(mockPluginLifecycle3.isLoaded).thenReturn(true)
+ whenever(mockPluginLifecycle4.isLoaded).thenReturn(true)
+
+ // Set the current clock to the final clock to load
+ registry.applySettings(ClockSettings("clock_4", null))
+ scheduler.runCurrent()
+
+ // When ClockRegistry attempts to unload a plugin, we at that point decide to load and
+ // unload other plugins. This causes ClockRegistry to modify the list of available clock
+ // plugins while it is being iterated over. In production this happens as a result of a
+ // thread race, instead of synchronously like it does here.
+ whenever(mockPluginLifecycle2.unloadPlugin()).then {
+ pluginListener.onPluginDetached(mockPluginLifecycle1)
+ pluginListener.onPluginLoaded(plugin4, mockContext, mockPluginLifecycle4)
+ }
+
+ // Load initial plugins
+ pluginListener.onPluginLoaded(plugin1, mockContext, mockPluginLifecycle1)
+ pluginListener.onPluginLoaded(plugin2, mockContext, mockPluginLifecycle2)
+ pluginListener.onPluginLoaded(plugin3, mockContext, mockPluginLifecycle3)
+
+ // Repeatedly verify the loaded providers to get final state
+ registry.verifyLoadedProviders()
+ scheduler.runCurrent()
+ registry.verifyLoadedProviders()
+ scheduler.runCurrent()
+
+ // Verify all plugins were correctly loaded into the registry
+ assertEquals(registry.getClocks().toSet(), setOf(
+ ClockMetadata("DEFAULT", "Default Clock"),
+ ClockMetadata("clock_2", "clock 2"),
+ ClockMetadata("clock_3", "clock 3"),
+ ClockMetadata("clock_4", "clock 4")
+ ))
+ }
@Test
fun jsonDeserialization_gotExpectedObject() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
index f581154..f4cd383 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -33,6 +33,7 @@
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
import android.os.Bundle;
+import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -397,9 +398,10 @@
@Test
public void testHandleSysKey() {
- mCommandQueue.handleSystemKey(1);
+ KeyEvent testEvent = new KeyEvent(1, 1);
+ mCommandQueue.handleSystemKey(testEvent);
waitForIdleSync();
- verify(mCallbacks).handleSystemKey(eq(1));
+ verify(mCallbacks).handleSystemKey(eq(testEvent));
}
@Test
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 d8b3270..65735f0 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,7 +21,6 @@
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.flowOf
class FakeBiometricSettingsRepository : BiometricSettingsRepository {
@@ -39,12 +38,17 @@
private val _isStrongBiometricAllowed = MutableStateFlow(false)
override val isStrongBiometricAllowed = _isStrongBiometricAllowed.asStateFlow()
+ private val _isNonStrongBiometricAllowed = MutableStateFlow(false)
+ override val isNonStrongBiometricAllowed: StateFlow<Boolean>
+ get() = _isNonStrongBiometricAllowed
+
private val _isFingerprintEnabledByDevicePolicy = MutableStateFlow(false)
override val isFingerprintEnabledByDevicePolicy =
_isFingerprintEnabledByDevicePolicy.asStateFlow()
+ private val _isFaceAuthSupportedInCurrentPosture = MutableStateFlow(false)
override val isFaceAuthSupportedInCurrentPosture: Flow<Boolean>
- get() = flowOf(true)
+ get() = _isFaceAuthSupportedInCurrentPosture
private val _isCurrentUserInLockdown = MutableStateFlow(false)
override val isCurrentUserInLockdown: Flow<Boolean>
@@ -66,7 +70,19 @@
_isFaceEnrolled.value = isFaceEnrolled
}
+ fun setIsFaceAuthSupportedInCurrentPosture(value: Boolean) {
+ _isFaceAuthSupportedInCurrentPosture.value = value
+ }
+
fun setIsFaceAuthEnabled(enabled: Boolean) {
_isFaceAuthEnabled.value = enabled
}
+
+ fun setIsUserInLockdown(value: Boolean) {
+ _isCurrentUserInLockdown.value = value
+ }
+
+ fun setIsNonStrongBiometricAllowed(value: Boolean) {
+ _isNonStrongBiometricAllowed.value = value
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
new file mode 100644
index 0000000..fe94117
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeCommandQueue.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import com.android.systemui.settings.DisplayTracker
+import com.android.systemui.statusbar.CommandQueue
+import org.mockito.Mockito.mock
+
+class FakeCommandQueue : CommandQueue(mock(Context::class.java), mock(DisplayTracker::class.java)) {
+ private val callbacks = mutableListOf<Callbacks>()
+
+ override fun addCallback(callback: Callbacks) {
+ callbacks.add(callback)
+ }
+
+ override fun removeCallback(callback: Callbacks) {
+ callbacks.remove(callback)
+ }
+
+ fun doForEachCallback(func: (callback: Callbacks) -> Unit) {
+ callbacks.forEach { func(it) }
+ }
+
+ fun callbackCount(): Int = callbacks.size
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
index 00b1a40..4bfd3d6 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeDeviceEntryFingerprintAuthRepository.kt
@@ -30,10 +30,19 @@
override val isRunning: Flow<Boolean>
get() = _isRunning
- override val availableFpSensorType: BiometricType?
- get() = null
+ private var fpSensorType = MutableStateFlow<BiometricType?>(null)
+ override val availableFpSensorType: Flow<BiometricType?>
+ get() = fpSensorType
fun setLockedOut(lockedOut: Boolean) {
_isLockedOut.value = lockedOut
}
+
+ fun setIsRunning(value: Boolean) {
+ _isRunning.value = value
+ }
+
+ fun setAvailableFpSensorType(value: BiometricType?) {
+ fpSensorType.value = value
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
index 1dda472..8a6d2aa 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardBouncerRepository.kt
@@ -48,8 +48,6 @@
override val showMessage = _showMessage.asStateFlow()
private val _resourceUpdateRequests = MutableStateFlow(false)
override val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow()
- override val bouncerPromptReason = 0
- override val bouncerErrorMessage: CharSequence? = null
private val _isAlternateBouncerVisible = MutableStateFlow(false)
override val alternateBouncerVisible = _isAlternateBouncerVisible.asStateFlow()
override var lastAlternateBouncerVisibleTime: Long = 0L
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 194ed02..d411590 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -129,6 +129,10 @@
_isKeyguardShowing.value = isShowing
}
+ fun setKeyguardGoingAway(isGoingAway: Boolean) {
+ _isKeyguardGoingAway.value = isGoingAway
+ }
+
fun setKeyguardOccluded(isOccluded: Boolean) {
_isKeyguardOccluded.value = isOccluded
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
new file mode 100644
index 0000000..6690de8
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeTrustRepository.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.MutableStateFlow
+
+class FakeTrustRepository : TrustRepository {
+ private val _isCurrentUserTrusted = MutableStateFlow(false)
+ override val isCurrentUserTrusted: Flow<Boolean>
+ get() = _isCurrentUserTrusted
+
+ fun setCurrentUserTrusted(trust: Boolean) {
+ _isCurrentUserTrusted.value = trust
+ }
+}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
index ced7955..9ff7dd5 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/FakeFgsManagerController.kt
@@ -23,11 +23,9 @@
/** A fake [FgsManagerController] to be used in tests. */
class FakeFgsManagerController(
- isAvailable: Boolean = true,
showFooterDot: Boolean = false,
numRunningPackages: Int = 0,
) : FgsManagerController {
- override val isAvailable: MutableStateFlow<Boolean> = MutableStateFlow(isAvailable)
override var numRunningPackages = numRunningPackages
set(value) {
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
index 53bb340..fbc2381 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/user/data/repository/FakeUserRepository.kt
@@ -100,4 +100,8 @@
fun setGuestUserAutoCreated(value: Boolean) {
_isGuestUserAutoCreated = value
}
+
+ fun setUserSwitching(value: Boolean) {
+ _userSwitchingInProgress.value = value
+ }
}
diff --git a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
index f3a949d..dd7d38f 100644
--- a/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
+++ b/services/companion/java/com/android/server/companion/datatransfer/SystemDataTransferProcessor.java
@@ -133,18 +133,6 @@
@UserIdInt int userId, int associationId) {
final AssociationInfo association = resolveAssociation(packageName, userId, associationId);
- // Check if the request's data type has been requested before.
- List<SystemDataTransferRequest> storedRequests =
- mSystemDataTransferRequestStore.readRequestsByAssociationId(userId,
- associationId);
- for (SystemDataTransferRequest storedRequest : storedRequests) {
- if (storedRequest instanceof PermissionSyncRequest) {
- Slog.e(LOG_TAG, "The request has been sent before, you can not send "
- + "the same request type again.");
- return null;
- }
- }
-
Slog.i(LOG_TAG, "Creating permission sync intent for userId [" + userId
+ "] associationId [" + associationId + "]");
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 b338d89..1363ef3 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -1046,18 +1046,30 @@
*/
void showToastWhereUidIsRunning(int uid, String text, @Toast.Duration int duration,
Looper looper) {
+ ArrayList<Integer> displayIdsForUid = getDisplayIdsWhereUidIsRunning(uid);
+ if (displayIdsForUid.isEmpty()) {
+ return;
+ }
+ DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
+ for (int i = 0; i < displayIdsForUid.size(); i++) {
+ Display display = displayManager.getDisplay(displayIdsForUid.get(i));
+ if (display != null && display.isValid()) {
+ Toast.makeText(mContext.createDisplayContext(display), looper, text,
+ duration).show();
+ }
+ }
+ }
+
+ private ArrayList<Integer> getDisplayIdsWhereUidIsRunning(int uid) {
+ ArrayList<Integer> displayIdsForUid = new ArrayList<>();
synchronized (mVirtualDeviceLock) {
- DisplayManager displayManager = mContext.getSystemService(DisplayManager.class);
for (int i = 0; i < mVirtualDisplays.size(); i++) {
if (mVirtualDisplays.valueAt(i).getWindowPolicyController().containsUid(uid)) {
- Display display = displayManager.getDisplay(mVirtualDisplays.keyAt(i));
- if (display != null && display.isValid()) {
- Toast.makeText(mContext.createDisplayContext(display), looper, text,
- duration).show();
- }
+ displayIdsForUid.add(mVirtualDisplays.keyAt(i));
}
}
}
+ return displayIdsForUid;
}
boolean isDisplayOwnedByVirtualDevice(int displayId) {
diff --git a/services/core/java/com/android/server/am/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 8fe61e7..78cbf2b 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -807,9 +807,9 @@
ServiceRecord r = res.record;
// Note, when startService() or startForegroundService() is called on an already
// running SHORT_SERVICE FGS, the call will succeed (i.e. we won't throw
- // ForegroundServiceStartNotAllowedException), even when the service is alerady timed
- // out. This is because these APIs will essnetially only change the "started" state
- // of the service, and it won't afect "the foreground-ness" of the service, or the type
+ // ForegroundServiceStartNotAllowedException), even when the service is already timed
+ // out. This is because these APIs will essentially only change the "started" state
+ // of the service, and it won't affect "the foreground-ness" of the service, or the type
// of the FGS.
// However, this call will still _not_ extend the SHORT_SERVICE timeout either.
// Also, if the app tries to change the type of the FGS later (using
diff --git a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
index 9079ba8..5dd0a3f 100644
--- a/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
+++ b/services/core/java/com/android/server/am/ActivityManagerDebugConfig.java
@@ -16,6 +16,11 @@
package com.android.server.am;
+import android.util.Log;
+import android.util.LogWriter;
+
+import java.io.PrintWriter;
+
/**
* Common class for the various debug {@link android.util.Log} output configuration in the activity
* manager package.
@@ -38,6 +43,10 @@
// Default log tag for the activity manager package.
static final String TAG_AM = "ActivityManager";
+ // Default writer that emits "info" log events for the activity manager package.
+ static final PrintWriter LOG_WRITER_INFO = new PrintWriter(
+ new LogWriter(Log.INFO, TAG_AM));
+
// Enable all debug log categories.
static final boolean DEBUG_ALL = false;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index a3e5820..c25e579 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -135,6 +135,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_POWER;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PROCESSES;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_SERVICE;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BACKUP;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_BROADCAST;
import static com.android.server.am.ActivityManagerDebugConfig.POSTFIX_CLEANUP;
@@ -13744,17 +13745,6 @@
+ "RECEIVER_NOT_EXPORTED flag");
}
- // STOPSHIP(b/259139792): Allow apps that are currently targeting U and in process of
- // updating their receivers to be exempt from this requirement until their receivers
- // are flagged.
- if (requireExplicitFlagForDynamicReceivers) {
- if ("com.shannon.imsservice".equals(callerPackage)) {
- // Note, a versionCode check for this package is not performed because this
- // package consumes the SecurityException, so it wouldn't be caught during
- // presubmit.
- requireExplicitFlagForDynamicReceivers = false;
- }
- }
if (!onlyProtectedBroadcasts) {
if (receiver == null && !explicitExportStateDefined) {
// sticky broadcast, no flag specified (flag isn't required)
@@ -18723,27 +18713,25 @@
@Override
public void waitForBroadcastIdle() {
- waitForBroadcastIdle(/* printWriter= */ null);
+ waitForBroadcastIdle(LOG_WRITER_INFO);
}
- public void waitForBroadcastIdle(@Nullable PrintWriter pw) {
+ public void waitForBroadcastIdle(@NonNull PrintWriter pw) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastIdle()");
BroadcastLoopers.waitForIdle(pw);
for (BroadcastQueue queue : mBroadcastQueues) {
queue.waitForIdle(pw);
}
- if (pw != null) {
- pw.println("All broadcast queues are idle!");
- pw.flush();
- }
+ pw.println("All broadcast queues are idle!");
+ pw.flush();
}
@Override
public void waitForBroadcastBarrier() {
- waitForBroadcastBarrier(/* printWriter= */ null, false, false);
+ waitForBroadcastBarrier(LOG_WRITER_INFO, false, false);
}
- public void waitForBroadcastBarrier(@Nullable PrintWriter pw,
+ public void waitForBroadcastBarrier(@NonNull PrintWriter pw,
boolean flushBroadcastLoopers, boolean flushApplicationThreads) {
enforceCallingPermission(permission.DUMP, "waitForBroadcastBarrier()");
if (flushBroadcastLoopers) {
@@ -18761,11 +18749,7 @@
* Wait for all pending {@link IApplicationThread} events to be processed in
* all currently running apps.
*/
- public void waitForApplicationBarrier(@Nullable PrintWriter pw) {
- if (pw == null) {
- pw = new PrintWriter(new LogWriter(Log.VERBOSE, TAG));
- }
-
+ public void waitForApplicationBarrier(@NonNull PrintWriter pw) {
final CountDownLatch finishedLatch = new CountDownLatch(1);
final AtomicInteger pingCount = new AtomicInteger(0);
final AtomicInteger pongCount = new AtomicInteger(0);
@@ -18779,6 +18763,7 @@
// too quickly in parallel below
pingCount.incrementAndGet();
+ synchronized (this) {
synchronized (mProcLock) {
final ArrayMap<String, SparseArray<ProcessRecord>> pmap =
mProcessList.getProcessNamesLOSP().getMap();
@@ -18803,6 +18788,7 @@
}
}
}
+ }
// Now that we've dispatched all "ping" events above, we can send our
// "pong" sentinel value
@@ -18813,15 +18799,18 @@
try {
if (finishedLatch.await(1, TimeUnit.SECONDS)) {
pw.println("Finished application barriers!");
+ pw.flush();
return;
} else {
pw.println("Waiting for application barriers, at " + pongCount.get() + " of "
+ pingCount.get() + "...");
+ pw.flush();
}
} catch (InterruptedException ignored) {
}
}
pw.println("Gave up waiting for application barriers!");
+ pw.flush();
}
void setIgnoreDeliveryGroupPolicy(@NonNull String broadcastAction) {
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index 72e17d8..6c6ac1e 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -38,6 +38,7 @@
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_LOW;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_MODERATE;
import static com.android.internal.app.procstats.ProcessStats.ADJ_MEM_FACTOR_NORMAL;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import static com.android.server.am.AppBatteryTracker.BatteryUsage.BATTERY_USAGE_COUNT;
@@ -112,6 +113,7 @@
import android.util.ArraySet;
import android.util.DebugUtils;
import android.util.DisplayMetrics;
+import android.util.TeeWriter;
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.window.SplashScreen;
@@ -3364,11 +3366,13 @@
}
int runWaitForBroadcastIdle(PrintWriter pw) throws RemoteException {
+ pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
mInternal.waitForBroadcastIdle(pw);
return 0;
}
int runWaitForBroadcastBarrier(PrintWriter pw) throws RemoteException {
+ pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
boolean flushBroadcastLoopers = false;
boolean flushApplicationThreads = false;
String opt;
@@ -3387,6 +3391,7 @@
}
int runWaitForApplicationBarrier(PrintWriter pw) throws RemoteException {
+ pw = new PrintWriter(new TeeWriter(LOG_WRITER_INFO, pw));
mInternal.waitForApplicationBarrier(pw);
return 0;
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index d9ba845..ed297d0 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -654,6 +654,7 @@
synchronized (mLock) {
final long elapsedRealtime = SystemClock.elapsedRealtime();
mHandler.post(() -> {
+ mCpuWakeupStats.onUidRemoved(uid);
synchronized (mStats) {
mStats.removeUidStatsLocked(uid, elapsedRealtime);
}
@@ -764,6 +765,7 @@
final long elapsedRealtime = SystemClock.elapsedRealtime();
final long uptime = SystemClock.uptimeMillis();
mHandler.post(() -> {
+ mCpuWakeupStats.noteUidProcessState(uid, state);
synchronized (mStats) {
mStats.noteUidProcessStateLocked(uid, state, elapsedRealtime, uptime);
}
diff --git a/services/core/java/com/android/server/am/BroadcastConstants.java b/services/core/java/com/android/server/am/BroadcastConstants.java
index 4d46963..094b040 100644
--- a/services/core/java/com/android/server/am/BroadcastConstants.java
+++ b/services/core/java/com/android/server/am/BroadcastConstants.java
@@ -203,11 +203,11 @@
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to broadcasts
- * targeting cached applications.
+ * targeting frozen applications.
*/
- public long DELAY_CACHED_MILLIS = DEFAULT_DELAY_CACHED_MILLIS;
- private static final String KEY_DELAY_CACHED_MILLIS = "bcast_delay_cached_millis";
- private static final long DEFAULT_DELAY_CACHED_MILLIS = +120_000;
+ public long DELAY_FROZEN_MILLIS = DEFAULT_DELAY_FROZEN_MILLIS;
+ private static final String KEY_DELAY_FROZEN_MILLIS = "bcast_delay_frozen_millis";
+ private static final long DEFAULT_DELAY_FROZEN_MILLIS = +120_000;
/**
* For {@link BroadcastQueueModernImpl}: Delay to apply to urgent
@@ -373,8 +373,8 @@
DEFAULT_MAX_PENDING_BROADCASTS);
DELAY_NORMAL_MILLIS = getDeviceConfigLong(KEY_DELAY_NORMAL_MILLIS,
DEFAULT_DELAY_NORMAL_MILLIS);
- DELAY_CACHED_MILLIS = getDeviceConfigLong(KEY_DELAY_CACHED_MILLIS,
- DEFAULT_DELAY_CACHED_MILLIS);
+ DELAY_FROZEN_MILLIS = getDeviceConfigLong(KEY_DELAY_FROZEN_MILLIS,
+ DEFAULT_DELAY_FROZEN_MILLIS);
DELAY_URGENT_MILLIS = getDeviceConfigLong(KEY_DELAY_URGENT_MILLIS,
DEFAULT_DELAY_URGENT_MILLIS);
MAX_HISTORY_COMPLETE_SIZE = getDeviceConfigInt(KEY_MAX_HISTORY_COMPLETE_SIZE,
@@ -421,8 +421,8 @@
pw.print(KEY_MAX_PENDING_BROADCASTS, MAX_PENDING_BROADCASTS).println();
pw.print(KEY_DELAY_NORMAL_MILLIS,
TimeUtils.formatDuration(DELAY_NORMAL_MILLIS)).println();
- pw.print(KEY_DELAY_CACHED_MILLIS,
- TimeUtils.formatDuration(DELAY_CACHED_MILLIS)).println();
+ pw.print(KEY_DELAY_FROZEN_MILLIS,
+ TimeUtils.formatDuration(DELAY_FROZEN_MILLIS)).println();
pw.print(KEY_DELAY_URGENT_MILLIS,
TimeUtils.formatDuration(DELAY_URGENT_MILLIS)).println();
pw.print(KEY_MAX_HISTORY_COMPLETE_SIZE, MAX_HISTORY_COMPLETE_SIZE).println();
diff --git a/services/core/java/com/android/server/am/BroadcastLoopers.java b/services/core/java/com/android/server/am/BroadcastLoopers.java
index a5535cb..92547ea 100644
--- a/services/core/java/com/android/server/am/BroadcastLoopers.java
+++ b/services/core/java/com/android/server/am/BroadcastLoopers.java
@@ -17,7 +17,6 @@
package com.android.server.am;
import android.annotation.NonNull;
-import android.annotation.Nullable;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -74,7 +73,7 @@
* defined by {@link MessageQueue#isIdle()}. Note that {@link Message#when}
* still in the future are ignored for the purposes of the idle test.
*/
- public static void waitForIdle(@Nullable PrintWriter pw) {
+ public static void waitForIdle(@NonNull PrintWriter pw) {
waitForCondition(pw, (looper, latch) -> {
final MessageQueue queue = looper.getQueue();
queue.addIdleHandler(() -> {
@@ -89,7 +88,7 @@
* Note that {@link Message#when} still in the future are ignored for the purposes
* of the idle test.
*/
- public static void waitForBarrier(@Nullable PrintWriter pw) {
+ public static void waitForBarrier(@NonNull PrintWriter pw) {
waitForCondition(pw, (looper, latch) -> {
(new Handler(looper)).post(() -> {
latch.countDown();
@@ -100,7 +99,7 @@
/**
* Wait for all registered {@link Looper} instances to meet a certain condition.
*/
- private static void waitForCondition(@Nullable PrintWriter pw,
+ private static void waitForCondition(@NonNull PrintWriter pw,
@NonNull BiConsumer<Looper, CountDownLatch> condition) {
final CountDownLatch latch;
synchronized (sLoopers) {
@@ -122,18 +121,12 @@
final long now = SystemClock.uptimeMillis();
if (now >= lastPrint + 1000) {
lastPrint = now;
- logv("Waiting for " + latch.getCount() + " loopers to drain...", pw);
+ pw.println("Waiting for " + latch.getCount() + " loopers to drain...");
+ pw.flush();
}
SystemClock.sleep(100);
}
- logv("Loopers drained!", pw);
- }
-
- private static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
- Slog.v(TAG, msg);
- if (pw != null) {
- pw.println(msg);
- pw.flush();
- }
+ pw.println("Loopers drained!");
+ pw.flush();
}
}
diff --git a/services/core/java/com/android/server/am/BroadcastProcessQueue.java b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
index 056e17a..fa3d4ca 100644
--- a/services/core/java/com/android/server/am/BroadcastProcessQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastProcessQueue.java
@@ -21,6 +21,7 @@
import static com.android.server.am.BroadcastRecord.isDeliveryStateTerminal;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
+import android.annotation.CheckResult;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -188,7 +189,7 @@
private @Reason int mRunnableAtReason = REASON_EMPTY;
private boolean mRunnableAtInvalidated;
- private boolean mProcessCached;
+ private boolean mUidFrozen;
private boolean mProcessInstrumented;
private boolean mProcessPersistent;
@@ -341,7 +342,12 @@
* Predicates that choose to remove a broadcast <em>must</em> finish
* delivery of the matched broadcast, to ensure that situations like ordered
* broadcasts are handled consistently.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
+ @CheckResult
public boolean forEachMatchingBroadcast(@NonNull BroadcastPredicate predicate,
@NonNull BroadcastConsumer consumer, boolean andRemove) {
boolean didSomething = false;
@@ -354,6 +360,7 @@
return didSomething;
}
+ @CheckResult
private boolean forEachMatchingBroadcastInQueue(@NonNull ArrayDeque<SomeArgs> queue,
@NonNull BroadcastPredicate predicate, @NonNull BroadcastConsumer consumer,
boolean andRemove) {
@@ -370,6 +377,10 @@
args.recycle();
it.remove();
onBroadcastDequeued(record, recordIndex, recordWouldBeSkipped);
+ } else {
+ // Even if we're leaving broadcast in queue, it may have
+ // been mutated in such a way to change our runnable time
+ invalidateRunnableAt();
}
didSomething = true;
}
@@ -381,33 +392,43 @@
/**
* Update the actively running "warm" process for this process.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
- public void setProcess(@Nullable ProcessRecord app) {
+ @CheckResult
+ public boolean setProcessAndUidFrozen(@Nullable ProcessRecord app, boolean uidFrozen) {
this.app = app;
- if (app != null) {
- setProcessCached(app.isCached());
- setProcessInstrumented(app.getActiveInstrumentation() != null);
- setProcessPersistent(app.isPersistent());
- } else {
- setProcessCached(false);
- setProcessInstrumented(false);
- setProcessPersistent(false);
- }
// Since we may have just changed our PID, invalidate cached strings
mCachedToString = null;
mCachedToShortString = null;
+
+ boolean didSomething = false;
+ if (app != null) {
+ didSomething |= setProcessInstrumented(app.getActiveInstrumentation() != null);
+ didSomething |= setProcessPersistent(app.isPersistent());
+ didSomething |= setUidFrozen(uidFrozen);
+ } else {
+ didSomething |= setProcessInstrumented(false);
+ didSomething |= setProcessPersistent(false);
+ didSomething |= setUidFrozen(uidFrozen);
+ }
+ return didSomething;
}
/**
- * Update if this process is in the "cached" state, typically signaling that
+ * Update if this UID is in the "frozen" state, typically signaling that
* broadcast dispatch should be paused or delayed.
*/
- @VisibleForTesting
- void setProcessCached(boolean cached) {
- if (mProcessCached != cached) {
- mProcessCached = cached;
+ private boolean setUidFrozen(boolean frozen) {
+ if (mUidFrozen != frozen) {
+ mUidFrozen = frozen;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -416,10 +437,13 @@
* signaling that broadcast dispatch should bypass all pauses or delays, to
* avoid holding up test suites.
*/
- public void setProcessInstrumented(boolean instrumented) {
+ private boolean setProcessInstrumented(boolean instrumented) {
if (mProcessInstrumented != instrumented) {
mProcessInstrumented = instrumented;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -427,10 +451,13 @@
* Update if this process is in the "persistent" state, which signals broadcast dispatch should
* bypass all pauses or delays to prevent the system from becoming out of sync with itself.
*/
- public void setProcessPersistent(boolean persistent) {
+ private boolean setProcessPersistent(boolean persistent) {
if (mProcessPersistent != persistent) {
mProcessPersistent = persistent;
invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
}
}
@@ -650,8 +677,20 @@
return mActive != null;
}
- void forceDelayBroadcastDelivery(long delayedDurationMs) {
- mForcedDelayedDurationMs = delayedDurationMs;
+ /**
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
+ */
+ @CheckResult
+ boolean forceDelayBroadcastDelivery(long delayedDurationMs) {
+ if (mForcedDelayedDurationMs != delayedDurationMs) {
+ mForcedDelayedDurationMs = delayedDurationMs;
+ invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -723,10 +762,21 @@
* broadcasts would be prioritized for dispatching, even if there are urgent broadcasts
* waiting. This is typically used in case there are callers waiting for "barrier" to be
* reached.
+ *
+ * @return if this operation may have changed internal state, indicating
+ * that the caller is responsible for invoking
+ * {@link BroadcastQueueModernImpl#updateRunnableList}
*/
+ @CheckResult
@VisibleForTesting
- void setPrioritizeEarliest(boolean prioritizeEarliest) {
- mPrioritizeEarliest = prioritizeEarliest;
+ boolean setPrioritizeEarliest(boolean prioritizeEarliest) {
+ if (mPrioritizeEarliest != prioritizeEarliest) {
+ mPrioritizeEarliest = prioritizeEarliest;
+ invalidateRunnableAt();
+ return true;
+ } else {
+ return false;
+ }
}
/**
@@ -813,7 +863,7 @@
public boolean isDeferredUntilActive() {
if (mRunnableAtInvalidated) updateRunnableAt();
- return mRunnableAtReason == BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER;
+ return mRunnableAtReason == BroadcastProcessQueue.REASON_INFINITE_DEFER;
}
public boolean hasDeferredBroadcasts() {
@@ -848,14 +898,14 @@
}
static final int REASON_EMPTY = 0;
- static final int REASON_CACHED = 1;
+ static final int REASON_FROZEN = 1;
static final int REASON_NORMAL = 2;
static final int REASON_MAX_PENDING = 3;
static final int REASON_BLOCKED = 4;
static final int REASON_INSTRUMENTED = 5;
static final int REASON_PERSISTENT = 6;
static final int REASON_FORCE_DELAYED = 7;
- static final int REASON_CACHED_INFINITE_DEFER = 8;
+ static final int REASON_INFINITE_DEFER = 8;
static final int REASON_CONTAINS_FOREGROUND = 10;
static final int REASON_CONTAINS_ORDERED = 11;
static final int REASON_CONTAINS_ALARM = 12;
@@ -864,17 +914,18 @@
static final int REASON_CONTAINS_RESULT_TO = 15;
static final int REASON_CONTAINS_INSTRUMENTED = 16;
static final int REASON_CONTAINS_MANIFEST = 17;
+ static final int REASON_FOREGROUND_ACTIVITIES = 18;
@IntDef(flag = false, prefix = { "REASON_" }, value = {
REASON_EMPTY,
- REASON_CACHED,
+ REASON_FROZEN,
REASON_NORMAL,
REASON_MAX_PENDING,
REASON_BLOCKED,
REASON_INSTRUMENTED,
REASON_PERSISTENT,
REASON_FORCE_DELAYED,
- REASON_CACHED_INFINITE_DEFER,
+ REASON_INFINITE_DEFER,
REASON_CONTAINS_FOREGROUND,
REASON_CONTAINS_ORDERED,
REASON_CONTAINS_ALARM,
@@ -883,6 +934,7 @@
REASON_CONTAINS_RESULT_TO,
REASON_CONTAINS_INSTRUMENTED,
REASON_CONTAINS_MANIFEST,
+ REASON_FOREGROUND_ACTIVITIES,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
@@ -890,14 +942,14 @@
static @NonNull String reasonToString(@Reason int reason) {
switch (reason) {
case REASON_EMPTY: return "EMPTY";
- case REASON_CACHED: return "CACHED";
+ case REASON_FROZEN: return "FROZEN";
case REASON_NORMAL: return "NORMAL";
case REASON_MAX_PENDING: return "MAX_PENDING";
case REASON_BLOCKED: return "BLOCKED";
case REASON_INSTRUMENTED: return "INSTRUMENTED";
case REASON_PERSISTENT: return "PERSISTENT";
case REASON_FORCE_DELAYED: return "FORCE_DELAYED";
- case REASON_CACHED_INFINITE_DEFER: return "INFINITE_DEFER";
+ case REASON_INFINITE_DEFER: return "INFINITE_DEFER";
case REASON_CONTAINS_FOREGROUND: return "CONTAINS_FOREGROUND";
case REASON_CONTAINS_ORDERED: return "CONTAINS_ORDERED";
case REASON_CONTAINS_ALARM: return "CONTAINS_ALARM";
@@ -906,6 +958,7 @@
case REASON_CONTAINS_RESULT_TO: return "CONTAINS_RESULT_TO";
case REASON_CONTAINS_INSTRUMENTED: return "CONTAINS_INSTRUMENTED";
case REASON_CONTAINS_MANIFEST: return "CONTAINS_MANIFEST";
+ case REASON_FOREGROUND_ACTIVITIES: return "FOREGROUND_ACTIVITIES";
default: return Integer.toString(reason);
}
}
@@ -963,6 +1016,11 @@
} else if (mProcessInstrumented) {
mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
mRunnableAtReason = REASON_INSTRUMENTED;
+ } else if (app != null && app.hasForegroundActivities()) {
+ // TODO: Listen for uid state changes to check when an uid goes in and out of
+ // the TOP state.
+ mRunnableAt = runnableAt + constants.DELAY_URGENT_MILLIS;
+ mRunnableAtReason = REASON_FOREGROUND_ACTIVITIES;
} else if (mCountOrdered > 0) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_ORDERED;
@@ -978,12 +1036,12 @@
} else if (mProcessPersistent) {
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_PERSISTENT;
- } else if (mProcessCached) {
+ } else if (mUidFrozen) {
if (r.deferUntilActive) {
// All enqueued broadcasts are deferrable, defer
if (mCountDeferred == mCountEnqueued) {
mRunnableAt = Long.MAX_VALUE;
- mRunnableAtReason = REASON_CACHED_INFINITE_DEFER;
+ mRunnableAtReason = REASON_INFINITE_DEFER;
} else {
// At least one enqueued broadcast isn't deferrable, repick time and reason
// for this record. If a later record is not deferrable and is one of these
@@ -998,14 +1056,14 @@
mRunnableAt = runnableAt;
mRunnableAtReason = REASON_CONTAINS_RESULT_TO;
} else {
- mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
- mRunnableAtReason = REASON_CACHED;
+ mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
+ mRunnableAtReason = REASON_FROZEN;
}
}
} else {
// This record isn't deferrable
- mRunnableAt = runnableAt + constants.DELAY_CACHED_MILLIS;
- mRunnableAtReason = REASON_CACHED;
+ mRunnableAt = runnableAt + constants.DELAY_FROZEN_MILLIS;
+ mRunnableAtReason = REASON_FROZEN;
}
} else if (mCountResultTo > 0) {
// All resultTo broadcasts are infinitely deferrable, so if the app
@@ -1187,8 +1245,8 @@
@NeverCompile
private void dumpProcessState(@NonNull IndentingPrintWriter pw) {
final StringBuilder sb = new StringBuilder();
- if (mProcessCached) {
- sb.append("CACHED");
+ if (mUidFrozen) {
+ sb.append("FROZEN");
}
if (mProcessInstrumented) {
if (sb.length() > 0) sb.append("|");
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index 75e9336..6d1344d 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -69,14 +69,6 @@
Slog.v(TAG, msg);
}
- static void logv(@NonNull String msg, @Nullable PrintWriter pw) {
- logv(msg);
- if (pw != null) {
- pw.println(msg);
- pw.flush();
- }
- }
-
static void checkState(boolean expression, @NonNull String msg) {
if (!expression) {
throw new IllegalStateException(msg);
@@ -219,7 +211,7 @@
* since running apps can continue sending new broadcasts in perpetuity;
* consider using {@link #waitForBarrier} instead.
*/
- public abstract void waitForIdle(@Nullable PrintWriter pw);
+ public abstract void waitForIdle(@NonNull PrintWriter pw);
/**
* Wait until any currently waiting broadcasts have been dispatched.
@@ -230,7 +222,7 @@
* Callers are advised that this method will <em>not</em> wait for any
* future broadcasts that are newly enqueued after being invoked.
*/
- public abstract void waitForBarrier(@Nullable PrintWriter pw);
+ public abstract void waitForBarrier(@NonNull PrintWriter pw);
/**
* Delays delivering broadcasts to the specified package.
diff --git a/services/core/java/com/android/server/am/BroadcastQueueImpl.java b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
index 4b8dc99..bd36c3f 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueImpl.java
@@ -835,7 +835,7 @@
OOM_ADJ_REASON_START_RECEIVER);
}
} else if (filter.receiverList.app != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(filter.receiverList.app,
+ mService.mOomAdjuster.unfreezeTemporarily(filter.receiverList.app,
CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
}
@@ -1129,7 +1129,7 @@
}
if (sendResult) {
if (r.callerApp != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+ mService.mOomAdjuster.unfreezeTemporarily(
r.callerApp,
CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
}
diff --git a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
index 5010ec0..3a25a6e 100644
--- a/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
+++ b/services/core/java/com/android/server/am/BroadcastQueueModernImpl.java
@@ -16,6 +16,7 @@
package com.android.server.am;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
import static android.os.Process.ZYGOTE_POLICY_FLAG_EMPTY;
import static android.os.Process.ZYGOTE_POLICY_FLAG_LATENCY_SENSITIVE;
@@ -28,6 +29,7 @@
import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_NORMAL;
import static com.android.internal.util.FrameworkStatsLog.SERVICE_REQUEST_EVENT_REPORTED__PACKAGE_STOPPED_STATE__PACKAGE_STATE_STOPPED;
import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_BROADCAST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.BroadcastProcessQueue.insertIntoRunnableList;
import static com.android.server.am.BroadcastProcessQueue.reasonToString;
import static com.android.server.am.BroadcastProcessQueue.removeFromRunnableList;
@@ -47,7 +49,7 @@
import android.app.ApplicationExitInfo;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
-import android.app.UidObserver;
+import android.app.IUidFrozenStateChangedCallback;
import android.app.usage.UsageEvents.Event;
import android.content.ComponentName;
import android.content.ContentResolver;
@@ -71,6 +73,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -208,6 +211,16 @@
private final AtomicReference<ArraySet<BroadcastRecord>> mReplacedBroadcastsCache =
new AtomicReference<>();
+ /**
+ * Map from UID to its last known "frozen" state.
+ * <p>
+ * We manually maintain this data structure since the lifecycle of
+ * {@link ProcessRecord} and {@link BroadcastProcessQueue} can be
+ * mismatched.
+ */
+ @GuardedBy("mService")
+ private final SparseBooleanArray mUidFrozen = new SparseBooleanArray();
+
private final BroadcastConstants mConstants;
private final BroadcastConstants mFgConstants;
private final BroadcastConstants mBgConstants;
@@ -348,8 +361,6 @@
// If app isn't running, and there's nothing in the queue, clean up
if (queue.isEmpty() && !queue.isActive() && !queue.isProcessWarm()) {
removeProcessQueue(queue.processName, queue.uid);
- } else {
- updateQueueDeferred(queue);
}
}
@@ -484,7 +495,7 @@
// relevant per-process queue
final BroadcastProcessQueue queue = getProcessQueue(app);
if (queue != null) {
- queue.setProcess(app);
+ setQueueProcess(queue, app);
}
boolean didSomething = false;
@@ -525,7 +536,7 @@
// relevant per-process queue
final BroadcastProcessQueue queue = getProcessQueue(app);
if (queue != null) {
- queue.setProcess(null);
+ setQueueProcess(queue, null);
}
if ((mRunningColdStart != null) && (mRunningColdStart == queue)) {
@@ -552,6 +563,7 @@
return (r.receivers.get(i) instanceof BroadcastFilter);
}, mBroadcastConsumerSkip, true);
if (didSomething || queue.isEmpty()) {
+ updateQueueDeferred(queue);
updateRunnableList(queue);
enqueueUpdateRunningList();
}
@@ -617,6 +629,7 @@
setDeliveryState(queue, null, r, i, receiver, BroadcastRecord.DELIVERY_DEFERRED,
"deferred at enqueue time");
}
+ updateQueueDeferred(queue);
updateRunnableList(queue);
enqueueUpdateRunningList();
}
@@ -927,7 +940,7 @@
final ProcessRecord app = r.resultToApp;
final IApplicationThread thread = (app != null) ? app.getOnewayThread() : null;
if (thread != null) {
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(
+ mService.mOomAdjuster.unfreezeTemporarily(
app, CachedAppOptimizer.UNFREEZE_REASON_FINISH_RECEIVER);
if (r.shareIdentity && app.uid != r.callingUid) {
mService.mPackageManagerInt.grantImplicitAccess(r.userId, r.intent,
@@ -1065,6 +1078,7 @@
final int queueIndex = getRunningIndexOf(queue);
mRunning[queueIndex] = null;
+ updateQueueDeferred(queue);
updateRunnableList(queue);
enqueueUpdateRunningList();
@@ -1147,6 +1161,7 @@
getReceiverUid(otherReceiver));
if (otherQueue != null) {
otherQueue.invalidateRunnableAt();
+ updateQueueDeferred(otherQueue);
updateRunnableList(otherQueue);
}
}
@@ -1247,7 +1262,7 @@
* the given {@link Predicate}.
*/
private boolean testAllProcessQueues(@NonNull Predicate<BroadcastProcessQueue> test,
- @NonNull String label, @Nullable PrintWriter pw) {
+ @NonNull String label, @NonNull PrintWriter pw) {
for (int i = 0; i < mProcessQueues.size(); i++) {
BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
while (leaf != null) {
@@ -1255,14 +1270,16 @@
final long now = SystemClock.uptimeMillis();
if (now > mLastTestFailureTime + DateUtils.SECOND_IN_MILLIS) {
mLastTestFailureTime = now;
- logv("Test " + label + " failed due to " + leaf.toShortString(), pw);
+ pw.println("Test " + label + " failed due to " + leaf.toShortString());
+ pw.flush();
}
return false;
}
leaf = leaf.processNameNext;
}
}
- logv("Test " + label + " passed", pw);
+ pw.println("Test " + label + " passed");
+ pw.flush();
return true;
}
@@ -1277,6 +1294,7 @@
if (queuePredicate.test(leaf)) {
if (leaf.forEachMatchingBroadcast(broadcastPredicate,
broadcastConsumer, andRemove)) {
+ updateQueueDeferred(leaf);
updateRunnableList(leaf);
didSomething = true;
}
@@ -1290,29 +1308,40 @@
return didSomething;
}
- private void forEachMatchingQueue(
+ private boolean forEachMatchingQueue(
@NonNull Predicate<BroadcastProcessQueue> queuePredicate,
@NonNull Consumer<BroadcastProcessQueue> queueConsumer) {
+ boolean didSomething = false;
for (int i = mProcessQueues.size() - 1; i >= 0; i--) {
BroadcastProcessQueue leaf = mProcessQueues.valueAt(i);
while (leaf != null) {
if (queuePredicate.test(leaf)) {
queueConsumer.accept(leaf);
+ updateQueueDeferred(leaf);
updateRunnableList(leaf);
+ didSomething = true;
}
leaf = leaf.processNameNext;
}
}
+ if (didSomething) {
+ enqueueUpdateRunningList();
+ }
+ return didSomething;
}
- private void updateQueueDeferred(
- @NonNull BroadcastProcessQueue leaf) {
+ @SuppressWarnings("CheckResult")
+ private void updateQueueDeferred(@NonNull BroadcastProcessQueue leaf) {
if (leaf.isDeferredUntilActive()) {
+ // We ignore the returned value here since callers are invoking us
+ // just before updateRunnableList()
leaf.forEachMatchingBroadcast((r, i) -> {
return r.deferUntilActive && (r.getDeliveryState(i)
== BroadcastRecord.DELIVERY_PENDING);
}, mBroadcastConsumerDefer, false);
} else if (leaf.hasDeferredBroadcasts()) {
+ // We ignore the returned value here since callers are invoking us
+ // just before updateRunnableList()
leaf.forEachMatchingBroadcast((r, i) -> {
return r.deferUntilActive && (r.getDeliveryState(i)
== BroadcastRecord.DELIVERY_DEFERRED);
@@ -1325,23 +1354,31 @@
mFgConstants.startObserving(mHandler, resolver);
mBgConstants.startObserving(mHandler, resolver);
- mService.registerUidObserver(new UidObserver() {
+ mService.registerUidFrozenStateChangedCallback(new IUidFrozenStateChangedCallback.Stub() {
@Override
- public void onUidCachedChanged(int uid, boolean cached) {
+ public void onUidFrozenStateChanged(int[] uids, int[] frozenStates) {
synchronized (mService) {
- BroadcastProcessQueue leaf = mProcessQueues.get(uid);
- while (leaf != null) {
- // Update internal state by refreshing values previously
- // read from any known running process
- leaf.setProcess(leaf.app);
- updateQueueDeferred(leaf);
- updateRunnableList(leaf);
- leaf = leaf.processNameNext;
+ for (int i = 0; i < uids.length; i++) {
+ final int uid = uids[i];
+ final boolean frozen = frozenStates[i] == UID_FROZEN_STATE_FROZEN;
+ if (frozen) {
+ mUidFrozen.put(uid, true);
+ } else {
+ mUidFrozen.delete(uid);
+ }
+
+ BroadcastProcessQueue leaf = mProcessQueues.get(uid);
+ while (leaf != null) {
+ // Update internal state by refreshing values previously
+ // read from any known running process
+ setQueueProcess(leaf, leaf.app);
+ leaf = leaf.processNameNext;
+ }
+ enqueueUpdateRunningList();
}
- enqueueUpdateRunningList();
}
}
- }, ActivityManager.UID_OBSERVER_CACHED, 0, "android");
+ });
// Kick off periodic health checks
mLocalHandler.sendEmptyMessage(MSG_CHECK_HEALTH);
@@ -1349,30 +1386,30 @@
@Override
public boolean isIdleLocked() {
- return isIdleLocked(null);
+ return isIdleLocked(LOG_WRITER_INFO);
}
- public boolean isIdleLocked(@Nullable PrintWriter pw) {
+ public boolean isIdleLocked(@NonNull PrintWriter pw) {
return testAllProcessQueues(q -> q.isIdle(), "idle", pw);
}
@Override
public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime) {
- return isBeyondBarrierLocked(barrierTime, null);
+ return isBeyondBarrierLocked(barrierTime, LOG_WRITER_INFO);
}
public boolean isBeyondBarrierLocked(@UptimeMillisLong long barrierTime,
- @Nullable PrintWriter pw) {
+ @NonNull PrintWriter pw) {
return testAllProcessQueues(q -> q.isBeyondBarrierLocked(barrierTime), "barrier", pw);
}
@Override
- public void waitForIdle(@Nullable PrintWriter pw) {
+ public void waitForIdle(@NonNull PrintWriter pw) {
waitFor(() -> isIdleLocked(pw));
}
@Override
- public void waitForBarrier(@Nullable PrintWriter pw) {
+ public void waitForBarrier(@NonNull PrintWriter pw) {
final long now = SystemClock.uptimeMillis();
waitFor(() -> isBeyondBarrierLocked(now, pw));
}
@@ -1495,7 +1532,20 @@
private void updateWarmProcess(@NonNull BroadcastProcessQueue queue) {
if (!queue.isProcessWarm()) {
- queue.setProcess(mService.getProcessRecordLocked(queue.processName, queue.uid));
+ setQueueProcess(queue, mService.getProcessRecordLocked(queue.processName, queue.uid));
+ }
+ }
+
+ /**
+ * Update the {@link ProcessRecord} associated with the given
+ * {@link BroadcastProcessQueue}. Also updates any runnable status that
+ * might have changed as a side-effect.
+ */
+ private void setQueueProcess(@NonNull BroadcastProcessQueue queue,
+ @Nullable ProcessRecord app) {
+ if (queue.setProcessAndUidFrozen(app, mUidFrozen.get(queue.uid, false))) {
+ updateQueueDeferred(queue);
+ updateRunnableList(queue);
}
}
@@ -1513,7 +1563,7 @@
mService.updateLruProcessLocked(queue.app, false, null);
}
- mService.mOomAdjuster.mCachedAppOptimizer.unfreezeTemporarily(queue.app,
+ mService.mOomAdjuster.unfreezeTemporarily(queue.app,
CachedAppOptimizer.UNFREEZE_REASON_START_RECEIVER);
if (queue.runningOomAdjusted) {
@@ -1689,7 +1739,7 @@
}
BroadcastProcessQueue created = new BroadcastProcessQueue(mConstants, processName, uid);
- created.setProcess(mService.getProcessRecordLocked(processName, uid));
+ setQueueProcess(created, mService.getProcessRecordLocked(processName, uid));
if (leaf == null) {
mProcessQueues.put(uid, created);
@@ -1812,12 +1862,18 @@
ipw.decreaseIndent();
ipw.println();
- ipw.println(" Broadcasts with ignored delivery group policies:");
+ ipw.println("Broadcasts with ignored delivery group policies:");
ipw.increaseIndent();
mService.dumpDeliveryGroupPolicyIgnoredActions(ipw);
ipw.decreaseIndent();
ipw.println();
+ ipw.println("Frozen UIDs:");
+ ipw.increaseIndent();
+ ipw.println(mUidFrozen.toString());
+ ipw.decreaseIndent();
+ ipw.println();
+
if (dumpConstants) {
mConstants.dump(ipw);
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6c9f602..110d69e 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -2124,7 +2124,7 @@
frozen = opt.isFrozen();
final UidRecord uidRec = proc.getUidRecord();
- if (frozen && uidRec.areAllProcessesFrozen()) {
+ if (frozen && uidRec != null && uidRec.areAllProcessesFrozen()) {
uidRec.setFrozen(true);
mFreezeHandler.sendMessage(mFreezeHandler.obtainMessage(
UID_FROZEN_STATE_CHANGED_MSG, proc));
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 84a8099..a86c02d 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -349,6 +349,7 @@
private final ArrayList<UidRecord> mTmpBecameIdle = new ArrayList<UidRecord>();
private final ActiveUids mTmpUidRecords;
private final ArrayDeque<ProcessRecord> mTmpQueue;
+ private final ArraySet<ProcessRecord> mTmpProcessSet = new ArraySet<>();
private final ArraySet<ProcessRecord> mPendingProcessSet = new ArraySet<>();
private final ArraySet<ProcessRecord> mProcessesInCycle = new ArraySet<>();
@@ -3472,4 +3473,29 @@
CachedAppOptimizer.getUnfreezeReasonCodeFromOomAdjReason(oomAdjReason));
}
}
+
+ @GuardedBy("mService")
+ void unfreezeTemporarily(ProcessRecord app, @OomAdjuster.OomAdjReason int reason) {
+ if (!mCachedAppOptimizer.useFreezer()) {
+ return;
+ }
+
+ final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+ if (!opt.isFrozen() && !opt.isPendingFreeze()) {
+ return;
+ }
+
+ final ArrayList<ProcessRecord> processes = mTmpProcessList;
+ final ActiveUids uids = mTmpUidRecords;
+ mTmpProcessSet.add(app);
+ collectReachableProcessesLocked(mTmpProcessSet, processes, uids);
+ mTmpProcessSet.clear();
+ // Now processes contains app's downstream and app
+ final int size = processes.size();
+ for (int i = 0; i < size; i++) {
+ ProcessRecord proc = processes.get(i);
+ mCachedAppOptimizer.unfreezeTemporarily(proc, reason);
+ }
+ processes.clear();
+ }
}
diff --git a/services/core/java/com/android/server/am/ProcessRecord.java b/services/core/java/com/android/server/am/ProcessRecord.java
index 50d00b4..e651e23 100644
--- a/services/core/java/com/android/server/am/ProcessRecord.java
+++ b/services/core/java/com/android/server/am/ProcessRecord.java
@@ -1050,6 +1050,11 @@
return mState.isCached();
}
+ @GuardedBy(anyOf = {"mService", "mProcLock"})
+ public boolean hasForegroundActivities() {
+ return mState.hasForegroundActivities();
+ }
+
boolean hasActivities() {
return mWindowProcessController.hasActivities();
}
diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java
index 965a07b..81ba4b8 100644
--- a/services/core/java/com/android/server/appop/AppOpsService.java
+++ b/services/core/java/com/android/server/appop/AppOpsService.java
@@ -1548,19 +1548,20 @@
}
private void enforceGetAppOpsStatsPermissionIfNeeded(int uid, String packageName) {
- final int callingUid = Binder.getCallingUid();
// We get to access everything
- if (callingUid == Process.myPid()) {
+ final int callingPid = Binder.getCallingPid();
+ if (callingPid == Process.myPid()) {
return;
}
// Apps can access their own data
+ final int callingUid = Binder.getCallingUid();
if (uid == callingUid && packageName != null
&& checkPackage(uid, packageName) == MODE_ALLOWED) {
return;
}
// Otherwise, you need a permission...
- mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS,
- Binder.getCallingPid(), callingUid, null);
+ mContext.enforcePermission(android.Manifest.permission.GET_APP_OPS_STATS, callingPid,
+ callingUid, null);
}
/**
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
new file mode 100644
index 0000000..f0e4b0f5
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -0,0 +1,76 @@
+/*
+ * 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 android.annotation.AnyThread;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodSubtype;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.inputmethod.InputMethodSubtypeHandle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+final class HardwareKeyboardShortcutController {
+ @GuardedBy("ImfLock.class")
+ private final ArrayList<InputMethodSubtypeHandle> mSubtypeHandles = new ArrayList<>();
+
+ @GuardedBy("ImfLock.class")
+ void reset(@NonNull InputMethodUtils.InputMethodSettings settings) {
+ mSubtypeHandles.clear();
+ for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
+ if (!imi.shouldShowInInputMethodPicker()) {
+ continue;
+ }
+ final List<InputMethodSubtype> subtypes =
+ settings.getEnabledInputMethodSubtypeListLocked(imi, true);
+ if (subtypes.isEmpty()) {
+ mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, null));
+ } else {
+ for (final InputMethodSubtype subtype : subtypes) {
+ if (subtype.isSuitableForPhysicalKeyboardLayoutMapping()) {
+ mSubtypeHandles.add(InputMethodSubtypeHandle.of(imi, subtype));
+ }
+ }
+ }
+ }
+ }
+
+ @AnyThread
+ @Nullable
+ static <T> T getNeighborItem(@NonNull List<T> list, @NonNull T value, boolean next) {
+ final int size = list.size();
+ for (int i = 0; i < size; ++i) {
+ if (Objects.equals(value, list.get(i))) {
+ final int nextIndex = (i + (next ? 1 : -1) + size) % size;
+ return list.get(nextIndex);
+ }
+ }
+ return null;
+ }
+
+ @GuardedBy("ImfLock.class")
+ @Nullable
+ InputMethodSubtypeHandle onSubtypeSwitch(
+ @NonNull InputMethodSubtypeHandle currentImeAndSubtype, boolean forward) {
+ return getNeighborItem(mSubtypeHandles, currentImeAndSubtype, forward);
+ }
+}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index b440208..2433211 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -316,6 +316,8 @@
final ArrayList<InputMethodInfo> mMethodList = new ArrayList<>();
final ArrayMap<String, InputMethodInfo> mMethodMap = new ArrayMap<>();
final InputMethodSubtypeSwitchingController mSwitchingController;
+ final HardwareKeyboardShortcutController mHardwareKeyboardShortcutController =
+ new HardwareKeyboardShortcutController();
/**
* Tracks how many times {@link #mMethodMap} was updated.
@@ -1731,6 +1733,7 @@
AdditionalSubtypeUtils.load(mAdditionalSubtypeMap, userId);
mSwitchingController =
InputMethodSubtypeSwitchingController.createInstanceLocked(mSettings, context);
+ mHardwareKeyboardShortcutController.reset(mSettings);
mMenuController = new InputMethodMenuController(this);
mBindingController =
bindingControllerForTesting != null
@@ -3268,6 +3271,7 @@
// TODO: Make sure that mSwitchingController and mSettings are sharing the
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ mHardwareKeyboardShortcutController.reset(mSettings);
sendOnNavButtonFlagsChangedLocked();
}
@@ -5293,6 +5297,7 @@
// TODO: Make sure that mSwitchingController and mSettings are sharing the
// the same enabled IMEs list.
mSwitchingController.resetCircularListLocked(mContext);
+ mHardwareKeyboardShortcutController.reset(mSettings);
sendOnNavButtonFlagsChangedLocked();
@@ -5827,10 +5832,37 @@
@Override
public void switchKeyboardLayout(int direction) {
synchronized (ImfLock.class) {
- if (direction > 0) {
- switchToNextInputMethodLocked(null /* token */, true /* onlyCurrentIme */);
- } else {
- // TODO(b/258853866): Support backwards switching.
+ final InputMethodInfo currentImi = mMethodMap.get(getSelectedMethodIdLocked());
+ if (currentImi == null) {
+ return;
+ }
+ final InputMethodSubtypeHandle currentSubtypeHandle =
+ InputMethodSubtypeHandle.of(currentImi, mCurrentSubtype);
+ final InputMethodSubtypeHandle nextSubtypeHandle =
+ mHardwareKeyboardShortcutController.onSubtypeSwitch(currentSubtypeHandle,
+ direction > 0);
+ if (nextSubtypeHandle == null) {
+ return;
+ }
+ final InputMethodInfo nextImi = mMethodMap.get(nextSubtypeHandle.getImeId());
+ if (nextImi == null) {
+ return;
+ }
+
+ final int subtypeCount = nextImi.getSubtypeCount();
+ if (subtypeCount == 0) {
+ if (nextSubtypeHandle.equals(InputMethodSubtypeHandle.of(nextImi, null))) {
+ setInputMethodLocked(nextImi.getId(), NOT_A_SUBTYPE_ID);
+ }
+ return;
+ }
+
+ for (int i = 0; i < subtypeCount; ++i) {
+ if (nextSubtypeHandle.equals(
+ InputMethodSubtypeHandle.of(nextImi, nextImi.getSubtypeAt(i)))) {
+ setInputMethodLocked(nextImi.getId(), i);
+ return;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 65dcec7..9ec9ff5 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -656,7 +656,6 @@
private ConditionProviders mConditionProviders;
private NotificationUsageStats mUsageStats;
private boolean mLockScreenAllowSecureNotifications = true;
- boolean mAllowFgsDismissal = false;
boolean mSystemExemptFromDismissal = false;
private static final int MY_UID = Process.myUid();
@@ -2581,19 +2580,9 @@
for (String name : properties.getKeyset()) {
if (SystemUiDeviceConfigFlags.NAS_DEFAULT_SERVICE.equals(name)) {
mAssistants.resetDefaultAssistantsIfNecessary();
- } else if (SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED.equals(name)) {
- String value = properties.getString(name, null);
- if ("true".equals(value)) {
- mAllowFgsDismissal = true;
- } else if ("false".equals(value)) {
- mAllowFgsDismissal = false;
- }
}
}
};
- mAllowFgsDismissal = DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
mSystemExemptFromDismissal = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_DEVICE_POLICY_MANAGER,
/* name= */ "application_exemptions",
@@ -7736,9 +7725,6 @@
// flags are set.
if ((notification.flags & FLAG_FOREGROUND_SERVICE) != 0) {
notification.flags |= FLAG_NO_CLEAR;
- if (!mAllowFgsDismissal) {
- notification.flags |= FLAG_ONGOING_EVENT;
- }
}
mRankingHelper.extractSignals(r);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 927a722..ab9d1cf 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -278,6 +278,7 @@
private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
static final int WRITE_USER_MSG = 1;
+ static final int WRITE_USER_LIST_MSG = 2;
static final int WRITE_USER_DELAY = 2*1000; // 2 seconds
private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;
@@ -321,7 +322,6 @@
private final Handler mHandler;
private final File mUsersDir;
- @GuardedBy("mPackagesLock")
private final File mUserListFile;
private final IBinder mUserRestrictionToken = new Binder();
@@ -3623,77 +3623,95 @@
mUpdatingSystemUserMode = true;
}
+
+ private ResilientAtomicFile getUserListFile() {
+ File tempBackup = new File(mUserListFile.getParent(), mUserListFile.getName() + ".backup");
+ File reserveCopy = new File(mUserListFile.getParent(),
+ mUserListFile.getName() + ".reservecopy");
+ int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+ return new ResilientAtomicFile(mUserListFile, tempBackup, reserveCopy, fileMode,
+ "user list", (priority, msg) -> {
+ Slog.e(LOG_TAG, msg);
+ // Something went wrong, schedule full rewrite.
+ scheduleWriteUserList();
+ });
+ }
+
@GuardedBy({"mPackagesLock"})
private void readUserListLP() {
- if (!mUserListFile.exists()) {
- fallbackToSingleUserLP();
- return;
- }
- FileInputStream fis = null;
- AtomicFile userListFile = new AtomicFile(mUserListFile);
- try {
- fis = userListFile.openRead();
- final TypedXmlPullParser parser = Xml.resolvePullParser(fis);
- int type;
- while ((type = parser.next()) != XmlPullParser.START_TAG
- && type != XmlPullParser.END_DOCUMENT) {
- // Skip
- }
+ try (ResilientAtomicFile file = getUserListFile()) {
+ FileInputStream fin = null;
+ try {
+ fin = file.openRead();
+ if (fin == null) {
+ Slog.e(LOG_TAG, "userlist.xml not found, fallback to single user");
+ fallbackToSingleUserLP();
+ return;
+ }
- if (type != XmlPullParser.START_TAG) {
- Slog.e(LOG_TAG, "Unable to read user list");
- fallbackToSingleUserLP();
- return;
- }
+ final TypedXmlPullParser parser = Xml.resolvePullParser(fin);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ // Skip
+ }
- mNextSerialNumber = -1;
- if (parser.getName().equals(TAG_USERS)) {
- mNextSerialNumber =
- parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
- mUserVersion =
- parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
- mUserTypeVersion =
- parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
- }
+ if (type != XmlPullParser.START_TAG) {
+ Slog.e(LOG_TAG, "Unable to read user list");
+ fallbackToSingleUserLP();
+ return;
+ }
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
- if (type == XmlPullParser.START_TAG) {
- final String name = parser.getName();
- if (name.equals(TAG_USER)) {
- UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+ mNextSerialNumber = -1;
+ if (parser.getName().equals(TAG_USERS)) {
+ mNextSerialNumber =
+ parser.getAttributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+ mUserVersion =
+ parser.getAttributeInt(null, ATTR_USER_VERSION, mUserVersion);
+ mUserTypeVersion =
+ parser.getAttributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+ }
- if (userData != null) {
- synchronized (mUsersLock) {
- mUsers.put(userData.info.id, userData);
- if (mNextSerialNumber < 0
- || mNextSerialNumber <= userData.info.id) {
- mNextSerialNumber = userData.info.id + 1;
- }
- }
- }
- } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
- while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
- && type != XmlPullParser.END_TAG) {
- if (type == XmlPullParser.START_TAG) {
- if (parser.getName().equals(TAG_RESTRICTIONS)) {
- synchronized (mGuestRestrictions) {
- UserRestrictionsUtils
- .readRestrictions(parser, mGuestRestrictions);
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
+ if (type == XmlPullParser.START_TAG) {
+ final String name = parser.getName();
+ if (name.equals(TAG_USER)) {
+ UserData userData = readUserLP(parser.getAttributeInt(null, ATTR_ID));
+
+ if (userData != null) {
+ synchronized (mUsersLock) {
+ mUsers.put(userData.info.id, userData);
+ if (mNextSerialNumber < 0
+ || mNextSerialNumber <= userData.info.id) {
+ mNextSerialNumber = userData.info.id + 1;
}
}
- break;
+ }
+ } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.END_TAG) {
+ if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_RESTRICTIONS)) {
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .readRestrictions(parser, mGuestRestrictions);
+ }
+ }
+ break;
+ }
}
}
}
}
- }
- updateUserIds();
- upgradeIfNecessaryLP();
- } catch (IOException | XmlPullParserException e) {
- fallbackToSingleUserLP();
- } finally {
- IoUtils.closeQuietly(fis);
+ updateUserIds();
+ upgradeIfNecessaryLP();
+ } catch (Exception e) {
+ // Remove corrupted file and retry.
+ file.failRead(fin, e);
+ readUserListLP();
+ return;
+ }
}
synchronized (mUsersLock) {
@@ -4099,6 +4117,18 @@
}
}
+ private void scheduleWriteUserList() {
+ if (DBG) {
+ debug("scheduleWriteUserList");
+ }
+ // No need to wrap it within a lock -- worst case, we'll just post the same message
+ // twice.
+ if (!mHandler.hasMessages(WRITE_USER_LIST_MSG)) {
+ Message msg = mHandler.obtainMessage(WRITE_USER_LIST_MSG);
+ mHandler.sendMessageDelayed(msg, WRITE_USER_DELAY);
+ }
+ }
+
private void scheduleWriteUser(UserData userData) {
if (DBG) {
debug("scheduleWriteUser");
@@ -4111,20 +4141,37 @@
}
}
+ private ResilientAtomicFile getUserFile(int userId) {
+ File file = new File(mUsersDir, userId + XML_SUFFIX);
+ File tempBackup = new File(mUsersDir, userId + XML_SUFFIX + ".backup");
+ File reserveCopy = new File(mUsersDir, userId + XML_SUFFIX + ".reservecopy");
+ int fileMode = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IXOTH;
+ return new ResilientAtomicFile(file, tempBackup, reserveCopy, fileMode,
+ "user info", (priority, msg) -> {
+ Slog.e(LOG_TAG, msg);
+ // Something went wrong, schedule full rewrite.
+ UserData userData = getUserDataNoChecks(userId);
+ if (userData != null) {
+ scheduleWriteUser(userData);
+ }
+ });
+ }
+
@GuardedBy({"mPackagesLock"})
private void writeUserLP(UserData userData) {
if (DBG) {
debug("writeUserLP " + userData);
}
- FileOutputStream fos = null;
- AtomicFile userFile = new AtomicFile(new File(mUsersDir, userData.info.id + XML_SUFFIX));
- try {
- fos = userFile.startWrite();
- writeUserLP(userData, fos);
- userFile.finishWrite(fos);
- } catch (Exception ioe) {
- Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
- userFile.failWrite(fos);
+ try (ResilientAtomicFile userFile = getUserFile(userData.info.id)) {
+ FileOutputStream fos = null;
+ try {
+ fos = userFile.startWrite();
+ writeUserLP(userData, fos);
+ userFile.finishWrite(fos);
+ } catch (Exception ioe) {
+ Slog.e(LOG_TAG, "Error writing user info " + userData.info.id, ioe);
+ userFile.failWrite(fos);
+ }
}
}
@@ -4253,65 +4300,71 @@
if (DBG) {
debug("writeUserList");
}
- FileOutputStream fos = null;
- AtomicFile userListFile = new AtomicFile(mUserListFile);
- try {
- fos = userListFile.startWrite();
- final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- serializer.startTag(null, TAG_USERS);
- serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
- serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
- serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+ try (ResilientAtomicFile file = getUserListFile()) {
+ FileOutputStream fos = null;
+ try {
+ fos = file.startWrite();
- serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
- synchronized (mGuestRestrictions) {
- UserRestrictionsUtils
- .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
- }
- serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
- int[] userIdsToWrite;
- synchronized (mUsersLock) {
- userIdsToWrite = new int[mUsers.size()];
- for (int i = 0; i < userIdsToWrite.length; i++) {
- UserInfo user = mUsers.valueAt(i).info;
- userIdsToWrite[i] = user.id;
+ final TypedXmlSerializer serializer = Xml.resolveSerializer(fos);
+ serializer.startDocument(null, true);
+ serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output",
+ true);
+
+ serializer.startTag(null, TAG_USERS);
+ serializer.attributeInt(null, ATTR_NEXT_SERIAL_NO, mNextSerialNumber);
+ serializer.attributeInt(null, ATTR_USER_VERSION, mUserVersion);
+ serializer.attributeInt(null, ATTR_USER_TYPE_VERSION, mUserTypeVersion);
+
+ serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
+ synchronized (mGuestRestrictions) {
+ UserRestrictionsUtils
+ .writeRestrictions(serializer, mGuestRestrictions, TAG_RESTRICTIONS);
}
- }
- for (int id : userIdsToWrite) {
- serializer.startTag(null, TAG_USER);
- serializer.attributeInt(null, ATTR_ID, id);
- serializer.endTag(null, TAG_USER);
- }
+ serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
+ int[] userIdsToWrite;
+ synchronized (mUsersLock) {
+ userIdsToWrite = new int[mUsers.size()];
+ for (int i = 0; i < userIdsToWrite.length; i++) {
+ UserInfo user = mUsers.valueAt(i).info;
+ userIdsToWrite[i] = user.id;
+ }
+ }
+ for (int id : userIdsToWrite) {
+ serializer.startTag(null, TAG_USER);
+ serializer.attributeInt(null, ATTR_ID, id);
+ serializer.endTag(null, TAG_USER);
+ }
- serializer.endTag(null, TAG_USERS);
+ serializer.endTag(null, TAG_USERS);
- serializer.endDocument();
- userListFile.finishWrite(fos);
- } catch (Exception e) {
- userListFile.failWrite(fos);
- Slog.e(LOG_TAG, "Error writing user list");
+ serializer.endDocument();
+ file.finishWrite(fos);
+ } catch (Exception e) {
+ Slog.e(LOG_TAG, "Error writing user list", e);
+ file.failWrite(fos);
+ }
}
}
@GuardedBy({"mPackagesLock"})
private UserData readUserLP(int id) {
- FileInputStream fis = null;
- try {
- AtomicFile userFile =
- new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX));
- fis = userFile.openRead();
- return readUserLP(id, fis);
- } catch (IOException ioe) {
- Slog.e(LOG_TAG, "Error reading user list");
- } catch (XmlPullParserException pe) {
- Slog.e(LOG_TAG, "Error reading user list");
- } finally {
- IoUtils.closeQuietly(fis);
+ try (ResilientAtomicFile file = getUserFile(id)) {
+ FileInputStream fis = null;
+ try {
+ fis = file.openRead();
+ if (fis == null) {
+ Slog.e(LOG_TAG, "User info not found, returning null, user id: " + id);
+ return null;
+ }
+ return readUserLP(id, fis);
+ } catch (Exception e) {
+ // Remove corrupted file and retry.
+ Slog.e(LOG_TAG, "Error reading user info, user id: " + id);
+ file.failRead(fis, e);
+ return readUserLP(id);
+ }
}
- return null;
}
@GuardedBy({"mPackagesLock"})
@@ -5805,9 +5858,8 @@
synchronized (mPackagesLock) {
writeUserListLP();
}
- // Remove user file
- AtomicFile userFile = new AtomicFile(new File(mUsersDir, userId + XML_SUFFIX));
- userFile.delete();
+ // Remove user file(s)
+ getUserFile(userId).delete();
updateUserIds();
if (RELEASE_DELETED_USER_ID) {
synchronized (mUsersLock) {
@@ -6770,6 +6822,13 @@
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
+ case WRITE_USER_LIST_MSG: {
+ removeMessages(WRITE_USER_LIST_MSG);
+ synchronized (mPackagesLock) {
+ writeUserListLP();
+ }
+ break;
+ }
case WRITE_USER_MSG:
removeMessages(WRITE_USER_MSG, msg.obj);
synchronized (mPackagesLock) {
@@ -6782,6 +6841,7 @@
+ ", it was probably removed before handler could handle it");
}
}
+ break;
}
}
}
diff --git a/services/core/java/com/android/server/pm/VerifyingSession.java b/services/core/java/com/android/server/pm/VerifyingSession.java
index f340f93..c9ebeae 100644
--- a/services/core/java/com/android/server/pm/VerifyingSession.java
+++ b/services/core/java/com/android/server/pm/VerifyingSession.java
@@ -652,20 +652,33 @@
private boolean isAdbVerificationEnabled(PackageInfoLite pkgInfoLite, int userId,
boolean requestedDisableVerification) {
+ boolean verifierIncludeAdb = android.provider.Settings.Global.getInt(
+ mPm.mContext.getContentResolver(),
+ android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
if (mPm.isUserRestricted(userId, UserManager.ENSURE_VERIFY_APPS)) {
+ if (!verifierIncludeAdb) {
+ Slog.w(TAG, "Force verification of ADB install because of user restriction.");
+ }
return true;
}
- // Check if the developer wants to skip verification for ADB installs
+
+ // Check if the verification disabled globally, first.
+ if (!verifierIncludeAdb) {
+ return false;
+ }
+
+ // Check if the developer wants to skip verification for ADB installs.
if (requestedDisableVerification) {
if (!packageExists(pkgInfoLite.packageName)) {
- // Always verify fresh install
+ // Always verify fresh install.
return true;
}
- // Only skip when apk is debuggable
+ // Only skip when apk is debuggable.
return !pkgInfoLite.debuggable;
}
- return android.provider.Settings.Global.getInt(mPm.mContext.getContentResolver(),
- android.provider.Settings.Global.PACKAGE_VERIFIER_INCLUDE_ADB, 1) != 0;
+
+ return true;
}
/**
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index b56e5c9..c5f939a 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -64,6 +64,7 @@
import android.permission.PermissionCheckerManager;
import android.permission.PermissionManager;
import android.permission.PermissionManagerInternal;
+import android.service.voice.VoiceInteractionManagerInternal;
import android.util.ArrayMap;
import android.util.Slog;
import android.util.SparseArray;
@@ -969,12 +970,13 @@
// the private data in your process; or by you explicitly calling to another
// app passing the source, in which case you must trust the other side;
- final int callingUid = Binder.getCallingUid();
- if (source.getUid() != callingUid && mContext.checkPermission(
+ final int callingUid = resolveUid(Binder.getCallingUid());
+ final int sourceUid = resolveUid(source.getUid());
+ if (sourceUid != callingUid && mContext.checkPermission(
Manifest.permission.UPDATE_APP_OPS_STATS, /*pid*/ -1, callingUid)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Cannot register attribution source for uid:"
- + source.getUid() + " from uid:" + callingUid);
+ + sourceUid + " from uid:" + callingUid);
}
final PackageManagerInternal packageManagerInternal = LocalServices.getService(
@@ -983,10 +985,10 @@
// TODO(b/234653108): Clean up this UID/package & cross-user check.
// If calling from the system process, allow registering attribution for package from
// any user
- int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? source.getUid()
+ int userId = UserHandle.getUserId((callingUid == Process.SYSTEM_UID ? sourceUid
: callingUid));
if (packageManagerInternal.getPackageUid(source.getPackageName(), 0, userId)
- != source.getUid()) {
+ != sourceUid) {
throw new SecurityException("Cannot register attribution source for package:"
+ source.getPackageName() + " from uid:" + callingUid);
}
@@ -1012,6 +1014,21 @@
return false;
}
}
+
+ private int resolveUid(int uid) {
+ final VoiceInteractionManagerInternal vimi = LocalServices
+ .getService(VoiceInteractionManagerInternal.class);
+ if (vimi == null) {
+ return uid;
+ }
+ final VoiceInteractionManagerInternal.HotwordDetectionServiceIdentity
+ hotwordDetectionServiceIdentity = vimi.getHotwordDetectionServiceIdentity();
+ if (hotwordDetectionServiceIdentity != null
+ && uid == hotwordDetectionServiceIdentity.getIsolatedUid()) {
+ return hotwordDetectionServiceIdentity.getOwnerUid();
+ }
+ return uid;
+ }
}
/**
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 54f87d0..8165958 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -729,7 +729,7 @@
mAutofillManagerInternal.onBackKeyPressed();
break;
case MSG_SYSTEM_KEY_PRESS:
- sendSystemKeyToStatusBar(msg.arg1);
+ sendSystemKeyToStatusBar((KeyEvent) msg.obj);
break;
case MSG_HANDLE_ALL_APPS:
launchAllAppsAction();
@@ -949,7 +949,7 @@
final boolean handledByPowerManager = mPowerManagerInternal.interceptPowerKeyDown(event);
// Inform the StatusBar; but do not allow it to consume the event.
- sendSystemKeyToStatusBarAsync(event.getKeyCode());
+ sendSystemKeyToStatusBarAsync(event);
// If the power key has still not yet been handled, then detect short
// press, long press, or multi press and decide what to do.
@@ -3001,7 +3001,11 @@
break;
case KeyEvent.KEYCODE_N:
if (down && event.isMetaPressed()) {
- toggleNotificationPanel();
+ if (event.isCtrlPressed()) {
+ sendSystemKeyToStatusBarAsync(event);
+ } else {
+ toggleNotificationPanel();
+ }
return key_consumed;
}
break;
@@ -3569,14 +3573,16 @@
@Override
public int applyKeyguardOcclusionChange() {
- if (mKeyguardOccludedChanged) {
- if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded changed occluded="
- + mPendingKeyguardOccluded);
- if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
- return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
- }
+ if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
+ + mPendingKeyguardOccluded);
+
+ // TODO(b/276433230): Explicitly save before/after for occlude state in each
+ // Transition so we don't need to update SysUI every time.
+ if (setKeyguardOccludedLw(mPendingKeyguardOccluded)) {
+ return FINISH_LAYOUT_REDO_LAYOUT | FINISH_LAYOUT_REDO_WALLPAPER;
+ } else {
+ return 0;
}
- return 0;
}
/**
@@ -3854,6 +3860,7 @@
private boolean setKeyguardOccludedLw(boolean isOccluded) {
if (DEBUG_KEYGUARD) Slog.d(TAG, "setKeyguardOccluded occluded=" + isOccluded);
mKeyguardOccludedChanged = false;
+ mPendingKeyguardOccluded = isOccluded;
mKeyguardDelegate.setOccluded(isOccluded, true /* notify */);
return mKeyguardDelegate.isShowing();
}
@@ -4119,7 +4126,7 @@
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_MUTE: {
if (down) {
- sendSystemKeyToStatusBarAsync(event.getKeyCode());
+ sendSystemKeyToStatusBarAsync(event);
NotificationManager nm = getNotificationService();
if (nm != null && !mHandleVolumeKeysInWM) {
@@ -4397,7 +4404,7 @@
case KeyEvent.KEYCODE_STYLUS_BUTTON_TERTIARY:
case KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL: {
if (down && mStylusButtonsEnabled) {
- sendSystemKeyToStatusBarAsync(keyCode);
+ sendSystemKeyToStatusBarAsync(event);
}
result &= ~ACTION_PASS_TO_USER;
break;
@@ -4494,7 +4501,7 @@
if (!mAccessibilityManager.isEnabled()
|| !mAccessibilityManager.sendFingerprintGesture(event.getKeyCode())) {
if (mSystemNavigationKeysEnabled) {
- sendSystemKeyToStatusBarAsync(event.getKeyCode());
+ sendSystemKeyToStatusBarAsync(event);
}
}
}
@@ -4503,11 +4510,11 @@
/**
* Notify the StatusBar that a system key was pressed.
*/
- private void sendSystemKeyToStatusBar(int keyCode) {
+ private void sendSystemKeyToStatusBar(KeyEvent key) {
IStatusBarService statusBar = getStatusBarService();
if (statusBar != null) {
try {
- statusBar.handleSystemKey(keyCode);
+ statusBar.handleSystemKey(key);
} catch (RemoteException e) {
// Oh well.
}
@@ -4517,8 +4524,8 @@
/**
* Notify the StatusBar that a system key was pressed without blocking the current thread.
*/
- private void sendSystemKeyToStatusBarAsync(int keyCode) {
- Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyCode, 0);
+ private void sendSystemKeyToStatusBarAsync(KeyEvent keyEvent) {
+ Message message = mHandler.obtainMessage(MSG_SYSTEM_KEY_PRESS, keyEvent);
message.setAsynchronous(true);
mHandler.sendMessage(message);
}
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
index d55fbc2..231ffc6 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
@@ -20,6 +20,7 @@
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
+import android.app.ActivityManager;
import android.content.Context;
import android.os.Handler;
import android.os.HandlerExecutor;
@@ -27,11 +28,10 @@
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.util.IndentingPrintWriter;
-import android.util.IntArray;
-import android.util.LongSparseArray;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.TimeSparseArray;
import android.util.TimeUtils;
@@ -59,7 +59,7 @@
private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
@VisibleForTesting
static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
- private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.MINUTES.toMillis(2);
+ private static final long WAKEUP_WRITE_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
private final Handler mHandler;
private final IrqDeviceMap mIrqDeviceMap;
@@ -69,10 +69,15 @@
@VisibleForTesting
final TimeSparseArray<Wakeup> mWakeupEvents = new TimeSparseArray<>();
+
+ /* Maps timestamp -> {subsystem -> {uid -> procState}} */
@VisibleForTesting
- final TimeSparseArray<SparseArray<SparseBooleanArray>> mWakeupAttribution =
+ final TimeSparseArray<SparseArray<SparseIntArray>> mWakeupAttribution =
new TimeSparseArray<>();
+ final SparseIntArray mUidProcStates = new SparseIntArray();
+ private final SparseIntArray mReusableUidProcStates = new SparseIntArray(4);
+
public CpuWakeupStats(Context context, int mapRes, Handler handler) {
mIrqDeviceMap = IrqDeviceMap.getInstance(context, mapRes);
mHandler = handler;
@@ -102,13 +107,14 @@
FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN,
FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN,
null,
- wakeupToLog.mElapsedMillis);
+ wakeupToLog.mElapsedMillis,
+ null);
Trace.instantForTrack(Trace.TRACE_TAG_POWER, TRACE_TRACK_WAKEUP_ATTRIBUTION,
wakeupToLog.mElapsedMillis + " --");
return;
}
- final SparseArray<SparseBooleanArray> wakeupAttribution = mWakeupAttribution.get(
+ final SparseArray<SparseIntArray> wakeupAttribution = mWakeupAttribution.get(
wakeupToLog.mElapsedMillis);
if (wakeupAttribution == null) {
// This is not expected but can theoretically happen in extreme situations, e.g. if we
@@ -121,24 +127,28 @@
for (int i = 0; i < wakeupAttribution.size(); i++) {
final int subsystem = wakeupAttribution.keyAt(i);
- final SparseBooleanArray uidMap = wakeupAttribution.valueAt(i);
+ final SparseIntArray uidProcStates = wakeupAttribution.valueAt(i);
final int[] uids;
- if (uidMap == null || uidMap.size() == 0) {
- uids = new int[0];
+ final int[] procStatesProto;
+
+ if (uidProcStates == null || uidProcStates.size() == 0) {
+ uids = procStatesProto = new int[0];
} else {
- final IntArray tmp = new IntArray(uidMap.size());
- for (int j = 0; j < uidMap.size(); j++) {
- if (uidMap.valueAt(j)) {
- tmp.add(uidMap.keyAt(j));
- }
+ final int numUids = uidProcStates.size();
+ uids = new int[numUids];
+ procStatesProto = new int[numUids];
+ for (int j = 0; j < numUids; j++) {
+ uids[j] = uidProcStates.keyAt(j);
+ procStatesProto[j] = ActivityManager.processStateAmToProto(
+ uidProcStates.valueAt(j));
}
- uids = tmp.toArray();
}
FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED,
FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ,
subsystemToStatsReason(subsystem),
uids,
- wakeupToLog.mElapsedMillis);
+ wakeupToLog.mElapsedMillis,
+ procStatesProto);
if (Trace.isTagEnabled(Trace.TRACE_TAG_POWER)) {
if (i == 0) {
@@ -154,6 +164,20 @@
traceEventBuilder.toString().trim());
}
+ /**
+ * Clean up data for a uid that is being removed.
+ */
+ public synchronized void onUidRemoved(int uid) {
+ mUidProcStates.delete(uid);
+ }
+
+ /**
+ * Notes a procstate change for the given uid to maintain the mapping internally.
+ */
+ public synchronized void noteUidProcessState(int uid, int state) {
+ mUidProcStates.put(uid, state);
+ }
+
/** Notes a wakeup reason as reported by SuspendControlService to battery stats. */
public synchronized void noteWakeupTimeAndReason(long elapsedRealtime, long uptime,
String rawReason) {
@@ -184,8 +208,17 @@
/** Notes a waking activity that could have potentially woken up the CPU. */
public synchronized void noteWakingActivity(int subsystem, long elapsedRealtime, int... uids) {
- if (!attemptAttributionWith(subsystem, elapsedRealtime, uids)) {
- mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime, uids);
+ if (uids == null) {
+ return;
+ }
+ mReusableUidProcStates.clear();
+ for (int i = 0; i < uids.length; i++) {
+ mReusableUidProcStates.put(uids[i],
+ mUidProcStates.get(uids[i], ActivityManager.PROCESS_STATE_UNKNOWN));
+ }
+ if (!attemptAttributionWith(subsystem, elapsedRealtime, mReusableUidProcStates)) {
+ mRecentWakingActivity.recordActivity(subsystem, elapsedRealtime,
+ mReusableUidProcStates);
}
}
@@ -196,7 +229,7 @@
return;
}
- SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
+ SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(wakeup.mElapsedMillis);
if (attribution == null) {
attribution = new SparseArray<>();
mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
@@ -210,14 +243,14 @@
final long startTime = wakeup.mElapsedMillis - WAKEUP_REASON_HALF_WINDOW_MS;
final long endTime = wakeup.mElapsedMillis + WAKEUP_REASON_HALF_WINDOW_MS;
- final SparseBooleanArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
+ final SparseIntArray uidsToBlame = mRecentWakingActivity.removeBetween(subsystem,
startTime, endTime);
attribution.put(subsystem, uidsToBlame);
}
}
private synchronized boolean attemptAttributionWith(int subsystem, long activityElapsed,
- int... uids) {
+ SparseIntArray uidProcStates) {
final int startIdx = mWakeupEvents.closestIndexOnOrAfter(
activityElapsed - WAKEUP_REASON_HALF_WINDOW_MS);
final int endIdx = mWakeupEvents.closestIndexOnOrBefore(
@@ -233,19 +266,19 @@
if (subsystems.get(subsystem)) {
// We don't expect more than one wakeup to be found within such a short window, so
// just attribute this one and exit
- SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
wakeup.mElapsedMillis);
if (attribution == null) {
attribution = new SparseArray<>();
mWakeupAttribution.put(wakeup.mElapsedMillis, attribution);
}
- SparseBooleanArray uidsToBlame = attribution.get(subsystem);
+ SparseIntArray uidsToBlame = attribution.get(subsystem);
if (uidsToBlame == null) {
- uidsToBlame = new SparseBooleanArray(uids.length);
- attribution.put(subsystem, uidsToBlame);
- }
- for (final int uid : uids) {
- uidsToBlame.put(uid, true);
+ attribution.put(subsystem, uidProcStates.clone());
+ } else {
+ for (int i = 0; i < uidProcStates.size(); i++) {
+ uidsToBlame.put(uidProcStates.keyAt(i), uidProcStates.valueAt(i));
+ }
}
return true;
}
@@ -267,6 +300,19 @@
mRecentWakingActivity.dump(pw, nowElapsed);
pw.println();
+ pw.println("Current proc-state map (" + mUidProcStates.size() + "):");
+ pw.increaseIndent();
+ for (int i = 0; i < mUidProcStates.size(); i++) {
+ if (i > 0) {
+ pw.print(", ");
+ }
+ UserHandle.formatUid(pw, mUidProcStates.keyAt(i));
+ pw.print(":" + ActivityManager.procStateToString(mUidProcStates.valueAt(i)));
+ }
+ pw.println();
+ pw.decreaseIndent();
+ pw.println();
+
final SparseLongArray attributionStats = new SparseLongArray();
pw.println("Wakeup events:");
pw.increaseIndent();
@@ -278,7 +324,7 @@
final Wakeup wakeup = mWakeupEvents.valueAt(i);
pw.println(wakeup);
pw.print("Attribution: ");
- final SparseArray<SparseBooleanArray> attribution = mWakeupAttribution.get(
+ final SparseArray<SparseIntArray> attribution = mWakeupAttribution.get(
wakeup.mElapsedMillis);
if (attribution == null) {
pw.println("N/A");
@@ -292,15 +338,17 @@
int attributed = IntPair.first(counters);
final int total = IntPair.second(counters) + 1;
- pw.print("subsystem: " + subsystemToString(attribution.keyAt(subsystemIdx)));
- pw.print(", uids: [");
- final SparseBooleanArray uids = attribution.valueAt(subsystemIdx);
- if (uids != null) {
- for (int uidIdx = 0; uidIdx < uids.size(); uidIdx++) {
+ pw.print(subsystemToString(attribution.keyAt(subsystemIdx)));
+ pw.print(" [");
+ final SparseIntArray uidProcStates = attribution.valueAt(subsystemIdx);
+ if (uidProcStates != null) {
+ for (int uidIdx = 0; uidIdx < uidProcStates.size(); uidIdx++) {
if (uidIdx > 0) {
pw.print(", ");
}
- UserHandle.formatUid(pw, uids.keyAt(uidIdx));
+ UserHandle.formatUid(pw, uidProcStates.keyAt(uidIdx));
+ pw.print(" " + ActivityManager.procStateToString(
+ uidProcStates.valueAt(uidIdx)));
}
attributed++;
}
@@ -330,29 +378,39 @@
pw.println();
}
+ /**
+ * This class stores recent unattributed activity history per subsystem.
+ * The activity is stored as a mapping of subsystem to timestamp to uid to procstate.
+ */
private static final class WakingActivityHistory {
private static final long WAKING_ACTIVITY_RETENTION_MS = TimeUnit.MINUTES.toMillis(10);
- private SparseArray<TimeSparseArray<SparseBooleanArray>> mWakingActivity =
+ private SparseArray<TimeSparseArray<SparseIntArray>> mWakingActivity =
new SparseArray<>();
- void recordActivity(int subsystem, long elapsedRealtime, int... uids) {
- if (uids == null) {
+ void recordActivity(int subsystem, long elapsedRealtime, SparseIntArray uidProcStates) {
+ if (uidProcStates == null) {
return;
}
- TimeSparseArray<SparseBooleanArray> wakingActivity = mWakingActivity.get(subsystem);
+ TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.get(subsystem);
if (wakingActivity == null) {
wakingActivity = new TimeSparseArray<>();
mWakingActivity.put(subsystem, wakingActivity);
}
- SparseBooleanArray uidsToBlame = wakingActivity.get(elapsedRealtime);
+ final SparseIntArray uidsToBlame = wakingActivity.get(elapsedRealtime);
if (uidsToBlame == null) {
- uidsToBlame = new SparseBooleanArray(uids.length);
+ wakingActivity.put(elapsedRealtime, uidProcStates.clone());
+ } else {
+ for (int i = 0; i < uidProcStates.size(); i++) {
+ final int uid = uidProcStates.keyAt(i);
+ // Just in case there are duplicate uids reported with the same timestamp,
+ // keep the processState which was reported first.
+ if (uidsToBlame.indexOfKey(uid) < 0) {
+ uidsToBlame.put(uid, uidProcStates.valueAt(i));
+ }
+ }
wakingActivity.put(elapsedRealtime, uidsToBlame);
}
- for (int i = 0; i < uids.length; i++) {
- uidsToBlame.put(uids[i], true);
- }
// Limit activity history per subsystem to the last WAKING_ACTIVITY_RETENTION_MS.
// Note that the last activity is always present, even if it occurred before
// WAKING_ACTIVITY_RETENTION_MS.
@@ -365,7 +423,7 @@
void clearAllBefore(long elapsedRealtime) {
for (int subsystemIdx = mWakingActivity.size() - 1; subsystemIdx >= 0; subsystemIdx--) {
- final TimeSparseArray<SparseBooleanArray> activityPerSubsystem =
+ final TimeSparseArray<SparseIntArray> activityPerSubsystem =
mWakingActivity.valueAt(subsystemIdx);
final int endIdx = activityPerSubsystem.closestIndexOnOrBefore(elapsedRealtime);
for (int removeIdx = endIdx; removeIdx >= 0; removeIdx--) {
@@ -377,20 +435,20 @@
}
}
- SparseBooleanArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
- final SparseBooleanArray uidsToReturn = new SparseBooleanArray();
+ SparseIntArray removeBetween(int subsystem, long startElapsed, long endElapsed) {
+ final SparseIntArray uidsToReturn = new SparseIntArray();
- final TimeSparseArray<SparseBooleanArray> activityForSubsystem =
+ final TimeSparseArray<SparseIntArray> activityForSubsystem =
mWakingActivity.get(subsystem);
if (activityForSubsystem != null) {
final int startIdx = activityForSubsystem.closestIndexOnOrAfter(startElapsed);
final int endIdx = activityForSubsystem.closestIndexOnOrBefore(endElapsed);
for (int i = endIdx; i >= startIdx; i--) {
- final SparseBooleanArray uidsForTime = activityForSubsystem.valueAt(i);
+ final SparseIntArray uidsForTime = activityForSubsystem.valueAt(i);
for (int j = 0; j < uidsForTime.size(); j++) {
- if (uidsForTime.valueAt(j)) {
- uidsToReturn.put(uidsForTime.keyAt(j), true);
- }
+ // In case the same uid appears in different uidsForTime maps, there is no
+ // good way to choose one processState, so just arbitrarily pick any.
+ uidsToReturn.put(uidsForTime.keyAt(j), uidsForTime.valueAt(j));
}
}
// More efficient to remove in a separate loop as it avoids repeatedly calling gc().
@@ -409,25 +467,23 @@
pw.increaseIndent();
for (int i = 0; i < mWakingActivity.size(); i++) {
pw.println("Subsystem " + subsystemToString(mWakingActivity.keyAt(i)) + ":");
- final LongSparseArray<SparseBooleanArray> wakingActivity =
- mWakingActivity.valueAt(i);
+ final TimeSparseArray<SparseIntArray> wakingActivity = mWakingActivity.valueAt(i);
if (wakingActivity == null) {
continue;
}
pw.increaseIndent();
for (int j = wakingActivity.size() - 1; j >= 0; j--) {
TimeUtils.formatDuration(wakingActivity.keyAt(j), nowElapsed, pw);
- final SparseBooleanArray uidsToBlame = wakingActivity.valueAt(j);
+ final SparseIntArray uidsToBlame = wakingActivity.valueAt(j);
if (uidsToBlame == null) {
pw.println();
continue;
}
pw.print(": ");
for (int k = 0; k < uidsToBlame.size(); k++) {
- if (uidsToBlame.valueAt(k)) {
- UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
- pw.print(", ");
- }
+ UserHandle.formatUid(pw, uidsToBlame.keyAt(k));
+ pw.print(" [" + ActivityManager.procStateToString(uidsToBlame.valueAt(k)));
+ pw.print("], ");
}
pw.println();
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 35e88c1..363d2fd 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -84,6 +84,7 @@
import android.util.Pair;
import android.util.Slog;
import android.util.SparseArray;
+import android.view.KeyEvent;
import android.view.WindowInsets;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsController.Appearance;
@@ -902,12 +903,12 @@
}
@Override
- public void handleSystemKey(int key) throws RemoteException {
+ public void handleSystemKey(KeyEvent key) throws RemoteException {
if (!checkCanCollapseStatusBar("handleSystemKey")) {
return;
}
- mLastSystemKey = key;
+ mLastSystemKey = key.getKeyCode();
if (mBar != null) {
try {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 94e041a..5f56923 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -179,6 +179,9 @@
import static com.android.server.wm.ActivityRecordProto.PROVIDES_MAX_BOUNDS;
import static com.android.server.wm.ActivityRecordProto.REPORTED_DRAWN;
import static com.android.server.wm.ActivityRecordProto.REPORTED_VISIBLE;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT;
+import static com.android.server.wm.ActivityRecordProto.SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT;
import static com.android.server.wm.ActivityRecordProto.SHOULD_SEND_COMPAT_FAKE_FOCUS;
import static com.android.server.wm.ActivityRecordProto.STARTING_DISPLAYED;
import static com.android.server.wm.ActivityRecordProto.STARTING_MOVED;
@@ -10228,6 +10231,12 @@
proto.write(LAST_DROP_INPUT_MODE, mLastDropInputMode);
proto.write(OVERRIDE_ORIENTATION, getOverrideOrientation());
proto.write(SHOULD_SEND_COMPAT_FAKE_FOCUS, shouldSendCompatFakeFocus());
+ proto.write(SHOULD_FORCE_ROTATE_FOR_CAMERA_COMPAT,
+ mLetterboxUiController.shouldForceRotateForCameraCompat());
+ proto.write(SHOULD_REFRESH_ACTIVITY_FOR_CAMERA_COMPAT,
+ mLetterboxUiController.shouldRefreshActivityForCameraCompat());
+ proto.write(SHOULD_REFRESH_ACTIVITY_VIA_PAUSE_FOR_CAMERA_COMPAT,
+ mLetterboxUiController.shouldRefreshActivityViaPauseForCameraCompat());
}
@Override
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 22dd0e5..d31fe23 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -1981,6 +1981,14 @@
return;
}
+ if (controlTarget != null) {
+ final WindowState win = controlTarget.getWindow();
+
+ if (win != null && win.isActivityTypeDream()) {
+ return;
+ }
+ }
+
final @InsetsType int restorePositionTypes = (Type.statusBars() | Type.navigationBars())
& controlTarget.getRequestedVisibleTypes();
diff --git a/services/core/java/com/android/server/wm/EmbeddedWindowController.java b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
index 052c09a..d65f464 100644
--- a/services/core/java/com/android/server/wm/EmbeddedWindowController.java
+++ b/services/core/java/com/android/server/wm/EmbeddedWindowController.java
@@ -99,23 +99,6 @@
}
}
- WindowState getHostWindow(IBinder inputToken) {
- EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
- return embeddedWindow != null ? embeddedWindow.mHostWindowState : null;
- }
-
- boolean isOverlay(IBinder inputToken) {
- EmbeddedWindow embeddedWindow = mWindows.get(inputToken);
- return embeddedWindow != null ? embeddedWindow.getIsOverlay() : false;
- }
-
- void setIsOverlay(IBinder focusGrantToken) {
- EmbeddedWindow embeddedWindow = mWindowsByFocusToken.get(focusGrantToken);
- if (embeddedWindow != null) {
- embeddedWindow.setIsOverlay();
- }
- }
-
void remove(IWindow client) {
for (int i = mWindows.size() - 1; i >= 0; i--) {
EmbeddedWindow ew = mWindows.valueAt(i);
@@ -176,14 +159,15 @@
public Session mSession;
InputChannel mInputChannel;
final int mWindowType;
- // Track whether the EmbeddedWindow is a system hosted overlay via
- // {@link OverlayHost}. In the case of client hosted overlays, the client
- // view hierarchy will take care of invoking requestEmbeddedWindowFocus
- // but for system hosted overlays we have to do this via tapOutsideDetection
- // and this variable is mostly used for tracking that.
- boolean mIsOverlay = false;
- private IBinder mFocusGrantToken;
+ /**
+ * A unique token associated with the embedded window that can be used by the host window
+ * to request focus transfer to the embedded. This is not the input token since we don't
+ * want to give clients access to each others input token.
+ */
+ private final IBinder mFocusGrantToken;
+
+ private boolean mIsFocusable;
/**
* @param session calling session to check ownership of the window
@@ -199,7 +183,8 @@
*/
EmbeddedWindow(Session session, WindowManagerService service, IWindow clientToken,
WindowState hostWindowState, int ownerUid, int ownerPid, int windowType,
- int displayId, IBinder focusGrantToken, String inputHandleName) {
+ int displayId, IBinder focusGrantToken, String inputHandleName,
+ boolean isFocusable) {
mSession = session;
mWmService = service;
mClient = clientToken;
@@ -214,6 +199,7 @@
final String hostWindowName =
(mHostWindowState != null) ? "-" + mHostWindowState.getWindowTag().toString()
: "";
+ mIsFocusable = isFocusable;
mName = "Embedded{" + inputHandleName + hostWindowName + "}";
}
@@ -279,13 +265,6 @@
return mOwnerUid;
}
- void setIsOverlay() {
- mIsOverlay = true;
- }
- boolean getIsOverlay() {
- return mIsOverlay;
- }
-
IBinder getFocusGrantToken() {
return mFocusGrantToken;
}
@@ -297,20 +276,33 @@
return null;
}
+ void setIsFocusable(boolean isFocusable) {
+ mIsFocusable = isFocusable;
+ }
+
/**
- * System hosted overlays need the WM to invoke grantEmbeddedWindowFocus and
- * so we need to participate inside handlePointerDownOutsideFocus logic
- * however client hosted overlays will rely on the hosting view hierarchy
- * to grant and revoke focus, and so the server side logic is not needed.
+ * When an embedded window is touched when it's not currently focus, we need to switch
+ * focus to that embedded window unless the embedded window was marked as not focusable.
*/
@Override
public boolean receiveFocusFromTapOutside() {
- return mIsOverlay;
+ return mIsFocusable;
}
private void handleTap(boolean grantFocus) {
if (mInputChannel != null) {
- mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+ if (mHostWindowState != null) {
+ mWmService.grantEmbeddedWindowFocus(mSession, mHostWindowState.mClient,
+ mFocusGrantToken, grantFocus);
+ if (grantFocus) {
+ // If granting focus to the embedded when tapped, we need to ensure the host
+ // gains focus as well or the transfer won't take effect since it requires
+ // the host to transfer the focus to the embedded.
+ mHostWindowState.handleTapOutsideFocusInsideSelf();
+ }
+ } else {
+ mWmService.grantEmbeddedWindowFocus(mSession, mFocusGrantToken, grantFocus);
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
index 301c184..3d4e0eb 100644
--- a/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
+++ b/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
@@ -289,6 +289,14 @@
mChanged = true;
}
+ void setFocusTransferTarget(IBinder toToken) {
+ if (mHandle.focusTransferTarget == toToken) {
+ return;
+ }
+ mHandle.focusTransferTarget = toToken;
+ mChanged = true;
+ }
+
@Override
public String toString() {
return mHandle + ", changed=" + mChanged;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index b7e2265..db44532 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -4687,6 +4687,7 @@
if (!isAttached()) {
return;
}
+ mTransitionController.collect(this);
final TaskDisplayArea taskDisplayArea = getDisplayArea();
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index c6ba600..0fe1f92 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -50,6 +50,7 @@
import static android.window.TransitionInfo.FLAG_IS_INPUT_METHOD;
import static android.window.TransitionInfo.FLAG_IS_VOICE_INTERACTION;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
+import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
import static android.window.TransitionInfo.FLAG_NO_ANIMATION;
import static android.window.TransitionInfo.FLAG_OCCLUDES_KEYGUARD;
import static android.window.TransitionInfo.FLAG_SHOW_WALLPAPER;
@@ -183,6 +184,12 @@
private final ArrayList<DisplayContent> mTargetDisplays = new ArrayList<>();
/**
+ * The (non alwaysOnTop) tasks which were on-top of their display before the transition. If
+ * tasks are nested, all the tasks that are parents of the on-top task are also included.
+ */
+ private final ArrayList<Task> mOnTopTasksStart = new ArrayList<>();
+
+ /**
* Set of participating windowtokens (activity/wallpaper) which are visible at the end of
* the transition animation.
*/
@@ -515,6 +522,7 @@
mParticipants.add(wc);
if (wc.getDisplayContent() != null && !mTargetDisplays.contains(wc.getDisplayContent())) {
mTargetDisplays.add(wc.getDisplayContent());
+ addOnTopTasks(wc.getDisplayContent(), mOnTopTasksStart);
}
if (info.mShowWallpaper) {
// Collect the wallpaper token (for isWallpaper(wc)) so it is part of the sync set.
@@ -526,6 +534,27 @@
}
}
+ /** Adds the top non-alwaysOnTop tasks within `task` to `out`. */
+ private static void addOnTopTasks(Task task, ArrayList<Task> out) {
+ for (int i = task.getChildCount() - 1; i >= 0; --i) {
+ final Task child = task.getChildAt(i).asTask();
+ if (child == null) return;
+ if (child.getWindowConfiguration().isAlwaysOnTop()) continue;
+ out.add(child);
+ addOnTopTasks(child, out);
+ break;
+ }
+ }
+
+ /** Get the top non-alwaysOnTop leaf task on the display `dc`. */
+ private static void addOnTopTasks(DisplayContent dc, ArrayList<Task> out) {
+ final Task topNotAlwaysOnTop = dc.getRootTask(
+ t -> !t.getWindowConfiguration().isAlwaysOnTop());
+ if (topNotAlwaysOnTop == null) return;
+ out.add(topNotAlwaysOnTop);
+ addOnTopTasks(topNotAlwaysOnTop, out);
+ }
+
/**
* Records wc as changing its state of existence during this transition. For example, a new
* task is considered an existence change while moving a task to front is not. wc is added
@@ -1000,11 +1029,13 @@
InsetsControlTarget prevImeTarget = dc.getImeTarget(
DisplayContent.IME_TARGET_CONTROL);
InsetsControlTarget newImeTarget = null;
+ TaskDisplayArea transientTDA = null;
// Transient-launch activities cannot be IME target (WindowState#canBeImeTarget),
// so re-compute in case the IME target is changed after transition.
for (int t = 0; t < mTransientLaunches.size(); ++t) {
if (mTransientLaunches.keyAt(t).getDisplayContent() == dc) {
newImeTarget = dc.computeImeTarget(true /* updateImeTarget */);
+ transientTDA = mTransientLaunches.keyAt(i).getTaskDisplayArea();
break;
}
}
@@ -1015,10 +1046,17 @@
InputMethodManagerInternal.get().updateImeWindowStatus(
false /* disableImeIcon */);
}
+ // An uncommitted transient launch can leave incomplete lifecycles if visibilities
+ // didn't change (eg. re-ordering with translucent tasks will leave launcher
+ // in RESUMED state), so force an update here.
+ if (!hasVisibleTransientLaunch && transientTDA != null) {
+ transientTDA.pauseBackTasks(null /* resuming */);
+ }
}
dc.removeImeSurfaceImmediately();
dc.handleCompleteDeferredRemoval();
}
+ validateKeyguardOcclusion();
validateVisibility();
mState = STATE_FINISHED;
@@ -1140,9 +1178,13 @@
}
// Check whether the participants were animated from back navigation.
mController.mAtm.mBackNavigationController.onTransactionReady(this);
+
+ collectOrderChanges();
+
// Resolve the animating targets from the participants.
mTargets = calculateTargets(mParticipants, mChanges);
final TransitionInfo info = calculateTransitionInfo(mType, mFlags, mTargets, transaction);
+ info.setDebugId(mSyncId);
// Repopulate the displays based on the resolved targets.
mTargetDisplays.clear();
@@ -1175,8 +1217,6 @@
if (mRecentsDisplayId != INVALID_DISPLAY) break;
}
- handleNonAppWindowsInTransition(mType, mFlags);
-
// The callback is only populated for custom activity-level client animations
sendRemoteCallback(mClientAnimationStartCallback);
@@ -1291,6 +1331,27 @@
info.releaseAnimSurfaces();
}
+ /** Collect tasks which moved-to-top but didn't change otherwise. */
+ @VisibleForTesting
+ void collectOrderChanges() {
+ if (mOnTopTasksStart.isEmpty()) return;
+ final ArrayList<Task> onTopTasksEnd = new ArrayList<>();
+ for (int i = 0; i < mTargetDisplays.size(); ++i) {
+ addOnTopTasks(mTargetDisplays.get(i), onTopTasksEnd);
+ }
+ for (int i = 0; i < onTopTasksEnd.size(); ++i) {
+ final Task task = onTopTasksEnd.get(i);
+ if (mOnTopTasksStart.contains(task)) continue;
+ mParticipants.add(task);
+ int changeIdx = mChanges.indexOfKey(task);
+ if (changeIdx < 0) {
+ mChanges.put(task, new ChangeInfo(task));
+ changeIdx = mChanges.indexOfKey(task);
+ }
+ mChanges.valueAt(changeIdx).mFlags |= ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP;
+ }
+ }
+
private void postCleanupOnFailure() {
mController.mAtm.mH.post(() -> {
synchronized (mController.mAtm.mGlobalLock) {
@@ -1480,19 +1541,6 @@
}
}
- private void handleNonAppWindowsInTransition(
- @TransitionType int transit, @TransitionFlags int flags) {
- if ((flags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
- // If the occlusion changed but the transition isn't an occlude/unocclude transition,
- // then we have to notify KeyguardService directly. This can happen if there is
- // another ongoing transition when the app changes occlusion OR if the app dies or
- // is killed. Both of these are common during tests.
- if (transit != TRANSIT_KEYGUARD_OCCLUDE && transit != TRANSIT_KEYGUARD_UNOCCLUDE) {
- mController.mAtm.mWindowManager.mPolicy.applyKeyguardOcclusionChange();
- }
- }
- }
-
private void reportStartReasonsToLogger() {
// Record transition start in metrics logger. We just assume everything is "DRAWN"
// at this point since splash-screen is a presentation (shell) detail.
@@ -2185,6 +2233,13 @@
return mainWin.getAttrs().rotationAnimation;
}
+ private void validateKeyguardOcclusion() {
+ if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+ mController.mStateValidators.add(
+ mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+ }
+ }
+
private void validateVisibility() {
for (int i = mTargets.size() - 1; i >= 0; --i) {
if (reduceMode(mTargets.get(i).mReadyMode) != TRANSIT_CLOSE) {
@@ -2246,13 +2301,17 @@
*/
private static final int FLAG_CHANGE_YES_ANIMATION = 0x10;
+ /** Whether this change's container moved to the top. */
+ private static final int FLAG_CHANGE_MOVED_TO_TOP = 0x20;
+
@IntDef(prefix = { "FLAG_" }, value = {
FLAG_NONE,
FLAG_SEAMLESS_ROTATION,
FLAG_TRANSIENT_LAUNCH,
FLAG_ABOVE_TRANSIENT_LAUNCH,
FLAG_CHANGE_NO_ANIMATION,
- FLAG_CHANGE_YES_ANIMATION
+ FLAG_CHANGE_YES_ANIMATION,
+ FLAG_CHANGE_MOVED_TO_TOP
})
@Retention(RetentionPolicy.SOURCE)
@interface Flag {}
@@ -2283,7 +2342,7 @@
int mDisplayId = -1;
@ActivityInfo.Config int mKnownConfigChanges;
- /** These are just extra info. They aren't used for change-detection. */
+ /** Extra information about this change. */
@Flag int mFlags = FLAG_NONE;
/** Snapshot surface and luma, if relevant. */
@@ -2335,7 +2394,8 @@
|| (mWindowingMode != 0 && mContainer.getWindowingMode() != mWindowingMode)
|| !mContainer.getBounds().equals(mAbsoluteBounds)
|| mRotation != mContainer.getWindowConfiguration().getRotation()
- || mDisplayId != getDisplayId(mContainer);
+ || mDisplayId != getDisplayId(mContainer)
+ || (mFlags & ChangeInfo.FLAG_CHANGE_MOVED_TO_TOP) != 0;
}
@TransitionInfo.TransitionMode
@@ -2436,6 +2496,9 @@
&& (mFlags & FLAG_CHANGE_YES_ANIMATION) == 0) {
flags |= FLAG_NO_ANIMATION;
}
+ if ((mFlags & FLAG_CHANGE_MOVED_TO_TOP) != 0) {
+ flags |= FLAG_MOVED_TO_TOP;
+ }
return flags;
}
diff --git a/services/core/java/com/android/server/wm/TrustedOverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 88c410b..f8edc2b 100644
--- a/services/core/java/com/android/server/wm/TrustedOverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -90,8 +90,6 @@
requireOverlaySurfaceControl();
mOverlays.add(p);
- mWmService.mEmbeddedWindowController.setIsOverlay(p.getInputToken());
-
SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
t.reparent(p.getSurfaceControl(), mSurfaceControl)
.show(p.getSurfaceControl());
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index a7a9060..dde87b1 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8624,7 +8624,8 @@
EmbeddedWindowController.EmbeddedWindow win =
new EmbeddedWindowController.EmbeddedWindow(session, this, window,
mInputToWindowMap.get(hostInputToken), callingUid, callingPid,
- sanitizedType, displayId, focusGrantToken, inputHandleName);
+ sanitizedType, displayId, focusGrantToken, inputHandleName,
+ (flags & FLAG_NOT_FOCUSABLE) == 0);
clientChannel = win.openInputChannel();
mEmbeddedWindowController.add(clientChannel.getToken(), win);
applicationHandle = win.getApplicationHandle();
@@ -8745,6 +8746,7 @@
}
name = win.toString();
applicationHandle = win.getApplicationHandle();
+ win.setIsFocusable((flags & FLAG_NOT_FOCUSABLE) == 0);
}
updateInputChannel(channelToken, win.mOwnerUid, win.mOwnerPid, displayId, surface, name,
@@ -9022,24 +9024,23 @@
Slog.e(TAG, "Embedded window does not belong to the host");
return;
}
- SurfaceControl.Transaction t = mTransactionFactory.get();
if (grantFocus) {
- t.requestFocusTransfer(embeddedWindow.getInputChannelToken(), embeddedWindow.toString(),
- hostWindow.mInputChannel.getToken(),
- hostWindow.getName(),
- hostWindow.getDisplayId()).apply();
+ hostWindow.mInputWindowHandle.setFocusTransferTarget(
+ embeddedWindow.getInputChannelToken());
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + embeddedWindow,
"reason=grantEmbeddedWindowFocus(true)");
} else {
- t.requestFocusTransfer(hostWindow.mInputChannel.getToken(), hostWindow.getName(),
- embeddedWindow.getInputChannelToken(),
- embeddedWindow.toString(),
- hostWindow.getDisplayId()).apply();
+ hostWindow.mInputWindowHandle.setFocusTransferTarget(null);
EventLog.writeEvent(LOGTAG_INPUT_FOCUS,
"Transfer focus request " + hostWindow,
"reason=grantEmbeddedWindowFocus(false)");
}
+ DisplayContent dc = mRoot.getDisplayContent(hostWindow.getDisplayId());
+ if (dc != null) {
+ dc.getInputMonitor().updateInputWindowsLw(true);
+ }
+
ProtoLog.v(WM_DEBUG_FOCUS, "grantEmbeddedWindowFocus win=%s grantFocus=%s",
embeddedWindow, grantFocus);
}
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 71acbb4..d64b5a1 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -136,7 +136,6 @@
jmethodID getContextForDisplay;
jmethodID notifyDropWindow;
jmethodID getParentSurfaceForPointers;
- jmethodID isPerDisplayTouchModeEnabled;
} gServiceClassInfo;
static struct {
@@ -369,10 +368,6 @@
virtual PointerIconStyle getCustomPointerIconId();
virtual void onPointerDisplayIdChanged(int32_t displayId, const FloatPoint& position);
- /* --- If touch mode is enabled per display or global --- */
-
- virtual bool isPerDisplayTouchModeEnabled();
-
private:
sp<InputManagerInterface> mInputManager;
@@ -1645,16 +1640,6 @@
InputReaderConfiguration::CHANGE_STYLUS_BUTTON_REPORTING);
}
-bool NativeInputManager::isPerDisplayTouchModeEnabled() {
- JNIEnv* env = jniEnv();
- jboolean enabled =
- env->CallBooleanMethod(mServiceObj, gServiceClassInfo.isPerDisplayTouchModeEnabled);
- if (checkAndClearExceptionFromCallback(env, "isPerDisplayTouchModeEnabled")) {
- return false;
- }
- return static_cast<bool>(enabled);
-}
-
FloatPoint NativeInputManager::getMouseCursorPosition() {
std::scoped_lock _l(mLock);
const auto pc = mLocked.pointerController.lock();
@@ -2846,9 +2831,6 @@
GET_METHOD_ID(gServiceClassInfo.getParentSurfaceForPointers, clazz,
"getParentSurfaceForPointers", "(I)J");
- GET_METHOD_ID(gServiceClassInfo.isPerDisplayTouchModeEnabled, clazz,
- "isPerDisplayTouchModeEnabled", "()Z");
-
// InputDevice
FIND_CLASS(gInputDeviceClassInfo.clazz, "android/view/InputDevice");
diff --git a/services/core/jni/gnss/AGnssRil.cpp b/services/core/jni/gnss/AGnssRil.cpp
index c7a1af7..b21489a 100644
--- a/services/core/jni/gnss/AGnssRil.cpp
+++ b/services/core/jni/gnss/AGnssRil.cpp
@@ -89,6 +89,10 @@
}
jboolean AGnssRil::injectNiSuplMessageData(const jbyteArray& msgData, jint length, jint slotIndex) {
+ if (mIAGnssRil->getInterfaceVersion() <= 2) {
+ ALOGE("IAGnssRil does not support injectNiSuplMessageData().");
+ return JNI_FALSE;
+ }
JNIEnv* env = getJniEnv();
jbyte* bytes = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(msgData, 0));
auto status = mIAGnssRil->injectNiSuplMessageData(std::vector<uint8_t>((const uint8_t*)bytes,
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
deleted file mode 100644
index 111cabd..0000000
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/SwitchKeyboardLayoutTest.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.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 36d191b..0ad0715 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -20,6 +20,7 @@
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED;
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__PROC_START_TYPE__PROCESS_START_TYPE_COLD;
import static com.android.internal.util.FrameworkStatsLog.BROADCAST_DELIVERY_EVENT_REPORTED__RECEIVER_TYPE__MANIFEST;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_ALARM;
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_FOREGROUND;
import static com.android.server.am.BroadcastProcessQueue.REASON_CONTAINS_INTERACTIVE;
@@ -129,7 +130,7 @@
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
- mConstants.DELAY_CACHED_MILLIS = 120_000;
+ mConstants.DELAY_FROZEN_MILLIS = 120_000;
final BroadcastSkipPolicy emptySkipPolicy = new BroadcastSkipPolicy(mAms) {
public boolean shouldSkip(BroadcastRecord r, Object o) {
@@ -371,13 +372,13 @@
List.of(makeMockRegisteredReceiver()), false);
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
- queue.setProcessCached(false);
+ queue.setProcessAndUidFrozen(null, false);
final long notCachedRunnableAt = queue.getRunnableAt();
- queue.setProcessCached(true);
+ queue.setProcessAndUidFrozen(null, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertFalse(queue.isRunnable());
- assertEquals(BroadcastProcessQueue.REASON_CACHED_INFINITE_DEFER,
+ assertEquals(BroadcastProcessQueue.REASON_INFINITE_DEFER,
queue.getRunnableAtReason());
assertEquals(ProcessList.SCHED_GROUP_UNDEFINED, queue.getPreferredSchedulingGroupLocked());
}
@@ -398,13 +399,13 @@
List.of(makeMockRegisteredReceiver()), false);
queue.enqueueOrReplaceBroadcast(airplaneRecord, 0, false);
- queue.setProcessCached(false);
+ queue.setProcessAndUidFrozen(null, false);
final long notCachedRunnableAt = queue.getRunnableAt();
- queue.setProcessCached(true);
+ queue.setProcessAndUidFrozen(null, true);
final long cachedRunnableAt = queue.getRunnableAt();
assertThat(cachedRunnableAt).isGreaterThan(notCachedRunnableAt);
assertTrue(queue.isRunnable());
- assertEquals(BroadcastProcessQueue.REASON_CACHED, queue.getRunnableAtReason());
+ assertEquals(BroadcastProcessQueue.REASON_FROZEN, queue.getRunnableAtReason());
assertEquals(ProcessList.SCHED_GROUP_BACKGROUND, queue.getPreferredSchedulingGroupLocked());
}
@@ -430,13 +431,13 @@
// verify that:
// (a) the queue is immediately runnable by existence of a fg-priority broadcast
// (b) the next one up is the fg-priority broadcast despite its later enqueue time
- queue.setProcessCached(false);
+ queue.setProcessAndUidFrozen(null, false);
assertTrue(queue.isRunnable());
assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
assertEquals(queue.peekNextBroadcastRecord(), airplaneRecord);
- queue.setProcessCached(true);
+ queue.setProcessAndUidFrozen(null, true);
assertTrue(queue.isRunnable());
assertThat(queue.getRunnableAt()).isAtMost(airplaneRecord.enqueueClockTime);
assertEquals(ProcessList.SCHED_GROUP_DEFAULT, queue.getPreferredSchedulingGroupLocked());
@@ -496,10 +497,10 @@
* Verify that a cached process that would normally be delayed becomes
* immediately runnable when the given broadcast is enqueued.
*/
- private void doRunnableAt_Cached(BroadcastRecord testRecord, int testRunnableAtReason) {
+ private void doRunnableAt_Frozen(BroadcastRecord testRecord, int testRunnableAtReason) {
final BroadcastProcessQueue queue = new BroadcastProcessQueue(mConstants,
PACKAGE_GREEN, getUidForPackage(PACKAGE_GREEN));
- queue.setProcessCached(true);
+ queue.setProcessAndUidFrozen(null, true);
final BroadcastRecord lazyRecord = makeBroadcastRecord(
new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED),
@@ -515,49 +516,49 @@
}
@Test
- public void testRunnableAt_Cached_Manifest() {
- doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ public void testRunnableAt_Frozen_Manifest() {
+ doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
List.of(makeMockManifestReceiver()), null, false), REASON_CONTAINS_MANIFEST);
}
@Test
- public void testRunnableAt_Cached_Ordered() {
- doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), null,
+ public void testRunnableAt_Frozen_Ordered() {
+ doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), null,
List.of(makeMockRegisteredReceiver()), null, true), REASON_CONTAINS_ORDERED);
}
@Test
- public void testRunnableAt_Cached_Foreground() {
+ public void testRunnableAt_Frozen_Foreground() {
final Intent foregroundIntent = new Intent();
foregroundIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- doRunnableAt_Cached(makeBroadcastRecord(foregroundIntent, null,
+ doRunnableAt_Frozen(makeBroadcastRecord(foregroundIntent, null,
List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_FOREGROUND);
}
@Test
- public void testRunnableAt_Cached_Interactive() {
+ public void testRunnableAt_Frozen_Interactive() {
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setInteractive(true);
- doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_INTERACTIVE);
}
@Test
- public void testRunnableAt_Cached_Alarm() {
+ public void testRunnableAt_Frozen_Alarm() {
final BroadcastOptions options = BroadcastOptions.makeBasic();
options.setAlarmBroadcast(true);
- doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
List.of(makeMockRegisteredReceiver()), null, false), REASON_CONTAINS_ALARM);
}
@Test
- public void testRunnableAt_Cached_Prioritized_NonDeferrable() {
+ public void testRunnableAt_Frozen_Prioritized_NonDeferrable() {
final List receivers = List.of(
withPriority(makeManifestReceiver(PACKAGE_RED, PACKAGE_RED), 10),
withPriority(makeManifestReceiver(PACKAGE_GREEN, PACKAGE_GREEN), -10));
final BroadcastOptions options = BroadcastOptions.makeBasic()
.setDeferralPolicy(BroadcastOptions.DEFERRAL_POLICY_NONE);
- doRunnableAt_Cached(makeBroadcastRecord(makeMockIntent(), options,
+ doRunnableAt_Frozen(makeBroadcastRecord(makeMockIntent(), options,
receivers, null, false), REASON_CONTAINS_PRIORITIZED);
}
@@ -1141,7 +1142,7 @@
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
}
- mImpl.waitForIdle(null);
+ mImpl.waitForIdle(LOG_WRITER_INFO);
// Verify that there is only one delivery event reported since one of the broadcasts
// should have been skipped.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index bca39ae..73f645a 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -16,8 +16,11 @@
package com.android.server.am;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN;
+import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN;
import static android.os.UserHandle.USER_SYSTEM;
+import static com.android.server.am.ActivityManagerDebugConfig.LOG_WRITER_INFO;
import static com.android.server.am.BroadcastProcessQueue.reasonToString;
import static com.android.server.am.BroadcastRecord.deliveryStateToString;
import static com.android.server.am.BroadcastRecord.isReceiverEquals;
@@ -53,7 +56,7 @@
import android.app.BackgroundStartPrivileges;
import android.app.BroadcastOptions;
import android.app.IApplicationThread;
-import android.app.UidObserver;
+import android.app.IUidFrozenStateChangedCallback;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.content.ComponentName;
@@ -162,7 +165,7 @@
private BroadcastQueue mQueue;
BroadcastConstants mConstants;
private BroadcastSkipPolicy mSkipPolicy;
- private UidObserver mUidObserver;
+ private IUidFrozenStateChangedCallback mUidFrozenStateChangedCallback;
/**
* Desired behavior of the next
@@ -222,7 +225,7 @@
realAms.mActivityTaskManager = new ActivityTaskManagerService(mContext);
realAms.mActivityTaskManager.initialize(null, null, mContext.getMainLooper());
realAms.mAtmInternal = spy(realAms.mActivityTaskManager.getAtmInternal());
- realAms.mOomAdjuster.mCachedAppOptimizer = spy(realAms.mOomAdjuster.mCachedAppOptimizer);
+ realAms.mOomAdjuster = spy(realAms.mOomAdjuster);
realAms.mPackageManagerInt = mPackageManagerInt;
realAms.mUsageStatsService = mUsageStatsManagerInt;
realAms.mProcessesReady = true;
@@ -289,9 +292,9 @@
doNothing().when(mAms).appNotResponding(any(), any());
doAnswer((invocation) -> {
- mUidObserver = invocation.getArgument(0);
+ mUidFrozenStateChangedCallback = invocation.getArgument(0);
return null;
- }).when(mAms).registerUidObserver(any(), anyInt(), anyInt(), any());
+ }).when(mAms).registerUidFrozenStateChangedCallback(any());
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.TIMEOUT = 100;
@@ -399,6 +402,18 @@
DEAD,
}
+ private void freezeUid(int uid) throws Exception {
+ mUidFrozenStateChangedCallback.onUidFrozenStateChanged(
+ new int[] { uid },
+ new int[] { UID_FROZEN_STATE_FROZEN });
+ }
+
+ private void thawUid(int uid) throws Exception {
+ mUidFrozenStateChangedCallback.onUidFrozenStateChanged(
+ new int[] { uid },
+ new int[] { UID_FROZEN_STATE_UNFROZEN });
+ }
+
private ProcessRecord makeActiveProcessRecord(String packageName) throws Exception {
return makeActiveProcessRecord(packageName, packageName, ProcessBehavior.NORMAL,
UserHandle.USER_SYSTEM);
@@ -659,7 +674,7 @@
}
private void waitForIdle() throws Exception {
- mQueue.waitForIdle(null);
+ mQueue.waitForIdle(LOG_WRITER_INFO);
}
private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
@@ -773,9 +788,6 @@
mQueue.dumpToDropBoxLocked(TAG);
BroadcastQueue.logv(TAG);
- BroadcastQueue.logv(TAG, null);
- BroadcastQueue.logv(TAG, new PrintWriter(new ByteArrayOutputStream()));
-
BroadcastQueue.logw(TAG);
assertNotNull(mQueue.toString());
@@ -951,7 +963,7 @@
// cold-started apps to be thawed, but the modern stack does
} else {
// Confirm that app was thawed
- verify(mAms.mOomAdjuster.mCachedAppOptimizer, atLeastOnce()).unfreezeTemporarily(
+ verify(mAms.mOomAdjuster, atLeastOnce()).unfreezeTemporarily(
eq(receiverApp), eq(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER));
// Confirm that we added package to process
@@ -1394,7 +1406,7 @@
anyInt(), any());
// Finally, verify that we thawed the final receiver
- verify(mAms.mOomAdjuster.mCachedAppOptimizer).unfreezeTemporarily(eq(callerApp),
+ verify(mAms.mOomAdjuster).unfreezeTemporarily(eq(callerApp),
eq(OomAdjuster.OOM_ADJ_REASON_FINISH_RECEIVER));
}
@@ -1661,8 +1673,8 @@
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
final ProcessRecord receiverOrangeApp = makeActiveProcessRecord(PACKAGE_ORANGE);
- receiverGreenApp.setCached(true);
- receiverBlueApp.setCached(true);
+ freezeUid(getUidForPackage(PACKAGE_GREEN));
+ freezeUid(getUidForPackage(PACKAGE_BLUE));
final Intent timeTick = new Intent(Intent.ACTION_TIME_TICK);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -1706,14 +1718,12 @@
eq(UserHandle.USER_SYSTEM), anyInt(), anyInt(), any());
// Shift blue to be active and confirm that deferred broadcast is delivered
- receiverBlueApp.setCached(false);
- mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_BLUE), false);
+ thawUid(getUidForPackage(PACKAGE_BLUE));
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverBlueApp, timeTick);
// Shift green to be active and confirm that deferred broadcast is delivered
- receiverGreenApp.setCached(false);
- mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+ thawUid(getUidForPackage(PACKAGE_GREEN));
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, timeTick);
}
@@ -1891,10 +1901,10 @@
assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
}
- mQueue.waitForBarrier(null);
+ mQueue.waitForBarrier(LOG_WRITER_INFO);
assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
- mQueue.waitForIdle(null);
+ mQueue.waitForIdle(LOG_WRITER_INFO);
assertTrue(mQueue.isIdleLocked());
assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
@@ -2046,9 +2056,9 @@
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final ProcessRecord receiverYellowApp = makeActiveProcessRecord(PACKAGE_YELLOW);
- receiverGreenApp.setCached(true);
- receiverBlueApp.setCached(true);
- receiverYellowApp.setCached(false);
+ freezeUid(getUidForPackage(PACKAGE_GREEN));
+ freezeUid(getUidForPackage(PACKAGE_BLUE));
+ thawUid(getUidForPackage(PACKAGE_YELLOW));
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final BroadcastOptions opts = BroadcastOptions.makeBasic()
@@ -2071,8 +2081,7 @@
verifyScheduleRegisteredReceiver(times(1), receiverYellowApp, airplane);
// Shift green to be active and confirm that deferred broadcast is delivered
- receiverGreenApp.setCached(false);
- mUidObserver.onUidCachedChanged(getUidForPackage(PACKAGE_GREEN), false);
+ thawUid(getUidForPackage(PACKAGE_GREEN));
waitForIdle();
verifyScheduleRegisteredReceiver(times(1), receiverGreenApp, airplane);
}
diff --git a/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
new file mode 100644
index 0000000..6eedeea
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/inputmethod/HardwareKeyboardShortcutControllerTest.java
@@ -0,0 +1,57 @@
+/*
+ * 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.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+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.List;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public final class HardwareKeyboardShortcutControllerTest {
+
+ @Test
+ public void testForwardRotation() {
+ final List<String> handles = Arrays.asList("0", "1", "2", "3");
+ assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", true));
+ assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "2", true));
+ assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", true));
+ }
+
+ @Test
+ public void testBackwardRotation() {
+ final List<String> handles = Arrays.asList("0", "1", "2", "3");
+ assertEquals("0", HardwareKeyboardShortcutController.getNeighborItem(handles, "1", false));
+ assertEquals("3", HardwareKeyboardShortcutController.getNeighborItem(handles, "0", false));
+ assertEquals("2", HardwareKeyboardShortcutController.getNeighborItem(handles, "3", false));
+ }
+
+ @Test
+ public void testNotMatching() {
+ final List<String> handles = Arrays.asList("0", "1", "2", "3");
+ assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", true));
+ assertNull(HardwareKeyboardShortcutController.getNeighborItem(handles, "X", false));
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
index 397d7b5..7cf5bc8 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
@@ -26,8 +26,9 @@
import android.content.Context;
import android.os.Handler;
+import android.util.IntArray;
import android.util.SparseArray;
-import android.util.SparseBooleanArray;
+import android.util.SparseIntArray;
import androidx.test.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
@@ -57,10 +58,24 @@
private static final int TEST_UID_4 = 56926423;
private static final int TEST_UID_5 = 76421423;
+ private static final int TEST_PROC_STATE_1 = 72331;
+ private static final int TEST_PROC_STATE_2 = 792351;
+ private static final int TEST_PROC_STATE_3 = 138831;
+ private static final int TEST_PROC_STATE_4 = 23231;
+ private static final int TEST_PROC_STATE_5 = 42;
+
private static final Context sContext = InstrumentationRegistry.getTargetContext();
private final Handler mHandler = Mockito.mock(Handler.class);
private final ThreadLocalRandom mRandom = ThreadLocalRandom.current();
+ private void populateDefaultProcStates(CpuWakeupStats obj) {
+ obj.mUidProcStates.put(TEST_UID_1, TEST_PROC_STATE_1);
+ obj.mUidProcStates.put(TEST_UID_2, TEST_PROC_STATE_2);
+ obj.mUidProcStates.put(TEST_UID_3, TEST_PROC_STATE_3);
+ obj.mUidProcStates.put(TEST_UID_4, TEST_PROC_STATE_4);
+ obj.mUidProcStates.put(TEST_UID_5, TEST_PROC_STATE_5);
+ }
+
@Test
public void removesOldWakeups() {
// The xml resource doesn't matter for this test.
@@ -96,6 +111,8 @@
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
+ populateDefaultProcStates(obj);
+
obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_ALARM_IRQ);
// Outside the window, so should be ignored.
@@ -106,15 +123,20 @@
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3, TEST_UID_5);
- final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+ TEST_PROC_STATE_3);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_4)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
}
@Test
@@ -122,6 +144,8 @@
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
+ populateDefaultProcStates(obj);
+
obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_WIFI_IRQ);
// Outside the window, so should be ignored.
@@ -132,15 +156,17 @@
// Should be attributed
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime + 3, TEST_UID_4, TEST_UID_5);
- final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_1)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_2)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(
+ TEST_PROC_STATE_4);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
}
@Test
@@ -148,6 +174,8 @@
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 92123210;
+ populateDefaultProcStates(obj);
+
obj.noteWakeupTimeAndReason(wakeupTime, 4,
KERNEL_REASON_WIFI_IRQ + ":" + KERNEL_REASON_ALARM_IRQ);
@@ -173,23 +201,31 @@
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 1, TEST_UID_2,
TEST_UID_5);
- final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(2);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_1)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_2)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+ TEST_PROC_STATE_3);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
+ TEST_PROC_STATE_4);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_WIFI)).isTrue();
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(true);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_3)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_4)).isEqualTo(false);
- assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(true);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_1)).isEqualTo(
+ TEST_PROC_STATE_1);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_2)).isEqualTo(
+ TEST_PROC_STATE_2);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_3)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).indexOfKey(TEST_UID_4)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_WIFI).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
}
@Test
@@ -206,12 +242,12 @@
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_WIFI, wakeupTime - 3, TEST_UID_4,
TEST_UID_5);
- final SparseArray<SparseBooleanArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
assertThat(attribution).isNotNull();
assertThat(attribution.size()).isEqualTo(1);
assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_UNKNOWN)).isTrue();
- final SparseBooleanArray uids = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
- assertThat(uids == null || uids.size() == 0).isTrue();
+ final SparseIntArray uidProcStates = attribution.get(CPU_WAKEUP_SUBSYSTEM_UNKNOWN);
+ assertThat(uidProcStates == null || uidProcStates.size() == 0).isTrue();
}
@Test
@@ -259,4 +295,39 @@
// Any nearby activity should not end up in the attribution map.
assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
}
+
+ @Test
+ public void uidProcStateBookkeeping() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+
+ assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+ final IntArray uids = new IntArray(87);
+ for (int i = 0; i < 87; i++) {
+ final int uid = mRandom.nextInt(1 << 20);
+ if (uids.indexOf(uid) < 0) {
+ uids.add(uid);
+ }
+ }
+
+ for (int i = 0; i < uids.size(); i++) {
+ final int uid = uids.get(i);
+ for (int j = 0; j < 43; j++) {
+ final int procState = mRandom.nextInt(1 << 15);
+ obj.noteUidProcessState(uid, procState);
+ assertThat(obj.mUidProcStates.get(uid)).isEqualTo(procState);
+ }
+ assertThat(obj.mUidProcStates.size()).isEqualTo(i + 1);
+ }
+
+ for (int i = 0; i < uids.size(); i++) {
+ obj.onUidRemoved(uids.get(i));
+ assertThat(obj.mUidProcStates.indexOfKey(uids.get(i))).isLessThan(0);
+ }
+
+ assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+
+ obj.onUidRemoved(213);
+ assertThat(obj.mUidProcStates.size()).isEqualTo(0);
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 42d1ace..37dd9f5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -1638,12 +1638,6 @@
any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
mContext.getTestablePermissions().setPermission(
android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
- "true",
- false);
- Thread.sleep(300);
final String tag = "testEnqueueNotificationWithTag_FgsAddsFlags_dismissalAllowed";
@@ -1665,38 +1659,6 @@
}
@Test
- public void testEnqueueNotificationWithTag_FGSaddsFlags_dismissalNotAllowed() throws Exception {
- when(mAmi.applyForegroundServiceNotification(
- any(), anyString(), anyInt(), anyString(), anyInt())).thenReturn(SHOW_IMMEDIATELY);
- mContext.getTestablePermissions().setPermission(
- android.Manifest.permission.USE_COLORIZED_NOTIFICATIONS, PERMISSION_GRANTED);
- DeviceConfig.setProperty(
- DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED,
- "false",
- false);
- Thread.sleep(300);
-
- final String tag = "testEnqueueNotificationWithTag_FGSaddsNoClear";
-
- Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
- .setContentTitle("foo")
- .setSmallIcon(android.R.drawable.sym_def_app_icon)
- .setFlag(FLAG_FOREGROUND_SERVICE, true)
- .build();
- StatusBarNotification sbn = new StatusBarNotification(PKG, PKG, 8, "tag", mUid, 0,
- n, UserHandle.getUserHandleForUid(mUid), null, 0);
- mBinderService.enqueueNotificationWithTag(PKG, PKG, tag,
- sbn.getId(), sbn.getNotification(), sbn.getUserId());
- waitForIdle();
-
- StatusBarNotification[] notifs =
- mBinderService.getActiveNotifications(PKG);
- assertThat(notifs[0].getNotification().flags).isEqualTo(
- FLAG_FOREGROUND_SERVICE | FLAG_CAN_COLORIZE | FLAG_NO_CLEAR | FLAG_ONGOING_EVENT);
- }
-
- @Test
public void testEnqueueNotificationWithTag_nullAction_fixed() throws Exception {
Notification n = new Notification.Builder(mContext, mTestNotificationChannel.getId())
.setContentTitle("foo")
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
new file mode 100644
index 0000000..ff2ce15
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/ConversionUtilTest.java
@@ -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.server.soundtrigger;
+
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
+import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
+import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
+import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
+import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
+import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import android.hardware.soundtrigger.ConversionUtil;
+import android.hardware.soundtrigger.SoundTrigger;
+
+import androidx.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.Locale;
+
+@RunWith(AndroidJUnit4.class)
+public class ConversionUtilTest {
+ private static final String TAG = "ConversionUtilTest";
+
+ @Test
+ public void testDefaultAudioFormatConstruction() {
+ // This method should generate a real format when passed null
+ final var format = aidl2apiAudioFormatWithDefault(
+ null /** exercise default **/,
+ true /** isInput **/
+ );
+ assertNotNull(format);
+ }
+
+ @Test
+ public void testRecognitionConfigRoundTrip() {
+ final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
+ | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
+ final var data = new byte[] {0x11, 0x22};
+ final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
+ keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
+ RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
+ new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
+ new ConfidenceLevel(5000, 80)});
+ keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
+ RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
+ new ConfidenceLevel(7777, 30),
+ new ConfidenceLevel(2222, 60)});
+
+ var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
+ keyphrases, data, flags);
+ assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
+ }
+
+ @Test
+ public void testByteArraySharedMemRoundTrip() {
+ final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
+ (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
+ assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
+ 10000000));
+
+ }
+
+ @Test
+ public void testPhraseRoundTrip() {
+ final var users = new int[] {10001, 10002};
+ final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
+ RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
+ Locale.forLanguageTag("no_NO"),
+ "Hello Android", /** keyphrase **/
+ users);
+ assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
+ }
+}
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
similarity index 89%
rename from tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
rename to services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
index f49d9c9..e6a1be8 100644
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/SoundTriggerTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger/SoundTriggerTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package android.hardware.soundtrigger;
+package com.android.server.soundtrigger;
import android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
import android.hardware.soundtrigger.SoundTrigger.Keyphrase;
@@ -22,6 +22,7 @@
import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
import android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
import android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.os.Parcel;
import android.test.InstrumentationTestCase;
@@ -50,10 +51,7 @@
Keyphrase unparceled = Keyphrase.CREATOR.createFromParcel(parcel);
// Verify that they are the same
- assertEquals(keyphrase.getId(), unparceled.getId());
- assertNull(unparceled.getUsers());
- assertEquals(keyphrase.getLocale(), unparceled.getLocale());
- assertEquals(keyphrase.getText(), unparceled.getText());
+ assertEquals(keyphrase, unparceled);
}
@SmallTest
@@ -115,10 +113,7 @@
KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
// Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertNull(unparceled.getData());
- assertEquals(ksm.getType(), unparceled.getType());
- assertTrue(Arrays.equals(keyphrases, unparceled.getKeyphrases()));
+ assertEquals(ksm, unparceled);
}
@SmallTest
@@ -162,10 +157,7 @@
KeyphraseSoundModel unparceled = KeyphraseSoundModel.CREATOR.createFromParcel(parcel);
// Verify that they are the same
- assertEquals(ksm.getUuid(), unparceled.getUuid());
- assertEquals(ksm.getType(), unparceled.getType());
- assertNull(unparceled.getKeyphrases());
- assertTrue(Arrays.equals(ksm.getData(), unparceled.getData()));
+ assertEquals(ksm, unparceled);
}
@SmallTest
@@ -226,7 +218,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
null /* data */,
12345678 /* halEventReceivedMillis */);
@@ -251,7 +247,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
new byte[1] /* data */,
12345678 /* halEventReceivedMillis */);
@@ -278,7 +278,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
data,
12345678 /* halEventReceivedMillis */);
@@ -335,7 +339,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
null /* data */,
null /* keyphraseExtras */,
12345678 /* halEventReceivedMillis */);
@@ -364,7 +372,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
new byte[1] /* data */,
kpExtra,
12345678 /* halEventReceivedMillis */);
@@ -409,7 +421,11 @@
3 /* captureDelayMs */,
4 /* capturePreambleMs */,
false /* triggerInData */,
- null /* captureFormat */,
+ new AudioFormat.Builder()
+ .setSampleRate(16000)
+ .setEncoding(AudioFormat.ENCODING_PCM_16BIT)
+ .setChannelMask(AudioFormat.CHANNEL_IN_MONO)
+ .build(),
data,
kpExtra,
12345678 /* halEventReceivedMillis */);
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
index 5661b12..7b7a0a3 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/ConversionUtilTest.java
@@ -16,36 +16,18 @@
package com.android.server.soundtrigger_middleware;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiAudioFormatWithDefault;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.aidl2apiRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlPhrase;
-import static android.hardware.soundtrigger.ConversionUtil.api2aidlRecognitionConfig;
-import static android.hardware.soundtrigger.ConversionUtil.byteArrayToSharedMemory;
-import static android.hardware.soundtrigger.ConversionUtil.sharedMemoryToByteArray;
-import static android.hardware.soundtrigger.SoundTrigger.ConfidenceLevel;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_GENERIC;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_AUTHENTICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_USER_IDENTIFICATION;
-import static android.hardware.soundtrigger.SoundTrigger.RECOGNITION_MODE_VOICE_TRIGGER;
-
-import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
import android.hardware.audio.common.V2_0.Uuid;
-import android.hardware.soundtrigger.SoundTrigger;
import androidx.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
-import java.util.Locale;
-
@RunWith(AndroidJUnit4.class)
public class ConversionUtilTest {
- private static final String TAG = "ConversionUtilTest";
+ private static final String TAG = "SoundTriggerMiddlewareConversionUtilTest";
@Test
public void testUuidRoundTrip() {
@@ -62,54 +44,4 @@
Uuid reconstructed = ConversionUtil.aidl2hidlUuid(aidl);
assertEquals(hidl, reconstructed);
}
-
- @Test
- public void testDefaultAudioFormatConstruction() {
- // This method should generate a real format when passed null
- final var format = aidl2apiAudioFormatWithDefault(
- null /** exercise default **/,
- true /** isInput **/
- );
- assertNotNull(format);
- }
-
- @Test
- public void testRecognitionConfigRoundTrip() {
- final int flags = SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_ECHO_CANCELLATION
- | SoundTrigger.ModuleProperties.AUDIO_CAPABILITY_NOISE_SUPPRESSION;
- final var data = new byte[] {0x11, 0x22};
- final var keyphrases = new SoundTrigger.KeyphraseRecognitionExtra[2];
- keyphrases[0] = new SoundTrigger.KeyphraseRecognitionExtra(99,
- RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_IDENTIFICATION, 13,
- new ConfidenceLevel[] {new ConfidenceLevel(9999, 50),
- new ConfidenceLevel(5000, 80)});
- keyphrases[1] = new SoundTrigger.KeyphraseRecognitionExtra(101,
- RECOGNITION_MODE_GENERIC, 8, new ConfidenceLevel[] {
- new ConfidenceLevel(7777, 30),
- new ConfidenceLevel(2222, 60)});
-
- var apiconfig = new SoundTrigger.RecognitionConfig(true, false /** must be false **/,
- keyphrases, data, flags);
- assertEquals(apiconfig, aidl2apiRecognitionConfig(api2aidlRecognitionConfig(apiconfig)));
- }
-
- @Test
- public void testByteArraySharedMemRoundTrip() {
- final var data = new byte[] { 0x11, 0x22, 0x33, 0x44,
- (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef };
- assertArrayEquals(data, sharedMemoryToByteArray(byteArrayToSharedMemory(data, "name"),
- 10000000));
-
- }
-
- @Test
- public void testPhraseRoundTrip() {
- final var users = new int[] {10001, 10002};
- final var apiphrase = new SoundTrigger.Keyphrase(17 /** id **/,
- RECOGNITION_MODE_VOICE_TRIGGER | RECOGNITION_MODE_USER_AUTHENTICATION,
- Locale.forLanguageTag("no_NO"),
- "Hello Android", /** keyphrase **/
- users);
- assertEquals(apiphrase, aidl2apiPhrase(api2aidlPhrase(apiphrase)));
- }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
index 20d410c..2914de1 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayPolicyTests.java
@@ -81,6 +81,17 @@
return win;
}
+ private WindowState createDreamWindow() {
+ final WindowState win = createDreamWindow(null, TYPE_BASE_APPLICATION, "dream");
+ final WindowManager.LayoutParams attrs = win.mAttrs;
+ attrs.width = MATCH_PARENT;
+ attrs.height = MATCH_PARENT;
+ attrs.flags =
+ FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+ attrs.format = PixelFormat.OPAQUE;
+ return win;
+ }
+
private WindowState createDimmingDialogWindow(boolean canBeImTarget) {
final WindowState win = spy(createWindow(null, TYPE_APPLICATION, "dimmingDialog"));
final WindowManager.LayoutParams attrs = win.mAttrs;
@@ -384,4 +395,25 @@
displayPolicy.requestTransientBars(mNavBarWindow, true);
assertTrue(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
}
+
+ @UseTestDisplay(addWindows = { W_NAVIGATION_BAR })
+ @Test
+ public void testTransientBarsSuppressedOnDreams() {
+ final WindowState win = createDreamWindow();
+
+ ((TestWindowManagerPolicy) mWm.mPolicy).mIsUserSetupComplete = true;
+ win.mAttrs.insetsFlags.behavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE;
+ win.setRequestedVisibleTypes(0, navigationBars());
+
+ final DisplayPolicy displayPolicy = mDisplayContent.getDisplayPolicy();
+ displayPolicy.addWindowLw(mNavBarWindow, mNavBarWindow.mAttrs);
+ final InsetsSourceProvider navBarProvider = mNavBarWindow.getControllableInsetProvider();
+ navBarProvider.updateControlForTarget(win, false);
+ navBarProvider.getSource().setVisible(false);
+
+ displayPolicy.setCanSystemBarsBeShownByUser(true);
+ displayPolicy.requestTransientBars(mNavBarWindow, true);
+
+ assertFalse(mDisplayContent.getInsetsPolicy().isTransient(navigationBars()));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index d7bf4b0..90506d4 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -1885,6 +1885,39 @@
assertEquals(newParent.getDisplayArea(), change.mCommonAncestor);
}
+ @Test
+ public void testMoveToTopWhileVisible() {
+ final Transition transition = createTestTransition(TRANSIT_OPEN);
+ final ArrayMap<WindowContainer, Transition.ChangeInfo> changes = transition.mChanges;
+ final ArraySet<WindowContainer> participants = transition.mParticipants;
+
+ // Start with taskB on top and taskA on bottom but both visible.
+ final Task rootTaskA = createTask(mDisplayContent);
+ final Task leafTaskA = createTaskInRootTask(rootTaskA, 0 /* userId */);
+ final Task taskB = createTask(mDisplayContent);
+ leafTaskA.setVisibleRequested(true);
+ taskB.setVisibleRequested(true);
+ // manually collect since this is a test transition and not known by transitionController.
+ transition.collect(leafTaskA);
+ rootTaskA.moveToFront("test", leafTaskA);
+
+ // All the tasks were already visible, so there shouldn't be any changes
+ ArrayList<Transition.ChangeInfo> targets = Transition.calculateTargets(
+ participants, changes);
+ assertTrue(targets.isEmpty());
+
+ // After collecting order changes, it should recognize that a task moved to top.
+ transition.collectOrderChanges();
+ targets = Transition.calculateTargets(participants, changes);
+ assertEquals(1, targets.size());
+
+ // Make sure the flag is set
+ final TransitionInfo info = Transition.calculateTransitionInfo(
+ transition.mType, 0 /* flags */, targets, mMockT);
+ assertTrue((info.getChanges().get(0).getFlags() & TransitionInfo.FLAG_MOVED_TO_TOP) != 0);
+ assertEquals(TRANSIT_CHANGE, info.getChanges().get(0).getMode());
+ }
+
private static void makeTaskOrganized(Task... tasks) {
final ITaskOrganizer organizer = mock(ITaskOrganizer.class);
for (Task t : tasks) {
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 0d7cdc8..7e3ec55 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowTestsBase.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.app.AppOpsManager.OP_NONE;
+import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ROTATION_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -467,6 +468,12 @@
return createWindow(null, type, activity, name);
}
+ WindowState createDreamWindow(WindowState parent, int type, String name) {
+ final WindowToken token = createWindowToken(
+ mDisplayContent, WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_DREAM, type);
+ return createWindow(parent, type, token, name);
+ }
+
// TODO: Move these calls to a builder?
WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name,
IWindow iwindow) {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
index 86c4bbf..37a325e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/FakeSoundTriggerHal.java
@@ -276,16 +276,15 @@
// for our clients.
mGlobalEventSession = new IInjectGlobalEvent.Stub() {
/**
- * Overrides IInjectGlobalEvent method.
* Simulate a HAL process restart. This method is not included in regular HAL interface,
* since the entire process is restarted by sending a signal.
* Since we run in-proc, we must offer an explicit restart method.
* oneway
*/
@Override
- public void triggerRestart() throws RemoteException {
+ public void triggerRestart() {
synchronized (FakeSoundTriggerHal.this.mLock) {
- if (mIsDead) throw new DeadObjectException();
+ if (mIsDead) return;
mIsDead = true;
mInjectionDispatcher.wrap((ISoundTriggerInjection cb) ->
cb.onRestarted(this));
@@ -305,15 +304,15 @@
}
}
- /**
- * Overrides IInjectGlobalEvent method.
- * oneway
- */
+ // oneway
@Override
public void setResourceContention(boolean isResourcesContended,
- IAcknowledgeEvent callback) throws RemoteException {
+ IAcknowledgeEvent callback) {
synchronized (FakeSoundTriggerHal.this.mLock) {
- if (mIsDead) throw new DeadObjectException();
+ // oneway, so don't throw on death
+ if (mIsDead || mIsResourceContended == isResourcesContended) {
+ return;
+ }
mIsResourceContended = isResourcesContended;
// Introducing contention is the only injection which can't be
// observed by the ST client.
@@ -325,7 +324,19 @@
}
}
}
+
+ // oneway
+ @Override
+ public void triggerOnResourcesAvailable() {
+ synchronized (FakeSoundTriggerHal.this.mLock) {
+ // oneway, so don't throw on death
+ if (mIsDead) return;
+ mGlobalCallbackDispatcher.wrap((ISoundTriggerHwGlobalCallback cb) ->
+ cb.onResourcesAvailable());
+ }
+ }
};
+
// Register the global event injection interface
mInjectionDispatcher.wrap((ISoundTriggerInjection cb)
-> cb.registerGlobalEventInjection(mGlobalEventSession));
@@ -465,7 +476,9 @@
if (session == null) {
Slog.wtf(TAG, "Attempted to start recognition with invalid handle");
}
-
+ if (mIsResourceContended) {
+ throw new ServiceSpecificException(Status.RESOURCE_CONTENTION);
+ }
if (session.getIsUnloaded()) {
// TODO(b/274470274) this is a deficiency in the existing HAL API, there is no way
// to handle this race gracefully
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 2f8d17d..4c134af 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareLogging.java
@@ -35,7 +35,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.SystemClock;
-import android.util.Log;
+import android.util.Slog;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
@@ -463,7 +463,7 @@
printObject(originatorIdentity),
printArgs(args),
printObject(retVal));
- Log.i(TAG, message);
+ Slog.i(TAG, message);
appendMessage(message);
}
@@ -474,7 +474,7 @@
object,
printObject(originatorIdentity),
printArgs(args));
- Log.i(TAG, message);
+ Slog.i(TAG, message);
appendMessage(message);
}
@@ -486,7 +486,7 @@
object,
printObject(originatorIdentity),
printArgs(args));
- Log.e(TAG, message, ex);
+ Slog.e(TAG, message, ex);
appendMessage(message + " " + ex.toString());
}
diff --git a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
index fa4c9b2..ff378ba 100644
--- a/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
+++ b/telephony/java/android/telephony/ims/stub/ImsRegistrationImplBase.java
@@ -43,6 +43,7 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
+import java.util.function.Consumer;
import java.util.function.Supplier;
/**
@@ -325,11 +326,19 @@
}
private void addRegistrationCallback(IImsRegistrationCallback c) throws RemoteException {
+ // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+ // list of callbacks to notify is copied over from the original list modified here. I also
+ // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+ // synchronize on outgoing and incoming operations.
mCallbacks.register(c);
updateNewCallbackWithState(c);
}
private void removeRegistrationCallback(IImsRegistrationCallback c) {
+ // This is purposefully not synchronized with broadcastToCallbacksLocked because the
+ // list of callbacks to notify is copied over from the original list modified here. I also
+ // do not want to risk introducing a deadlock by using the same mCallbacks Object to
+ // synchronize on outgoing and incoming operations.
mCallbacks.unregister(c);
}
@@ -420,7 +429,7 @@
@SystemApi
public final void onRegistered(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERED);
- mCallbacks.broadcastAction((c) -> {
+ broadcastToCallbacksLocked((c) -> {
try {
c.onRegistered(attributes);
} catch (RemoteException e) {
@@ -449,7 +458,7 @@
@SystemApi
public final void onRegistering(@NonNull ImsRegistrationAttributes attributes) {
updateToState(attributes, RegistrationManager.REGISTRATION_STATE_REGISTERING);
- mCallbacks.broadcastAction((c) -> {
+ broadcastToCallbacksLocked((c) -> {
try {
c.onRegistering(attributes);
} catch (RemoteException e) {
@@ -507,7 +516,7 @@
updateToDisconnectedState(info, suggestedAction, imsRadioTech);
// ImsReasonInfo should never be null.
final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
- mCallbacks.broadcastAction((c) -> {
+ broadcastToCallbacksLocked((c) -> {
try {
c.onDeregistered(reasonInfo, suggestedAction, imsRadioTech);
} catch (RemoteException e) {
@@ -569,7 +578,7 @@
updateToDisconnectedState(info, suggestedAction, imsRadioTech);
// ImsReasonInfo should never be null.
final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
- mCallbacks.broadcastAction((c) -> {
+ broadcastToCallbacksLocked((c) -> {
try {
c.onDeregisteredWithDetails(reasonInfo, suggestedAction, imsRadioTech, details);
} catch (RemoteException e) {
@@ -591,7 +600,7 @@
public final void onTechnologyChangeFailed(@ImsRegistrationTech int imsRadioTech,
ImsReasonInfo info) {
final ImsReasonInfo reasonInfo = (info != null) ? info : new ImsReasonInfo();
- mCallbacks.broadcastAction((c) -> {
+ broadcastToCallbacksLocked((c) -> {
try {
c.onTechnologyChangeFailed(imsRadioTech, reasonInfo);
} catch (RemoteException e) {
@@ -614,7 +623,20 @@
mUris = ArrayUtils.cloneOrNull(uris);
mUrisSet = true;
}
- mCallbacks.broadcastAction((c) -> onSubscriberAssociatedUriChanged(c, uris));
+ broadcastToCallbacksLocked((c) -> onSubscriberAssociatedUriChanged(c, uris));
+ }
+
+ /**
+ * Broadcast the specified operation in a synchronized manner so that multiple threads do not
+ * try to call broadcast at the same time, which will generate an error.
+ * @param c The Consumer lambda method containing the callback to call.
+ */
+ private void broadcastToCallbacksLocked(Consumer<IImsRegistrationCallback> c) {
+ // One broadcast can happen at a time, so synchronize threads so only one
+ // beginBroadcast/endBroadcast happens at a time.
+ synchronized (mCallbacks) {
+ mCallbacks.broadcastAction(c);
+ }
}
private void onSubscriberAssociatedUriChanged(IImsRegistrationCallback callback, Uri[] uris) {
diff --git a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
index 2954c2d..e229f05 100644
--- a/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
+++ b/telephony/java/android/telephony/satellite/ISatelliteDatagramCallback.aidl
@@ -18,7 +18,7 @@
import android.telephony.satellite.SatelliteDatagram;
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
/**
* Interface for satellite datagrams callback.
@@ -31,10 +31,10 @@
* @param datagramId An id that uniquely identifies incoming datagram.
* @param datagram Datagram received from satellite.
* @param pendingCount Number of datagrams yet to be received from satellite.
- * @param callback This callback will be used by datagram receiver app to send received
- * datagramId to Telephony. If the callback is not received within five minutes,
- * Telephony will resend the datagram.
+ * @param callback This callback will be used by datagram receiver app to to inform
+ * Telephony that datagram is received. If the callback is not received
+ * within five minutes, Telephony will resend the datagram.
*/
void onSatelliteDatagramReceived(long datagramId, in SatelliteDatagram datagram,
- int pendingCount, ILongConsumer callback);
+ int pendingCount, IVoidConsumer callback);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index d8a6faf..d0409bf 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
-import com.android.internal.telephony.ILongConsumer;
+import java.util.function.Consumer;
/**
* A callback class for listening to satellite datagrams.
@@ -33,11 +33,11 @@
* @param datagramId An id that uniquely identifies incoming datagram.
* @param datagram Datagram to be received over satellite.
* @param pendingCount Number of datagrams yet to be received by the app.
- * @param callback This callback will be used by datagram receiver app to send received
- * datagramId to Telephony. If the callback is not received within five minutes,
- * Telephony will resend the datagram.
+ * @param callback This callback will be used by datagram receiver app to inform Telephony
+ * that they received the datagram. If the callback is not received within
+ * five minutes, Telephony will resend the datagram.
*/
@UnsupportedAppUsage
void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
- int pendingCount, @NonNull ILongConsumer callback);
+ int pendingCount, @NonNull Consumer<Void> callback);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 7d82fd8..20f9bc8b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -37,7 +37,7 @@
import android.telephony.TelephonyFrameworkInitializer;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.ILongConsumer;
+import com.android.internal.telephony.IVoidConsumer;
import com.android.internal.telephony.ITelephony;
import com.android.telephony.Rlog;
@@ -862,7 +862,7 @@
*
* @param token The token to be used as a unique identifier for provisioning with satellite
* gateway.
- * @param regionId The region ID for the device's current location.
+ * @param provisionData Data from the provisioning app that can be used by provisioning server
* @param cancellationSignal The optional signal used by the caller to cancel the provision
* request. Even when the cancellation is signaled, Telephony will
* still trigger the callback to return the result of this request.
@@ -874,13 +874,14 @@
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
@UnsupportedAppUsage
- public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+ public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(token);
Objects.requireNonNull(executor);
Objects.requireNonNull(resultListener);
+ Objects.requireNonNull(provisionData);
ICancellationSignal cancelRemote = null;
try {
@@ -893,7 +894,7 @@
() -> resultListener.accept(result)));
}
};
- cancelRemote = telephony.provisionSatelliteService(mSubId, token, regionId,
+ cancelRemote = telephony.provisionSatelliteService(mSubId, token, provisionData,
errorCallback);
} else {
throw new IllegalStateException("telephony service is null.");
@@ -1186,10 +1187,22 @@
@Override
public void onSatelliteDatagramReceived(long datagramId,
@NonNull SatelliteDatagram datagram, int pendingCount,
- @NonNull ILongConsumer ack) {
+ @NonNull IVoidConsumer internalAck) {
+ Consumer<Void> externalAck = new Consumer<Void>() {
+ @Override
+ public void accept(Void result) {
+ try {
+ internalAck.accept();
+ } catch (RemoteException e) {
+ logd("onSatelliteDatagramReceived "
+ + "RemoteException: " + e);
+ }
+ }
+ };
+
executor.execute(() -> Binder.withCleanCallingIdentity(
() -> callback.onSatelliteDatagramReceived(
- datagramId, datagram, pendingCount, ack)));
+ datagramId, datagram, pendingCount, externalAck)));
}
};
sSatelliteDatagramCallbackMap.put(callback, internalCallback);
@@ -1244,7 +1257,7 @@
* This method requests modem to check if there are any pending datagrams to be received over
* satellite. If there are any incoming datagrams, they will be received via
* {@link SatelliteDatagramCallback#onSatelliteDatagramReceived(long, SatelliteDatagram, int,
- * ILongConsumer)}
+ * Consumer)} )}
*
* @param executor The executor on which the result listener will be called.
* @param resultListener Listener for the {@link SatelliteError} result of the operation.
diff --git a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
index a780cb9..ea4e2e2 100644
--- a/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
+++ b/telephony/java/android/telephony/satellite/stub/ISatellite.aidl
@@ -67,6 +67,15 @@
in IIntegerConsumer resultCallback);
/**
+ * Allow cellular modem scanning while satellite mode is on.
+ * @param enabled {@code true} to enable cellular modem while satellite mode is on
+ * and {@code false} to disable
+ * @param errorCallback The callback to receive the error code result of the operation.
+ */
+ void enableCellularModemWhileSatelliteModeIsOn(in boolean enabled,
+ in IIntegerConsumer errorCallback);
+
+ /**
* Request to enable or disable the satellite modem and demo mode. If the satellite modem
* is enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
* this may also re-enable the cellular modem.
@@ -194,7 +203,7 @@
*
* @param token The token to be used as a unique identifier for provisioning with satellite
* gateway.
- * @param regionId The region ID for the device's current location.
+ * @param provisionData Data from the provisioning app that can be used by provisioning server
* @param resultCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -210,7 +219,7 @@
* SatelliteError:REQUEST_ABORTED
* SatelliteError:NETWORK_TIMEOUT
*/
- void provisionSatelliteService(in String token, in String regionId,
+ void provisionSatelliteService(in String token, in byte[] provisionData,
in IIntegerConsumer resultCallback);
/**
diff --git a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
index debb394..d606f87 100644
--- a/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
+++ b/telephony/java/android/telephony/satellite/stub/SatelliteImplBase.java
@@ -79,6 +79,15 @@
}
@Override
+ public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+ IIntegerConsumer errorCallback) throws RemoteException {
+ executeMethodAsync(
+ () -> SatelliteImplBase.this
+ .enableCellularModemWhileSatelliteModeIsOn(enabled, errorCallback),
+ "enableCellularModemWhileSatelliteModeIsOn");
+ }
+
+ @Override
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
IIntegerConsumer errorCallback) throws RemoteException {
executeMethodAsync(
@@ -132,11 +141,11 @@
}
@Override
- public void provisionSatelliteService(String token, String regionId,
+ public void provisionSatelliteService(String token, byte[] provisionData,
IIntegerConsumer errorCallback) throws RemoteException {
executeMethodAsync(
() -> SatelliteImplBase.this
- .provisionSatelliteService(token, regionId, errorCallback),
+ .provisionSatelliteService(token, provisionData, errorCallback),
"provisionSatelliteService");
}
@@ -261,6 +270,17 @@
}
/**
+ * Allow cellular modem scanning while satellite mode is on.
+ * @param enabled {@code true} to enable cellular modem while satellite mode is on
+ * and {@code false} to disable
+ * @param errorCallback The callback to receive the error code result of the operation.
+ */
+ public void enableCellularModemWhileSatelliteModeIsOn(boolean enabled,
+ @NonNull IIntegerConsumer errorCallback) {
+ // stub implementation
+ }
+
+ /**
* Request to enable or disable the satellite modem and demo mode. If the satellite modem is
* enabled, this may also disable the cellular modem, and if the satellite modem is disabled,
* this may also re-enable the cellular modem.
@@ -401,7 +421,8 @@
*
* @param token The token to be used as a unique identifier for provisioning with satellite
* gateway.
- * @param regionId The region ID for the device's current location.
+ * @param provisionData Data from the provisioning app that can be used by provisioning
+ * server
* @param errorCallback The callback to receive the error code result of the operation.
*
* Valid error codes returned:
@@ -417,7 +438,7 @@
* SatelliteError:REQUEST_ABORTED
* SatelliteError:NETWORK_TIMEOUT
*/
- public void provisionSatelliteService(@NonNull String token, @NonNull String regionId,
+ public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
@NonNull IIntegerConsumer errorCallback) {
// stub implementation
}
diff --git a/telephony/java/com/android/internal/telephony/ITelephony.aidl b/telephony/java/com/android/internal/telephony/ITelephony.aidl
index bab08b5..cbdf38ae 100644
--- a/telephony/java/com/android/internal/telephony/ITelephony.aidl
+++ b/telephony/java/com/android/internal/telephony/ITelephony.aidl
@@ -2823,15 +2823,15 @@
* @param subId The subId of the subscription to be provisioned.
* @param token The token to be used as a unique identifier for provisioning with satellite
* gateway.
- * @param regionId The region ID for the device's current location.
+ * @provisionData Data from the provisioning app that can be used by provisioning server
* @param callback The callback to get the result of the request.
*
* @return The signal transport used by callers to cancel the provision request.
*/
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
- ICancellationSignal provisionSatelliteService(int subId, in String token, in String regionId,
- in IIntegerConsumer callback);
+ ICancellationSignal provisionSatelliteService(int subId, in String token,
+ in byte[] provisionData, in IIntegerConsumer callback);
/**
* Unregister the subscription with the satellite provider.
@@ -2980,4 +2980,13 @@
@JavaPassthrough(annotation="@android.annotation.RequiresPermission("
+ "android.Manifest.permission.SATELLITE_COMMUNICATION)")
void requestTimeForNextSatelliteVisibility(int subId, in ResultReceiver receiver);
+
+ /**
+ * This API can be used by only CTS to update satellite vendor service package name.
+ *
+ * @param servicePackageName The package name of the satellite vendor service.
+ * @return {@code true} if the satellite vendor service is set successfully,
+ * {@code false} otherwise.
+ */
+ boolean setSatelliteServicePackageName(in String servicePackageName);
}
diff --git a/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
new file mode 100644
index 0000000..b5557fd
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IVoidConsumer.aidl
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ package com.android.internal.telephony;
+
+ /**
+ * Copies consumer pattern for an operation that requires void result from another process to
+ * finish.
+ */
+ oneway interface IVoidConsumer {
+ void accept();
+ }
\ No newline at end of file
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index fef5211..4ba538e 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -50,6 +50,9 @@
"platform-test-annotations",
"wm-flicker-window-extensions",
],
+ data: [
+ ":FlickerTestApp",
+ ],
}
java_library {
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 9dc4bf0..314b9e4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/CommonAssertions.kt
@@ -280,21 +280,28 @@
*
* @param originalLayer
* ```
+ *
* Layer that should be visible at the start
+ *
* @param newLayer Layer that should be visible at the end
* @param ignoreEntriesWithRotationLayer If entries with a visible rotation layer should be ignored
+ *
* ```
* when checking the transition. If true we will not fail the assertion if a rotation layer is
* visible to fill the gap between the [originalLayer] being visible and the [newLayer] being
* visible.
* @param ignoreSnapshot
* ```
+ *
* If the snapshot layer should be ignored during the transition
+ *
* ```
* (useful mostly for app launch)
* @param ignoreSplashscreen
* ```
+ *
* If the splashscreen layer should be ignored during the transition.
+ *
* ```
* If true then we will allow for a splashscreen to be shown before the layer is shown,
* otherwise we won't and the layer must appear immediately.
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 7aea05d..fde0981 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
@@ -71,7 +71,7 @@
* Open Assistance UI.
*
* @param longpress open the UI by long pressing power button. Otherwise open the UI through
- * vioceinteraction shell command directly.
+ * vioceinteraction shell command directly.
*/
@JvmOverloads
fun openUI(longpress: Boolean = false) {
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 79c048a..d4f48fe 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
@@ -60,7 +60,6 @@
*
* @param wmHelper Helper used to get window region.
* @param direction UiAutomator Direction enum to indicate the swipe direction.
- *
* @return true if the swipe operation is successful.
*/
fun switchToPreviousAppByQuickSwitchGesture(
@@ -96,7 +95,6 @@
* @param packageName The targe application's package name.
* @param identifier The resource id of the target object.
* @param timeout The timeout duration in milliseconds.
- *
* @return true if the target object exists.
*/
@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 e497ae4..a72c12d 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
@@ -57,18 +57,16 @@
obj.click()
}
- /**
- * Drags the PIP window to the provided final coordinates without releasing the pointer.
- */
- fun dragPipWindowAwayFromEdgeWithoutRelease(
- wmHelper: WindowManagerStateHelper,
- steps: Int
- ) {
+ /** Drags the PIP window to the provided final coordinates without releasing the pointer. */
+ fun dragPipWindowAwayFromEdgeWithoutRelease(wmHelper: WindowManagerStateHelper, steps: Int) {
val initWindowRect = getWindowRect(wmHelper).clone()
// initial pointer at the center of the window
- val initialCoord = GestureHelper.Tuple(initWindowRect.centerX().toFloat(),
- initWindowRect.centerY().toFloat())
+ val initialCoord =
+ GestureHelper.Tuple(
+ initWindowRect.centerX().toFloat(),
+ initWindowRect.centerY().toFloat()
+ )
// the offset to the right (or left) of the window center to drag the window to
val offset = 50
@@ -76,8 +74,8 @@
// the actual final x coordinate with the offset included;
// if the pip window is closer to the right edge of the display the offset is negative
// otherwise the offset is positive
- val endX = initWindowRect.centerX() +
- offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
+ val endX =
+ initWindowRect.centerX() + offset * (if (isCloserToRightEdge(wmHelper)) -1 else 1)
val finalCoord = GestureHelper.Tuple(endX.toFloat(), initWindowRect.centerY().toFloat())
// drag to the final coordinate
@@ -106,7 +104,8 @@
val startX = initWindowRect.centerX()
val y = initWindowRect.centerY()
- val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ val displayRect =
+ wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
?: throw IllegalStateException("Default display is null")
// the offset to the right (or left) of the display center to drag the window to
@@ -129,7 +128,8 @@
fun isCloserToRightEdge(wmHelper: WindowManagerStateHelper): Boolean {
val windowRect = getWindowRect(wmHelper)
- val displayRect = wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
+ val displayRect =
+ wmHelper.currentState.wmState.getDefaultDisplay()?.displayRect
?: throw IllegalStateException("Default display is null")
return windowRect.centerX() > displayRect.centerX()
@@ -301,9 +301,7 @@
closePipWindow(WindowManagerStateHelper(mInstrumentation))
}
- /**
- * Returns the pip window bounds.
- */
+ /** Returns the pip window bounds. */
fun getWindowRect(wmHelper: WindowManagerStateHelper): Rect {
val windowRegion = wmHelper.getWindowRegion(this)
require(!windowRegion.isEmpty) { "Unable to find a PIP window in the current state" }
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
index 432df20..c355e27 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeOnDismissPopupDialogTestCfArm.kt
@@ -39,4 +39,4 @@
)
}
}
-}
\ No newline at end of file
+}
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 a4e4b6f..df9d33b 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
@@ -40,6 +40,7 @@
* Don't show if this is not explicitly requested by the user and the input method
* is fullscreen. That would be too disruptive.
* ```
+ *
* More details on b/190352379
*
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToHomeTest`
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 e85da1f..7954dd1 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
@@ -40,6 +40,7 @@
* Don't show if this is not explicitly requested by the user and the input method
* is fullscreen. That would be too disruptive.
* ```
+ *
* More details on b/190352379
*
* To run this test: `atest FlickerTests:CloseImeAutoOpenWindowToAppTest`
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 1fee20d..f97a038 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
@@ -19,7 +19,6 @@
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
@@ -27,6 +26,7 @@
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.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.setRotation
import com.android.server.wm.flicker.snapshotStartingWindowLayerCoversExactlyOnApp
import org.junit.Assume
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
index efda0ff..e1aa418 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeOnAppStartWhenLaunchingAppFromOverviewTestCfArm.kt
@@ -40,4 +40,4 @@
)
}
}
-}
\ No newline at end of file
+}
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 daee332..690ed53 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
@@ -20,13 +20,13 @@
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.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.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
@@ -86,9 +86,7 @@
}
}
/** {@inheritDoc} */
- @Presubmit
- @Test
- override fun entireScreenCovered() = super.entireScreenCovered()
+ @Presubmit @Test override fun entireScreenCovered() = super.entireScreenCovered()
@Presubmit
@Test
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 7514c9b..866e858 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
@@ -19,14 +19,14 @@
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 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.ImeShownOnAppStartHelper
+import com.android.server.wm.flicker.helpers.ImeStateInitializeHelper
import com.android.server.wm.flicker.helpers.setRotation
import org.junit.FixMethodOrder
import org.junit.Test
@@ -44,20 +44,27 @@
* Make sure no apps are running on the device
* Launch an app [testApp] that automatically displays IME and wait animation to complete
* ```
+ *
* To run only the presubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
* ```
+ *
* To run only the postsubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
* ```
+ *
* To run only the flaky assertions add: `--
+ *
* ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 a57aa5b..6f22589 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
@@ -19,7 +19,6 @@
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
@@ -29,6 +28,7 @@
import android.view.WindowInsets.Type.statusBars
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.FixMethodOrder
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
index cffc05d..8891d26 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/ShowImeWhileDismissingThemedPopupDialogTestCfArm.kt
@@ -45,4 +45,4 @@
)
}
}
-}
\ No newline at end of file
+}
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 9ea12a9..7135df6 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
@@ -20,7 +20,6 @@
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
@@ -28,6 +27,7 @@
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.BaseTest
+import com.android.server.wm.flicker.helpers.ImeShownOnAppStartHelper
import com.android.server.wm.flicker.navBarLayerIsVisibleAtStartAndEnd
import com.android.server.wm.flicker.statusBarLayerIsVisibleAtStartAndEnd
import org.junit.Assume
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 e8f9aa3..3c577ac 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
@@ -44,6 +44,7 @@
* Launch a secondary activity within the app
* Close the secondary activity back to the initial one
* ```
+ *
* Notes:
* ```
* 1. Part of the test setup occurs automatically via
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 05abf9f..360a233 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
@@ -39,6 +39,7 @@
* Make sure no apps are running on the device
* Launch an app [testApp] by clicking it's icon on all apps and wait animation to complete
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 63ffee6..12c0874 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
@@ -135,14 +135,15 @@
}
/**
- * Ensures that posted notifications will be visible on the lockscreen and not
- * suppressed due to being marked as seen.
+ * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+ * due to being marked as seen.
*/
@ClassRule
@JvmField
- val disableUnseenNotifFilterRule = SettingOverrideRule(
- Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- /* value= */ "0",
- )
+ val disableUnseenNotifFilterRule =
+ SettingOverrideRule(
+ Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+ /* value= */ "0",
+ )
}
}
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 a221ef6..222caed 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
@@ -150,14 +150,15 @@
}
/**
- * Ensures that posted notifications will be visible on the lockscreen and not
- * suppressed due to being marked as seen.
+ * Ensures that posted notifications will be visible on the lockscreen and not suppressed
+ * due to being marked as seen.
*/
@ClassRule
@JvmField
- val disableUnseenNotifFilterRule = SettingOverrideRule(
- Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
- /* value= */ "0",
- )
+ val disableUnseenNotifFilterRule =
+ SettingOverrideRule(
+ Settings.Secure.LOCK_SCREEN_SHOW_ONLY_UNSEEN_NOTIFICATIONS,
+ /* value= */ "0",
+ )
}
}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
index d90b3ca..43d28fa 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromNotificationWarmCfArm.kt
@@ -42,4 +42,4 @@
return FlickerTestFactory.nonRotationTests()
}
}
-}
\ No newline at end of file
+}
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 3fccd12..03f21e4 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
@@ -221,9 +221,10 @@
.getIdentifier("image_wallpaper_component", "string", "android")
// frameworks/base/core/res/res/values/config.xml returns package plus class name,
// but wallpaper layer has only class name
- val rawComponentMatcher = ComponentNameMatcher.unflattenFromString(
- instrumentation.targetContext.resources.getString(resourceId)
- )
+ val rawComponentMatcher =
+ ComponentNameMatcher.unflattenFromString(
+ instrumentation.targetContext.resources.getString(resourceId)
+ )
return ComponentNameMatcher(rawComponentMatcher.className)
}
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 4a4180b..fe789a7 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
@@ -39,6 +39,7 @@
* 0 -> 90 degrees
* 90 -> 0 degrees
* ```
+ *
* Actions:
* ```
* Launch an app (via intent)
@@ -47,22 +48,29 @@
* Change device orientation
* Stop tracing
* ```
+ *
* To run this test: `atest FlickerTests:ChangeAppRotationTest`
*
* To run only the presubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
* ```
+ *
* To run only the postsubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
* ```
+ *
* To run only the flaky assertions add: `--
+ *
* ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
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 17b3b2b..4d010f3 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
@@ -45,6 +45,7 @@
* 90 -> 0 degrees
* 90 -> 0 degrees (with starved UI thread)
* ```
+ *
* Actions:
* ```
* Launch an app in fullscreen and supporting seamless rotation (via intent)
@@ -53,22 +54,29 @@
* Change device orientation
* Stop tracing
* ```
+ *
* To run this test: `atest FlickerTests:SeamlessAppRotationTest`
*
* To run only the presubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Presubmit`
* ```
+ *
* To run only the postsubmit assertions add: `--
+ *
* ```
* --module-arg FlickerTests:exclude-annotation:androidx.test.filters.FlakyTest
* --module-arg FlickerTests:include-annotation:android.platform.test.annotations.Postsubmit`
* ```
+ *
* To run only the flaky assertions add: `--
+ *
* ```
* --module-arg FlickerTests:include-annotation:androidx.test.filters.FlakyTest`
* ```
+ *
* Notes:
* ```
* 1. Some default assertions (e.g., nav bar, status bar and screen covered)
diff --git a/tests/OdmApps/Android.bp b/tests/OdmApps/Android.bp
index de86498..5f03aa2 100644
--- a/tests/OdmApps/Android.bp
+++ b/tests/OdmApps/Android.bp
@@ -26,4 +26,7 @@
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["device-tests"],
+ data: [
+ ":TestOdmApp",
+ ],
}
diff --git a/tests/SoundTriggerTests/Android.mk b/tests/SoundTriggerTests/Android.mk
deleted file mode 100644
index cc0fa1c..0000000
--- a/tests/SoundTriggerTests/Android.mk
+++ /dev/null
@@ -1,39 +0,0 @@
-#
-# Copyright (C) 2014 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.
-#
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-LOCAL_MODULE_TAGS := tests
-
-ifeq ($(SOUND_TRIGGER_USE_STUB_MODULE), 1)
- LOCAL_SRC_FILES := $(call all-subdir-java-files)
- LOCAL_PRIVILEGED_MODULE := true
- LOCAL_CERTIFICATE := platform
- TARGET_OUT_DATA_APPS_PRIVILEGED := $(TARGET_OUT_DATA)/priv-app
-else
- LOCAL_SRC_FILES := src/android/hardware/soundtrigger/SoundTriggerTest.java
-endif
-
-LOCAL_STATIC_JAVA_LIBRARIES := mockito-target
-LOCAL_JAVA_LIBRARIES := android.test.runner android.test.base
-
-LOCAL_PACKAGE_NAME := SoundTriggerTests
-LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
-LOCAL_LICENSE_CONDITIONS := notice
-LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../NOTICE
-LOCAL_PRIVATE_PLATFORM_APIS := true
-
-include $(BUILD_PACKAGE)
diff --git a/tests/SoundTriggerTests/AndroidManifest.xml b/tests/SoundTriggerTests/AndroidManifest.xml
deleted file mode 100644
index f7454c7..0000000
--- a/tests/SoundTriggerTests/AndroidManifest.xml
+++ /dev/null
@@ -1,29 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!-- Copyright (C) 2014 The Android Open Source Project
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
--->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="android.hardware.soundtrigger">
- <uses-permission android:name="android.permission.MANAGE_SOUND_TRIGGER" />
- <uses-permission android:name="android.permission.INTERNET" />
-
- <application>
- <uses-library android:name="android.test.runner" />
- </application>
-
- <instrumentation android:name="android.test.InstrumentationTestRunner"
- android:targetPackage="android.hardware.soundtrigger"
- android:label="Tests for android.hardware.soundtrigger" />
-</manifest>
diff --git a/tests/SoundTriggerTests/OWNERS b/tests/SoundTriggerTests/OWNERS
deleted file mode 100644
index 1e41886..0000000
--- a/tests/SoundTriggerTests/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /media/java/android/media/soundtrigger/OWNERS
diff --git a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java b/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
deleted file mode 100644
index 2c3592c..0000000
--- a/tests/SoundTriggerTests/src/android/hardware/soundtrigger/stubhal/GenericSoundModelTest.java
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- * Copyright (C) 2014 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.hardware.soundtrigger;
-
-import static org.mockito.Matchers.any;
-import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-
-import android.content.Context;
-import android.hardware.soundtrigger.SoundTrigger.GenericRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.GenericSoundModel;
-import android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
-import android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
-import android.media.soundtrigger.SoundTriggerManager;
-import android.os.ParcelUuid;
-import android.os.ServiceManager;
-import android.test.AndroidTestCase;
-import android.test.suitebuilder.annotation.LargeTest;
-import android.test.suitebuilder.annotation.SmallTest;
-
-import com.android.internal.app.ISoundTriggerService;
-
-import org.mockito.MockitoAnnotations;
-
-import java.io.DataOutputStream;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Random;
-import java.util.UUID;
-
-public class GenericSoundModelTest extends AndroidTestCase {
- static final int MSG_DETECTION_ERROR = -1;
- static final int MSG_DETECTION_RESUME = 0;
- static final int MSG_DETECTION_PAUSE = 1;
- static final int MSG_KEYPHRASE_TRIGGER = 2;
- static final int MSG_GENERIC_TRIGGER = 4;
-
- private Random random = new Random();
- private HashSet<UUID> loadedModelUuids;
- private ISoundTriggerService soundTriggerService;
- private SoundTriggerManager soundTriggerManager;
-
- @Override
- public void setUp() throws Exception {
- super.setUp();
- MockitoAnnotations.initMocks(this);
-
- Context context = getContext();
- soundTriggerService = ISoundTriggerService.Stub.asInterface(
- ServiceManager.getService(Context.SOUND_TRIGGER_SERVICE));
- soundTriggerManager = (SoundTriggerManager) context.getSystemService(
- Context.SOUND_TRIGGER_SERVICE);
-
- loadedModelUuids = new HashSet<UUID>();
- }
-
- @Override
- public void tearDown() throws Exception {
- for (UUID modelUuid : loadedModelUuids) {
- soundTriggerService.deleteSoundModel(new ParcelUuid(modelUuid));
- }
- super.tearDown();
- }
-
- GenericSoundModel new_sound_model() {
- // Create sound model
- byte[] data = new byte[1024];
- random.nextBytes(data);
- UUID modelUuid = UUID.randomUUID();
- UUID mVendorUuid = UUID.randomUUID();
- return new GenericSoundModel(modelUuid, mVendorUuid, data);
- }
-
- @SmallTest
- public void testUpdateGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- // Update sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
-
- // Confirm it was updated
- GenericSoundModel returnedModel =
- soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
- assertEquals(model, returnedModel);
- }
-
- @SmallTest
- public void testDeleteGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- // Update sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
-
- // Delete sound model
- soundTriggerService.deleteSoundModel(new ParcelUuid(model.getUuid()));
- loadedModelUuids.remove(model.getUuid());
-
- // Confirm it was deleted
- GenericSoundModel returnedModel =
- soundTriggerService.getSoundModel(new ParcelUuid(model.getUuid()));
- assertEquals(null, returnedModel);
- }
-
- @LargeTest
- public void testStartStopGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
- // Update and start sound model recognition
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
- int r = soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback,
- config);
- assertEquals("Could Not Start Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
-
- // Stop recognition
- r = soundTriggerService.stopRecognition(new ParcelUuid(model.getUuid()), spyCallback);
- assertEquals("Could Not Stop Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- }
-
- @LargeTest
- public void testTriggerGenericSoundModel() throws Exception {
- GenericSoundModel model = new_sound_model();
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
- // Update and start sound model
- soundTriggerService.updateSoundModel(model);
- loadedModelUuids.add(model.getUuid());
- soundTriggerService.startRecognition(new ParcelUuid(model.getUuid()), spyCallback, config);
-
- // Send trigger to stub HAL
- Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- out.writeBytes("trig " + model.getUuid().toString() + "\r\n");
- out.flush();
- socket.close();
-
- // Verify trigger was received
- verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
- }
-
- /**
- * Tests a more complicated pattern of loading, unloading, triggering, starting and stopping
- * recognition. Intended to find unexpected errors that occur in unexpected states.
- */
- @LargeTest
- public void testFuzzGenericSoundModel() throws Exception {
- int numModels = 2;
-
- final int STATUS_UNLOADED = 0;
- final int STATUS_LOADED = 1;
- final int STATUS_STARTED = 2;
-
- class ModelInfo {
- int status;
- GenericSoundModel model;
-
- public ModelInfo(GenericSoundModel model, int status) {
- this.status = status;
- this.model = model;
- }
- }
-
- Random predictableRandom = new Random(100);
-
- ArrayList modelInfos = new ArrayList<ModelInfo>();
- for(int i=0; i<numModels; i++) {
- // Create sound model
- byte[] data = new byte[1024];
- predictableRandom.nextBytes(data);
- UUID modelUuid = UUID.randomUUID();
- UUID mVendorUuid = UUID.randomUUID();
- GenericSoundModel model = new GenericSoundModel(modelUuid, mVendorUuid, data);
- ModelInfo modelInfo = new ModelInfo(model, STATUS_UNLOADED);
- modelInfos.add(modelInfo);
- }
-
- boolean captureTriggerAudio = true;
- boolean allowMultipleTriggers = true;
- RecognitionConfig config = new RecognitionConfig(captureTriggerAudio, allowMultipleTriggers,
- null, null);
- TestRecognitionStatusCallback spyCallback = spy(new TestRecognitionStatusCallback());
-
-
- int numOperationsToRun = 100;
- for(int i=0; i<numOperationsToRun; i++) {
- // Select a random model
- int modelInfoIndex = predictableRandom.nextInt(modelInfos.size());
- ModelInfo modelInfo = (ModelInfo) modelInfos.get(modelInfoIndex);
-
- // Perform a random operation
- int operation = predictableRandom.nextInt(5);
-
- if (operation == 0 && modelInfo.status == STATUS_UNLOADED) {
- // Update and start sound model
- soundTriggerService.updateSoundModel(modelInfo.model);
- loadedModelUuids.add(modelInfo.model.getUuid());
- modelInfo.status = STATUS_LOADED;
- } else if (operation == 1 && modelInfo.status == STATUS_LOADED) {
- // Start the sound model
- int r = soundTriggerService.startRecognition(new ParcelUuid(
- modelInfo.model.getUuid()),
- spyCallback, config);
- assertEquals("Could Not Start Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- modelInfo.status = STATUS_STARTED;
- } else if (operation == 2 && modelInfo.status == STATUS_STARTED) {
- // Send trigger to stub HAL
- Socket socket = new Socket(InetAddress.getLocalHost(), 14035);
- DataOutputStream out = new DataOutputStream(socket.getOutputStream());
- out.writeBytes("trig " + modelInfo.model.getUuid() + "\r\n");
- out.flush();
- socket.close();
-
- // Verify trigger was received
- verify(spyCallback, timeout(100)).onGenericSoundTriggerDetected(any());
- reset(spyCallback);
- } else if (operation == 3 && modelInfo.status == STATUS_STARTED) {
- // Stop recognition
- int r = soundTriggerService.stopRecognition(new ParcelUuid(
- modelInfo.model.getUuid()),
- spyCallback);
- assertEquals("Could Not Stop Recognition with code: " + r,
- android.hardware.soundtrigger.SoundTrigger.STATUS_OK, r);
- modelInfo.status = STATUS_LOADED;
- } else if (operation == 4 && modelInfo.status != STATUS_UNLOADED) {
- // Delete sound model
- soundTriggerService.deleteSoundModel(new ParcelUuid(modelInfo.model.getUuid()));
- loadedModelUuids.remove(modelInfo.model.getUuid());
-
- // Confirm it was deleted
- GenericSoundModel returnedModel = soundTriggerService.getSoundModel(
- new ParcelUuid(modelInfo.model.getUuid()));
- assertEquals(null, returnedModel);
- modelInfo.status = STATUS_UNLOADED;
- }
- }
- }
-
- public class TestRecognitionStatusCallback extends IRecognitionStatusCallback.Stub {
- @Override
- public void onGenericSoundTriggerDetected(GenericRecognitionEvent recognitionEvent) {
- }
-
- @Override
- public void onKeyphraseDetected(KeyphraseRecognitionEvent recognitionEvent) {
- }
-
- @Override
- public void onError(int status) {
- }
-
- @Override
- public void onRecognitionPaused() {
- }
-
- @Override
- public void onRecognitionResumed() {
- }
- }
-}
diff --git a/tests/StagedInstallTest/Android.bp b/tests/StagedInstallTest/Android.bp
index ffde8c7..23efe54 100644
--- a/tests/StagedInstallTest/Android.bp
+++ b/tests/StagedInstallTest/Android.bp
@@ -55,6 +55,7 @@
"cts-install-lib-host",
],
data: [
+ ":StagedInstallInternalTestApp",
":apex.apexd_test",
":com.android.apex.apkrollback.test_v1",
":com.android.apex.apkrollback.test_v2",
diff --git a/tests/SystemMemoryTest/host/Android.bp b/tests/SystemMemoryTest/host/Android.bp
index 7974462..cc8bc45 100644
--- a/tests/SystemMemoryTest/host/Android.bp
+++ b/tests/SystemMemoryTest/host/Android.bp
@@ -26,4 +26,7 @@
srcs: ["src/**/*.java"],
libs: ["tradefed"],
test_suites: ["general-tests"],
+ data: [
+ ":SystemMemoryTestDevice",
+ ],
}