Merge "Avoid changing z-order of overlay layer after resizing display" into udc-qpr-dev
diff --git a/core/java/android/app/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 4e2b6fa..d189bab 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -24,7 +24,6 @@
import android.app.IApplicationThread;
import android.app.IActivityClientController;
import android.app.IActivityController;
-import android.app.IAppTask;
import android.app.IAssistDataReceiver;
import android.app.IInstrumentationWatcher;
import android.app.IProcessObserver;
@@ -107,14 +106,6 @@
in ProfilerInfo profilerInfo, in Bundle options, int userId);
boolean startNextMatchingActivity(in IBinder callingActivity,
in Intent intent, in Bundle options);
-
- /**
- * The DreamActivity has to be started in a special way that does not involve the PackageParser.
- * The DreamActivity is a framework component inserted in the dream application process. Hence,
- * it is not declared in the application's manifest and cannot be parsed. startDreamActivity
- * creates the activity and starts it without reaching out to the PackageParser.
- */
- boolean startDreamActivity(in Intent intent);
int startActivityIntentSender(in IApplicationThread caller,
in IIntentSender target, in IBinder whitelistToken, in Intent fillInIntent,
in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,
diff --git a/core/java/android/app/IUiAutomationConnection.aidl b/core/java/android/app/IUiAutomationConnection.aidl
index fbb0748..63cae63 100644
--- a/core/java/android/app/IUiAutomationConnection.aidl
+++ b/core/java/android/app/IUiAutomationConnection.aidl
@@ -24,6 +24,8 @@
import android.view.WindowContentFrameStats;
import android.view.WindowAnimationFrameStats;
import android.os.ParcelFileDescriptor;
+import android.window.ScreenCapture.ScreenCaptureListener;
+import android.window.ScreenCapture.LayerCaptureArgs;
import java.util.List;
@@ -43,8 +45,8 @@
void injectInputEventToInputFilter(in InputEvent event);
void syncInputTransactions(boolean waitForAnimations);
boolean setRotation(int rotation);
- Bitmap takeScreenshot(in Rect crop);
- Bitmap takeSurfaceControlScreenshot(in SurfaceControl surfaceControl);
+ boolean takeScreenshot(in Rect crop, in ScreenCaptureListener listener);
+ boolean takeSurfaceControlScreenshot(in SurfaceControl surfaceControl, in ScreenCaptureListener listener);
boolean clearWindowContentFrameStats(int windowId);
WindowContentFrameStats getWindowContentFrameStats(int windowId);
void clearWindowAnimationFrameStats();
diff --git a/core/java/android/app/NotificationManager.java b/core/java/android/app/NotificationManager.java
index 785470f..79b68c1 100644
--- a/core/java/android/app/NotificationManager.java
+++ b/core/java/android/app/NotificationManager.java
@@ -571,6 +571,12 @@
*/
public static final int BUBBLE_PREFERENCE_SELECTED = 2;
+ /**
+ * Maximum length of the component name of a registered NotificationListenerService.
+ * @hide
+ */
+ public static int MAX_SERVICE_COMPONENT_NAME_LENGTH = 500;
+
@UnsupportedAppUsage
private static INotificationManager sService;
diff --git a/core/java/android/app/TaskInfo.java b/core/java/android/app/TaskInfo.java
index c4e4995..2b5175c 100644
--- a/core/java/android/app/TaskInfo.java
+++ b/core/java/android/app/TaskInfo.java
@@ -242,6 +242,18 @@
public boolean isLetterboxDoubleTapEnabled;
/**
+ * Whether the user aspect ratio settings button is enabled
+ * @hide
+ */
+ public boolean topActivityEligibleForUserAspectRatioButton;
+
+ /**
+ * Hint about the letterbox state of the top activity.
+ * @hide
+ */
+ public boolean topActivityBoundsLetterboxed;
+
+ /**
* Whether the update comes from a letterbox double-tap action from the user or not.
* @hide
*/
@@ -460,7 +472,8 @@
public boolean hasCompatUI() {
return hasCameraCompatControl() || topActivityInSizeCompat
|| topActivityEligibleForLetterboxEducation
- || isLetterboxDoubleTapEnabled;
+ || isLetterboxDoubleTapEnabled
+ || topActivityEligibleForUserAspectRatioButton;
}
/**
@@ -510,6 +523,8 @@
&& supportsMultiWindow == that.supportsMultiWindow
&& displayAreaFeatureId == that.displayAreaFeatureId
&& isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap
+ && topActivityEligibleForUserAspectRatioButton
+ == that.topActivityEligibleForUserAspectRatioButton
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
&& topActivityLetterboxWidth == that.topActivityLetterboxWidth
&& topActivityLetterboxHeight == that.topActivityLetterboxHeight
@@ -543,6 +558,8 @@
&& taskId == that.taskId
&& topActivityInSizeCompat == that.topActivityInSizeCompat
&& isFromLetterboxDoubleTap == that.isFromLetterboxDoubleTap
+ && topActivityEligibleForUserAspectRatioButton
+ == that.topActivityEligibleForUserAspectRatioButton
&& topActivityEligibleForLetterboxEducation
== that.topActivityEligibleForLetterboxEducation
&& topActivityLetterboxVerticalPosition == that.topActivityLetterboxVerticalPosition
@@ -606,6 +623,8 @@
displayAreaFeatureId = source.readInt();
cameraCompatControlState = source.readInt();
isLetterboxDoubleTapEnabled = source.readBoolean();
+ topActivityEligibleForUserAspectRatioButton = source.readBoolean();
+ topActivityBoundsLetterboxed = source.readBoolean();
isFromLetterboxDoubleTap = source.readBoolean();
topActivityLetterboxVerticalPosition = source.readInt();
topActivityLetterboxHorizontalPosition = source.readInt();
@@ -660,6 +679,8 @@
dest.writeInt(displayAreaFeatureId);
dest.writeInt(cameraCompatControlState);
dest.writeBoolean(isLetterboxDoubleTapEnabled);
+ dest.writeBoolean(topActivityEligibleForUserAspectRatioButton);
+ dest.writeBoolean(topActivityBoundsLetterboxed);
dest.writeBoolean(isFromLetterboxDoubleTap);
dest.writeInt(topActivityLetterboxVerticalPosition);
dest.writeInt(topActivityLetterboxHorizontalPosition);
@@ -701,8 +722,11 @@
+ " topActivityInSizeCompat=" + topActivityInSizeCompat
+ " topActivityEligibleForLetterboxEducation= "
+ topActivityEligibleForLetterboxEducation
- + " topActivityLetterboxed= " + isLetterboxDoubleTapEnabled
- + " isFromDoubleTap= " + isFromLetterboxDoubleTap
+ + " isLetterboxDoubleTapEnabled= " + isLetterboxDoubleTapEnabled
+ + " topActivityEligibleForUserAspectRatioButton= "
+ + topActivityEligibleForUserAspectRatioButton
+ + " topActivityBoundsLetterboxed= " + topActivityBoundsLetterboxed
+ + " isFromLetterboxDoubleTap= " + isFromLetterboxDoubleTap
+ " topActivityLetterboxVerticalPosition= " + topActivityLetterboxVerticalPosition
+ " topActivityLetterboxHorizontalPosition= "
+ topActivityLetterboxHorizontalPosition
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 247d5bc..c3d26d4 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -37,6 +37,7 @@
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Region;
+import android.hardware.HardwareBuffer;
import android.hardware.display.DisplayManagerGlobal;
import android.os.Build;
import android.os.Handler;
@@ -71,6 +72,8 @@
import android.view.accessibility.AccessibilityWindowInfo;
import android.view.accessibility.IAccessibilityInteractionConnection;
import android.view.inputmethod.EditorInfo;
+import android.window.ScreenCapture;
+import android.window.ScreenCapture.ScreenshotHardwareBuffer;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -1160,17 +1163,12 @@
Point displaySize = new Point();
display.getRealSize(displaySize);
- int rotation = display.getRotation();
-
// Take the screenshot
- Bitmap screenShot = null;
+ ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
try {
- // Calling out without a lock held.
- screenShot = mUiAutomationConnection.takeScreenshot(
- new Rect(0, 0, displaySize.x, displaySize.y));
- if (screenShot == null) {
- Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
- + mDisplayId);
+ if (!mUiAutomationConnection.takeScreenshot(
+ new Rect(0, 0, displaySize.x, displaySize.y), syncScreenCapture)) {
return null;
}
} catch (RemoteException re) {
@@ -1178,10 +1176,23 @@
return null;
}
- // Optimization
- screenShot.setHasAlpha(false);
+ final ScreenshotHardwareBuffer screenshotBuffer =
+ syncScreenCapture.getBuffer();
+ Bitmap screenShot = screenshotBuffer.asBitmap();
+ if (screenShot == null) {
+ Log.e(LOG_TAG, "mUiAutomationConnection.takeScreenshot() returned null for display "
+ + mDisplayId);
+ return null;
+ }
+ Bitmap swBitmap;
+ try (HardwareBuffer buffer = screenshotBuffer.getHardwareBuffer()) {
+ swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
+ }
+ screenShot.recycle();
- return screenShot;
+ // Optimization
+ swBitmap.setHasAlpha(false);
+ return swBitmap;
}
/**
@@ -1218,12 +1229,27 @@
// Apply a sync transaction to ensure SurfaceFlinger is flushed before capturing a
// screenshot.
new SurfaceControl.Transaction().apply(true);
+ ScreenCapture.SynchronousScreenCaptureListener syncScreenCapture =
+ ScreenCapture.createSyncCaptureListener();
try {
- return mUiAutomationConnection.takeSurfaceControlScreenshot(sc);
+ if (!mUiAutomationConnection.takeSurfaceControlScreenshot(sc, syncScreenCapture)) {
+ return null;
+ }
+
} catch (RemoteException re) {
Log.e(LOG_TAG, "Error while taking screenshot!", re);
return null;
}
+ ScreenCapture.ScreenshotHardwareBuffer captureBuffer =
+ syncScreenCapture.getBuffer();
+ Bitmap screenShot = captureBuffer.asBitmap();
+ Bitmap swBitmap;
+ try (HardwareBuffer buffer = captureBuffer.getHardwareBuffer()) {
+ swBitmap = screenShot.copy(Bitmap.Config.ARGB_8888, false);
+ }
+
+ screenShot.recycle();
+ return swBitmap;
}
/**
diff --git a/core/java/android/app/UiAutomationConnection.java b/core/java/android/app/UiAutomationConnection.java
index 34f0964..52949d6 100644
--- a/core/java/android/app/UiAutomationConnection.java
+++ b/core/java/android/app/UiAutomationConnection.java
@@ -25,7 +25,6 @@
import android.annotation.UserIdInt;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.Rect;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -51,8 +50,6 @@
import android.view.accessibility.IAccessibilityManager;
import android.window.ScreenCapture;
import android.window.ScreenCapture.CaptureArgs;
-import android.window.ScreenCapture.ScreenshotHardwareBuffer;
-import android.window.ScreenCapture.SynchronousScreenCaptureListener;
import libcore.io.IoUtils;
@@ -224,56 +221,54 @@
}
@Override
- public Bitmap takeScreenshot(Rect crop) {
+ public boolean takeScreenshot(Rect crop, ScreenCapture.ScreenCaptureListener listener) {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
+
final long identity = Binder.clearCallingIdentity();
try {
final CaptureArgs captureArgs = new CaptureArgs.Builder<>()
.setSourceCrop(crop)
.build();
- SynchronousScreenCaptureListener syncScreenCapture =
- ScreenCapture.createSyncCaptureListener();
- mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs,
- syncScreenCapture);
- final ScreenshotHardwareBuffer screenshotBuffer =
- syncScreenCapture.getBuffer();
- return screenshotBuffer == null ? null : screenshotBuffer.asBitmap();
+ mWindowManager.captureDisplay(DEFAULT_DISPLAY, captureArgs, listener);
} catch (RemoteException re) {
re.rethrowAsRuntimeException();
} finally {
Binder.restoreCallingIdentity(identity);
}
- return null;
+
+ return true;
}
@Nullable
@Override
- public Bitmap takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl) {
+ public boolean takeSurfaceControlScreenshot(@NonNull SurfaceControl surfaceControl,
+ ScreenCapture.ScreenCaptureListener listener) {
synchronized (mLock) {
throwIfCalledByNotTrustedUidLocked();
throwIfShutdownLocked();
throwIfNotConnectedLocked();
}
- ScreenCapture.ScreenshotHardwareBuffer captureBuffer;
final long identity = Binder.clearCallingIdentity();
try {
- captureBuffer = ScreenCapture.captureLayers(
+ ScreenCapture.LayerCaptureArgs args =
new ScreenCapture.LayerCaptureArgs.Builder(surfaceControl)
- .setChildrenOnly(false)
- .build());
+ .setChildrenOnly(false)
+ .build();
+ int status = ScreenCapture.captureLayers(args, listener);
+
+ if (status != 0) {
+ return false;
+ }
} finally {
Binder.restoreCallingIdentity(identity);
}
- if (captureBuffer == null) {
- return null;
- }
- return captureBuffer.asBitmap();
+ return true;
}
@Override
diff --git a/core/java/android/app/admin/DevicePolicyManagerInternal.java b/core/java/android/app/admin/DevicePolicyManagerInternal.java
index 0e78275..8dd50f0 100644
--- a/core/java/android/app/admin/DevicePolicyManagerInternal.java
+++ b/core/java/android/app/admin/DevicePolicyManagerInternal.java
@@ -250,6 +250,16 @@
public abstract ComponentName getProfileOwnerAsUser(@UserIdInt int userId);
/**
+ * Returns the device owner component for the device, or {@code null} if there is not one.
+ *
+ * @deprecated added temporarily to support Android Role permission granting.
+ * Please contact Android Enterprise Device Policy team before calling this function.
+ */
+ @Deprecated
+ @Nullable
+ public abstract ComponentName getDeviceOwnerComponent(boolean callingUserOnly);
+
+ /**
* Returns the user id of the device owner, or {@link UserHandle#USER_NULL} if there is not one.
*/
@UserIdInt
diff --git a/core/java/android/content/BroadcastReceiver.java b/core/java/android/content/BroadcastReceiver.java
index 3d76b28..d7195a7 100644
--- a/core/java/android/content/BroadcastReceiver.java
+++ b/core/java/android/content/BroadcastReceiver.java
@@ -65,7 +65,7 @@
* thread of your app.
*
* <p>Note on threading: the state inside of this class is not itself
- * thread-safe, however you can use it from any thread if you properly
+ * thread-safe. However, you can use it from any thread if you make
* sure that you do not have races. Typically this means you will hand
* the entire object to another thread, which will be solely responsible
* for setting any results and finally calling {@link #finish()}.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 5d076d4..6d82922 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1413,7 +1413,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1501,7 +1501,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directories returned
* by {@link #getFilesDir()}, etc.
@@ -1812,7 +1812,7 @@
* </ul>
* <p>
* If a shared storage device is emulated (as determined by
- * {@link Environment#isExternalStorageEmulated(File)}), it's contents are
+ * {@link Environment#isExternalStorageEmulated(File)}), its contents are
* backed by a private user data partition, which means there is little
* benefit to storing data here instead of the private directory returned by
* {@link #getCacheDir()}.
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 47a5db8..3fdd023 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -770,6 +770,11 @@
void setSplashScreenTheme(String packageName, String themeName, int userId);
+ int getUserMinAspectRatio(String packageName, int userId);
+
+ @EnforcePermission("INSTALL_PACKAGES")
+ void setUserMinAspectRatio(String packageName, int userId, int aspectRatio);
+
List<String> getMimeGroup(String packageName, String group);
boolean isAutoRevokeWhitelisted(String packageName);
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 105b38a..4b883cd 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -3045,10 +3045,6 @@
* The update ownership enforcement can only be enabled on initial installation. Set
* this to {@code true} on package update is a no-op.
*
- * Apps may opt themselves out of update ownership by setting the
- * <a href="https://developer.android.com/guide/topics/manifest/manifest-element.html#allowupdateownership">android:alllowUpdateOwnership</a>
- * attribute in their manifest to <code>false</code>.
- *
* Note: To enable the update ownership enforcement, the installer must have the
* {@link android.Manifest.permission#ENFORCE_UPDATE_OWNERSHIP ENFORCE_UPDATE_OWNERSHIP}
* permission.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 8fafb18..66aadac 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2342,6 +2342,64 @@
*/
public static final int INSTALL_FAILED_SHARED_LIBRARY_BAD_CERTIFICATE_DIGEST = -130;
+ /**
+ * App minimum aspect ratio set by the user which will override app-defined aspect ratio.
+ *
+ * @hide
+ */
+ @IntDef(prefix = { "USER_MIN_ASPECT_RATIO_" }, value = {
+ USER_MIN_ASPECT_RATIO_UNSET,
+ USER_MIN_ASPECT_RATIO_SPLIT_SCREEN,
+ USER_MIN_ASPECT_RATIO_DISPLAY_SIZE,
+ USER_MIN_ASPECT_RATIO_4_3,
+ USER_MIN_ASPECT_RATIO_16_9,
+ USER_MIN_ASPECT_RATIO_3_2,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface UserMinAspectRatio {}
+
+ /**
+ * No aspect ratio override has been set by user.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_UNSET = 0;
+
+ /**
+ * Aspect ratio override code: user forces app to split screen aspect ratio. This is adjusted to
+ * half of the screen without the split screen divider.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_SPLIT_SCREEN = 1;
+
+ /**
+ * Aspect ratio override code: user forces app to the aspect ratio of the device display size.
+ * This will be the portrait aspect ratio of the device if the app is portrait or the landscape
+ * aspect ratio of the device if the app is landscape.
+ *
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_DISPLAY_SIZE = 2;
+
+ /**
+ * Aspect ratio override code: user forces app to 4:3 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_4_3 = 3;
+
+ /**
+ * Aspect ratio override code: user forces app to 16:9 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_16_9 = 4;
+
+ /**
+ * Aspect ratio override code: user forces app to 3:2 min aspect ratio
+ * @hide
+ */
+ public static final int USER_MIN_ASPECT_RATIO_3_2 = 5;
+
/** @hide */
@IntDef(flag = true, prefix = { "DELETE_" }, value = {
DELETE_KEEP_DATA,
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index d48e20e..6baf91d7 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -34,6 +34,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
+import android.os.Binder;
import android.os.ConditionVariable;
import android.os.IBinder;
import android.os.RemoteException;
@@ -48,7 +49,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -346,28 +346,29 @@
}
}
- public long registerClient(Context ctx) {
+ public boolean registerClient(Context ctx, IBinder token) {
synchronized (mLock) {
connectToProxyLocked(ctx);
- if (mProxy != null) {
- try {
- return mProxy.registerClient();
- } catch (RemoteException e) {
- Log.e(TAG, "Failed to initialize extension! Extension service does "
- + " not respond!");
- return -1;
- }
- } else {
- return -1;
+ if (mProxy == null) {
+ return false;
}
+
+ try {
+ return mProxy.registerClient(token);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to initialize extension! Extension service does "
+ + " not respond!");
+ }
+
+ return false;
}
}
- public void unregisterClient(long clientId) {
+ public void unregisterClient(IBinder token) {
synchronized (mLock) {
if (mProxy != null) {
try {
- mProxy.unregisterClient(clientId);
+ mProxy.unregisterClient(token);
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize extension! Extension service does"
+ " not respond!");
@@ -438,15 +439,15 @@
/**
* @hide
*/
- public static long registerClient(Context ctx) {
- return CameraExtensionManagerGlobal.get().registerClient(ctx);
+ public static boolean registerClient(Context ctx, IBinder token) {
+ return CameraExtensionManagerGlobal.get().registerClient(ctx, token);
}
/**
* @hide
*/
- public static void unregisterClient(long clientId) {
- CameraExtensionManagerGlobal.get().unregisterClient(clientId);
+ public static void unregisterClient(IBinder token) {
+ CameraExtensionManagerGlobal.get().unregisterClient(token);
}
/**
@@ -564,8 +565,9 @@
*/
public @NonNull List<Integer> getSupportedExtensions() {
ArrayList<Integer> ret = new ArrayList<>();
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getSupportedExtensions:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
return Collections.unmodifiableList(ret);
}
@@ -576,7 +578,7 @@
}
}
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableList(ret);
@@ -599,8 +601,9 @@
* supported device-specific extension
*/
public boolean isPostviewAvailable(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#isPostviewAvailable:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -623,7 +626,7 @@
Log.e(TAG, "Failed to query the extension for postview availability! Extension "
+ "service does not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return false;
@@ -656,9 +659,9 @@
@NonNull
public List<Size> getPostviewSupportedSizes(@Extension int extension,
@NonNull Size captureSize, int format) {
-
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getPostviewSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -719,7 +722,7 @@
+ "service does not respond!");
return Collections.emptyList();
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
}
@@ -756,8 +759,9 @@
// TODO: Revisit this code once the Extension preview processor output format
// ambiguity is resolved in b/169799538.
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -787,7 +791,7 @@
+ " not respond!");
return new ArrayList<>();
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
}
@@ -814,8 +818,9 @@
public @NonNull
List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
try {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getExtensionSupportedSizes:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -867,7 +872,7 @@
}
}
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
} catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension supported sizes! Extension service does"
@@ -888,7 +893,6 @@
* @param format device-specific extension output format
* @return the range of estimated minimal and maximal capture latency in milliseconds
* or null if no capture latency info can be provided
- *
* @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} /
* {@link ImageFormat#YUV_420_888}; or unsupported extension.
*/
@@ -903,8 +907,9 @@
throw new IllegalArgumentException("Unsupported format: " + format);
}
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getEstimatedCaptureLatencyRangeMillis:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -952,7 +957,7 @@
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return null;
@@ -968,8 +973,9 @@
* @throws IllegalArgumentException in case of an unsupported extension.
*/
public boolean isCaptureProcessProgressAvailable(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#isCaptureProcessProgressAvailable:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -992,7 +998,7 @@
Log.e(TAG, "Failed to query the extension progress callbacks! Extension service does"
+ " not respond!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return false;
@@ -1013,8 +1019,9 @@
*/
@NonNull
public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getAvailableCaptureRequestKeys:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -1033,10 +1040,11 @@
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
- extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId));
+ extenders.second.onInit(token, mCameraId,
+ mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureRequestMeta = extenders.second.getAvailableCaptureRequestKeys();
- extenders.second.onDeInit();
+ extenders.second.onDeInit(token);
}
if (captureRequestMeta != null) {
@@ -1067,7 +1075,7 @@
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture request keys!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableSet(ret);
@@ -1092,8 +1100,9 @@
*/
@NonNull
public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
- long clientId = registerClient(mContext);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + "#getAvailableCaptureResultKeys:" + mCameraId);
+ boolean success = registerClient(mContext, token);
+ if (!success) {
throw new IllegalArgumentException("Unsupported extensions");
}
@@ -1111,10 +1120,11 @@
} else {
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
initializeExtension(extension);
- extenders.second.onInit(mCameraId, mCharacteristicsMapNative.get(mCameraId));
+ extenders.second.onInit(token, mCameraId,
+ mCharacteristicsMapNative.get(mCameraId));
extenders.second.init(mCameraId, mCharacteristicsMapNative.get(mCameraId));
captureResultMeta = extenders.second.getAvailableCaptureResultKeys();
- extenders.second.onDeInit();
+ extenders.second.onDeInit(token);
}
if (captureResultMeta != null) {
@@ -1126,7 +1136,7 @@
}
CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
Object crKey = CaptureResult.Key.class;
- Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+ Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>) crKey;
ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
resultKeys, /*includeSynthetic*/ true));
@@ -1145,7 +1155,7 @@
} catch (RemoteException e) {
throw new IllegalStateException("Failed to query the available capture result keys!");
} finally {
- unregisterClient(clientId);
+ unregisterClient(token);
}
return Collections.unmodifiableSet(ret);
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index 85f8ca6..a098362 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -67,6 +67,7 @@
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
@@ -178,22 +179,20 @@
boolean folded = ArrayUtils.contains(mFoldedDeviceStates, state);
mFoldedDeviceState = folded;
- ArrayList<WeakReference<DeviceStateListener>> invalidListeners = new ArrayList<>();
- for (WeakReference<DeviceStateListener> listener : mDeviceStateListeners) {
- DeviceStateListener callback = listener.get();
+ Iterator<WeakReference<DeviceStateListener>> it = mDeviceStateListeners.iterator();
+ while(it.hasNext()) {
+ DeviceStateListener callback = it.next().get();
if (callback != null) {
callback.onDeviceStateChanged(folded);
} else {
- invalidListeners.add(listener);
+ it.remove();
}
}
- if (!invalidListeners.isEmpty()) {
- mDeviceStateListeners.removeAll(invalidListeners);
- }
}
public synchronized void addDeviceStateListener(DeviceStateListener listener) {
listener.onDeviceStateChanged(mFoldedDeviceState);
+ mDeviceStateListeners.removeIf(l -> l.get() == null);
mDeviceStateListeners.add(new WeakReference<>(listener));
}
diff --git a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
index b52c6500..3b7d801 100644
--- a/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
+++ b/core/java/android/hardware/camera2/extension/ICameraExtensionsProxyService.aidl
@@ -20,11 +20,13 @@
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
+import android.os.IBinder;
+
/** @hide */
interface ICameraExtensionsProxyService
{
- long registerClient();
- void unregisterClient(long clientId);
+ boolean registerClient(in IBinder token);
+ void unregisterClient(in IBinder token);
boolean advancedExtensionsSupported();
void initializeSession(in IInitializeSessionCallback cb);
void releaseSession();
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 754f8f6..5a22418 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -24,11 +24,13 @@
import android.hardware.camera2.extension.Size;
import android.hardware.camera2.extension.SizeList;
+import android.os.IBinder;
+
/** @hide */
interface IImageCaptureExtenderImpl
{
- void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
- void onDeInit();
+ void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics);
+ void onDeInit(in IBinder token);
@nullable CaptureStageImpl onPresetSession();
@nullable CaptureStageImpl onEnableSession();
@nullable CaptureStageImpl onDisableSession();
diff --git a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
index 01046d0..9ea8a74 100644
--- a/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IPreviewExtenderImpl.aidl
@@ -22,11 +22,13 @@
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.SizeList;
+import android.os.IBinder;
+
/** @hide */
interface IPreviewExtenderImpl
{
- void onInit(in String cameraId, in CameraMetadataNative cameraCharacteristics);
- void onDeInit();
+ void onInit(in IBinder token, in String cameraId, in CameraMetadataNative cameraCharacteristics);
+ void onDeInit(in IBinder token);
@nullable CaptureStageImpl onPresetSession();
@nullable CaptureStageImpl onEnableSession();
@nullable CaptureStageImpl onDisableSession();
diff --git a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
index 13b93a8..0581ec0 100644
--- a/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ISessionProcessorImpl.aidl
@@ -25,13 +25,15 @@
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.OutputSurface;
+import android.os.IBinder;
+
/** @hide */
interface ISessionProcessorImpl
{
- CameraSessionConfig initSession(in String cameraId,
+ CameraSessionConfig initSession(in IBinder token, in String cameraId,
in Map<String, CameraMetadataNative> charsMap, in OutputSurface previewSurface,
in OutputSurface imageCaptureSurface, in OutputSurface postviewSurface);
- void deInitSession();
+ void deInitSession(in IBinder token);
void onCaptureSessionStart(IRequestProcessorImpl requestProcessor);
void onCaptureSessionEnd();
int startRepeating(in ICaptureCallback callback);
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 65d4b43..ae700a0 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -60,6 +60,7 @@
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
@@ -79,7 +80,6 @@
private final Executor mExecutor;
private CameraDevice mCameraDevice;
private final Map<String, CameraMetadataNative> mCharacteristicsMap;
- private final long mExtensionClientId;
private final Handler mHandler;
private final HandlerThread mHandlerThread;
private final CameraExtensionSession.StateCallback mCallbacks;
@@ -90,6 +90,7 @@
private final HashMap<Integer, ImageReader> mReaderMap = new HashMap<>();
private RequestProcessor mRequestProcessor = new RequestProcessor();
private final int mSessionId;
+ private final IBinder mToken;
private Surface mClientRepeatingRequestSurface;
private Surface mClientCaptureSurface;
@@ -114,8 +115,9 @@
@NonNull Map<String, CameraCharacteristics> characteristicsMap,
@NonNull Context ctx, @NonNull ExtensionSessionConfiguration config, int sessionId)
throws CameraAccessException, RemoteException {
- long clientId = CameraExtensionCharacteristics.registerClient(ctx);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + " : " + sessionId);
+ boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
+ if (!success) {
throw new UnsupportedOperationException("Unsupported extension!");
}
@@ -202,11 +204,10 @@
IAdvancedExtenderImpl extender = CameraExtensionCharacteristics.initializeAdvancedExtension(
config.getExtension());
extender.init(cameraId, characteristicsMapNative);
-
- CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(clientId,
- extender, cameraDevice, characteristicsMapNative, repeatingRequestSurface,
+ CameraAdvancedExtensionSessionImpl ret = new CameraAdvancedExtensionSessionImpl(extender,
+ cameraDevice, characteristicsMapNative, repeatingRequestSurface,
burstCaptureSurface, postviewSurface, config.getStateCallback(),
- config.getExecutor(), sessionId);
+ config.getExecutor(), sessionId, token);
ret.mStatsAggregator.setClientName(ctx.getOpPackageName());
ret.mStatsAggregator.setExtensionType(config.getExtension());
@@ -216,15 +217,13 @@
return ret;
}
- private CameraAdvancedExtensionSessionImpl(long extensionClientId,
- @NonNull IAdvancedExtenderImpl extender,
+ private CameraAdvancedExtensionSessionImpl(@NonNull IAdvancedExtenderImpl extender,
@NonNull CameraDeviceImpl cameraDevice,
Map<String, CameraMetadataNative> characteristicsMap,
@Nullable Surface repeatingRequestSurface, @Nullable Surface burstCaptureSurface,
@Nullable Surface postviewSurface,
@NonNull StateCallback callback, @NonNull Executor executor,
- int sessionId) {
- mExtensionClientId = extensionClientId;
+ int sessionId, @NonNull IBinder token) {
mAdvancedExtender = extender;
mCameraDevice = cameraDevice;
mCharacteristicsMap = characteristicsMap;
@@ -240,6 +239,7 @@
mSessionClosed = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
+ mToken = token;
mInterfaceLock = cameraDevice.mInterfaceLock;
mStatsAggregator = new ExtensionSessionStatsAggregator(mCameraDevice.getId(),
@@ -260,7 +260,8 @@
OutputSurface postviewSurface = initializeParcelable(mClientPostviewSurface);
mSessionProcessor = mAdvancedExtender.getSessionProcessor();
- CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mCameraDevice.getId(),
+ CameraSessionConfig sessionConfig = mSessionProcessor.initSession(mToken,
+ mCameraDevice.getId(),
mCharacteristicsMap, previewSurface, captureSurface, postviewSurface);
List<CameraOutputConfig> outputConfigs = sessionConfig.outputConfigs;
ArrayList<OutputConfiguration> outputList = new ArrayList<>();
@@ -526,7 +527,11 @@
synchronized (mInterfaceLock) {
if (mInitialized) {
try {
- mCaptureSession.stopRepeating();
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: already be closed, nothing else to do
+ }
mSessionProcessor.stopRepeating();
mSessionProcessor.onCaptureSessionEnd();
mSessionClosed = true;
@@ -565,7 +570,7 @@
if (!mSessionClosed) {
mSessionProcessor.onCaptureSessionEnd();
}
- mSessionProcessor.deInitSession();
+ mSessionProcessor.deInitSession(mToken);
} catch (RemoteException e) {
Log.e(TAG, "Failed to de-initialize session processor, extension service"
+ " does not respond!") ;
@@ -573,12 +578,10 @@
mSessionProcessor = null;
}
- if (mExtensionClientId >= 0) {
- CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
- }
+ CameraExtensionCharacteristics.unregisterClient(mToken);
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
}
mInitialized = false;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index 9ebef0b..1db4808 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -56,6 +56,7 @@
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.util.LongSparseArray;
@@ -79,7 +80,6 @@
private final Executor mExecutor;
private final CameraDevice mCameraDevice;
- private final long mExtensionClientId;
private final IImageCaptureExtenderImpl mImageExtender;
private final IPreviewExtenderImpl mPreviewExtender;
private final Handler mHandler;
@@ -91,6 +91,7 @@
private final Set<CaptureRequest.Key> mSupportedRequestKeys;
private final Set<CaptureResult.Key> mSupportedResultKeys;
private final ExtensionSessionStatsAggregator mStatsAggregator;
+ private final IBinder mToken;
private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
@@ -111,6 +112,7 @@
private int mPreviewProcessorType = IPreviewExtenderImpl.PROCESSOR_TYPE_NONE;
private boolean mInitialized;
+ private boolean mSessionClosed;
// Enable/Disable internal preview/(repeating request). Extensions expect
// that preview/(repeating request) is enabled and active at any point in time.
// In case the client doesn't explicitly enable repeating requests, the framework
@@ -135,8 +137,9 @@
@NonNull ExtensionSessionConfiguration config,
int sessionId)
throws CameraAccessException, RemoteException {
- long clientId = CameraExtensionCharacteristics.registerClient(ctx);
- if (clientId < 0) {
+ final IBinder token = new Binder(TAG + " : " + sessionId);
+ boolean success = CameraExtensionCharacteristics.registerClient(ctx, token);
+ if (!success) {
throw new UnsupportedOperationException("Unsupported extension!");
}
@@ -224,15 +227,16 @@
}
extenders.first.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
- extenders.first.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
+ extenders.first.onInit(token, cameraId,
+ characteristicsMap.get(cameraId).getNativeMetadata());
extenders.second.init(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
- extenders.second.onInit(cameraId, characteristicsMap.get(cameraId).getNativeMetadata());
+ extenders.second.onInit(token, cameraId,
+ characteristicsMap.get(cameraId).getNativeMetadata());
CameraExtensionSessionImpl session = new CameraExtensionSessionImpl(
extenders.second,
extenders.first,
supportedPreviewSizes,
- clientId,
cameraDevice,
repeatingRequestSurface,
burstCaptureSurface,
@@ -240,6 +244,7 @@
config.getStateCallback(),
config.getExecutor(),
sessionId,
+ token,
extensionChars.getAvailableCaptureRequestKeys(config.getExtension()),
extensionChars.getAvailableCaptureResultKeys(config.getExtension()));
@@ -254,7 +259,6 @@
public CameraExtensionSessionImpl(@NonNull IImageCaptureExtenderImpl imageExtender,
@NonNull IPreviewExtenderImpl previewExtender,
@NonNull List<Size> previewSizes,
- long extensionClientId,
@NonNull android.hardware.camera2.impl.CameraDeviceImpl cameraDevice,
@Nullable Surface repeatingRequestSurface,
@Nullable Surface burstCaptureSurface,
@@ -262,9 +266,9 @@
@NonNull StateCallback callback,
@NonNull Executor executor,
int sessionId,
+ @NonNull IBinder token,
@NonNull Set<CaptureRequest.Key> requestKeys,
@Nullable Set<CaptureResult.Key> resultKeys) {
- mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
mCameraDevice = cameraDevice;
@@ -278,8 +282,10 @@
mHandlerThread.start();
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
+ mSessionClosed = false;
mInitializeHandler = new InitializeSessionHandler();
mSessionId = sessionId;
+ mToken = token;
mSupportedRequestKeys = requestKeys;
mSupportedResultKeys = resultKeys;
mCaptureResultsSupported = !resultKeys.isEmpty();
@@ -775,7 +781,12 @@
synchronized (mInterfaceLock) {
if (mInitialized) {
mInternalRepeatingRequestEnabled = false;
- mCaptureSession.stopRepeating();
+ try {
+ mCaptureSession.stopRepeating();
+ } catch (IllegalStateException e) {
+ // OK: already be closed, nothing else to do
+ mSessionClosed = true;
+ }
ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
try {
@@ -793,13 +804,14 @@
Log.e(TAG, "Failed to disable extension! Extension service does not "
+ "respond!");
}
- if (!captureStageList.isEmpty()) {
+ if (!captureStageList.isEmpty() && !mSessionClosed) {
CaptureRequest disableRequest = createRequest(mCameraDevice, captureStageList,
mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
mCaptureSession.capture(disableRequest,
new CloseRequestHandler(mRepeatingRequestImageCallback), mHandler);
}
+ mSessionClosed = true;
mStatsAggregator.commit(/*isFinal*/true); // Commit stats before closing session
mCaptureSession.close();
}
@@ -854,19 +866,22 @@
mHandlerThread.quit();
try {
- mPreviewExtender.onDeInit();
- mImageExtender.onDeInit();
+ if (!mSessionClosed) {
+ // return value is omitted. nothing can do after session is closed.
+ mPreviewExtender.onDisableSession();
+ mImageExtender.onDisableSession();
+ }
+ mPreviewExtender.onDeInit(mToken);
+ mImageExtender.onDeInit(mToken);
} catch (RemoteException e) {
Log.e(TAG, "Failed to release extensions! Extension service does not"
+ " respond!");
}
- if (mExtensionClientId >= 0) {
- CameraExtensionCharacteristics.unregisterClient(mExtensionClientId);
- if (mInitialized || (mCaptureSession != null)) {
- notifyClose = true;
- CameraExtensionCharacteristics.releaseSession();
- }
+ CameraExtensionCharacteristics.unregisterClient(mToken);
+ if (mInitialized || (mCaptureSession != null)) {
+ notifyClose = true;
+ CameraExtensionCharacteristics.releaseSession();
}
mInitialized = false;
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 76efce5..022f3c4 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -1762,6 +1762,24 @@
* 123,1,critical,0.8,default;123,1,moderate,0.6,id_2;456,2,moderate,0.9,critical,0.7
*/
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
+
+ /**
+ * Key for new power controller feature flag. If enabled new DisplayPowerController will
+ * be used.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_NEW_POWER_CONTROLLER = "use_newly_structured_display_power_controller";
+
+ /**
+ * Key for normal brightness mode controller feature flag.
+ * It enables NormalBrightnessModeController.
+ * Read value via {@link android.provider.DeviceConfig#getBoolean(String, String, boolean)}
+ * with {@link android.provider.DeviceConfig#NAMESPACE_DISPLAY_MANAGER} as the namespace.
+ * @hide
+ */
+ String KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER = "use_normal_brightness_mode_controller";
}
/**
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index 2f9c207..a9c4818 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -52,6 +52,8 @@
import static android.view.WindowInsets.Type.navigationBars;
import static android.view.WindowInsets.Type.statusBars;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
import android.annotation.AnyThread;
import android.annotation.CallSuper;
import android.annotation.DrawableRes;
@@ -158,7 +160,6 @@
import com.android.internal.inputmethod.InputMethodPrivilegedOperations;
import com.android.internal.inputmethod.InputMethodPrivilegedOperationsRegistry;
import com.android.internal.inputmethod.SoftInputShowHideReason;
-import com.android.internal.util.Preconditions;
import com.android.internal.util.RingBuffer;
import org.xmlpull.v1.XmlPullParserException;
@@ -481,53 +482,43 @@
public static final int BACK_DISPOSITION_ADJUST_NOTHING = 3;
/**
- * Enum values to be used for {@link #setBackDisposition(int)}.
+ * Enum flag to be used for {@link #setBackDisposition(int)}.
*
* @hide
*/
- @IntDef(prefix = { "BACK_DISPOSITION_" }, value = {
- BACK_DISPOSITION_DEFAULT,
- BACK_DISPOSITION_WILL_NOT_DISMISS,
- BACK_DISPOSITION_WILL_DISMISS,
- BACK_DISPOSITION_ADJUST_NOTHING,
- })
- @Retention(RetentionPolicy.SOURCE)
+ @Retention(SOURCE)
+ @IntDef(value = {BACK_DISPOSITION_DEFAULT, BACK_DISPOSITION_WILL_NOT_DISMISS,
+ BACK_DISPOSITION_WILL_DISMISS, BACK_DISPOSITION_ADJUST_NOTHING},
+ prefix = "BACK_DISPOSITION_")
public @interface BackDispositionMode {}
/**
- * Enum flags to be used for {@link #setImeWindowStatus}, representing the current state of the
- * IME window visibility.
- *
* @hide
- */
- @IntDef(flag = true, prefix = { "IME_" }, value = {
- IME_ACTIVE,
- IME_VISIBLE,
- IME_VISIBLE_IMPERCEPTIBLE,
- })
- @Retention(RetentionPolicy.SOURCE)
- public @interface ImeWindowVisibility {}
-
- /**
* The IME is active. It may or may not be visible.
- * @hide
*/
public static final int IME_ACTIVE = 0x1;
/**
- * The IME is perceptibly visible to the user.
* @hide
+ * The IME is perceptibly visible to the user.
*/
public static final int IME_VISIBLE = 0x2;
/**
+ * @hide
+ * The IME is active and ready with views but set invisible.
+ * This flag cannot be combined with {@link #IME_VISIBLE}.
+ */
+ public static final int IME_INVISIBLE = 0x4;
+
+ /**
+ * @hide
* The IME is visible, but not yet perceptible to the user (e.g. fading in)
* by {@link android.view.WindowInsetsController}.
*
* @see InputMethodManager#reportPerceptible
- * @hide
*/
- public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x4;
+ public static final int IME_VISIBLE_IMPERCEPTIBLE = 0x8;
// Min and max values for back disposition.
private static final int BACK_DISPOSITION_MIN = BACK_DISPOSITION_DEFAULT;
@@ -640,18 +631,9 @@
int mStatusIcon;
- /**
- * Latest value reported of back disposition mode.
- */
@BackDispositionMode
int mBackDisposition;
- /**
- * Latest value reported of IME window visibility flags.
- */
- @ImeWindowVisibility
- private int mImeWindowVisibility;
-
private Object mLock = new Object();
@GuardedBy("mLock")
private boolean mNotifyUserActionSent;
@@ -1228,14 +1210,8 @@
mImeSurfaceRemoverRunnable = null;
}
- private void setImeWindowStatus(@ImeWindowVisibility int vis,
- @BackDispositionMode int backDisposition) {
- if (vis == mImeWindowVisibility && backDisposition == mBackDisposition) {
- return;
- }
- mImeWindowVisibility = Preconditions.checkFlagsArgument(vis, IME_ACTIVE | IME_VISIBLE);
- mBackDisposition = backDisposition;
- mPrivOps.setImeWindowStatusAsync(mImeWindowVisibility, mBackDisposition);
+ private void setImeWindowStatus(int visibilityFlags, int backDisposition) {
+ mPrivOps.setImeWindowStatusAsync(visibilityFlags, backDisposition);
}
/** Set region of the keyboard to be avoided from back gesture */
@@ -1909,11 +1885,15 @@
* @param disposition disposition mode to be set
*/
public void setBackDisposition(@BackDispositionMode int disposition) {
- if (disposition < BACK_DISPOSITION_MIN || disposition > BACK_DISPOSITION_MAX) {
+ if (disposition == mBackDisposition) {
+ return;
+ }
+ if (disposition > BACK_DISPOSITION_MAX || disposition < BACK_DISPOSITION_MIN) {
Log.e(TAG, "Invalid back disposition value (" + disposition + ") specified.");
return;
}
- setImeWindowStatus(mImeWindowVisibility, disposition);
+ mBackDisposition = disposition;
+ setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
}
/**
@@ -2887,8 +2867,14 @@
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow");
mDecorViewWasVisible = mDecorViewVisible;
mInShowWindow = true;
+ final int previousImeWindowStatus =
+ (mDecorViewVisible ? IME_ACTIVE : 0) | (isInputViewShown()
+ ? (!mWindowVisible ? IME_INVISIBLE : IME_VISIBLE) : 0);
startViews(prepareWindow(showInput));
- setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
+ final int nextImeWindowStatus = mapToImeWindowStatus();
+ if (previousImeWindowStatus != nextImeWindowStatus) {
+ setImeWindowStatus(nextImeWindowStatus, mBackDisposition);
+ }
mNavigationBarController.onWindowShown();
// compute visibility
@@ -4099,9 +4085,9 @@
};
}
- @ImeWindowVisibility
private int mapToImeWindowStatus() {
- return IME_ACTIVE | (mDecorViewVisible ? IME_VISIBLE : 0);
+ return IME_ACTIVE
+ | (isInputViewShown() ? IME_VISIBLE : 0);
}
private boolean isAutomotive() {
diff --git a/core/java/android/os/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index c9073fa..7664bad 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -112,9 +112,19 @@
private static final int ANGLE_GL_DRIVER_ALL_ANGLE_OFF = 0;
// Values for ANGLE_GL_DRIVER_SELECTION_VALUES
- private static final String ANGLE_GL_DRIVER_CHOICE_DEFAULT = "default";
- private static final String ANGLE_GL_DRIVER_CHOICE_ANGLE = "angle";
- private static final String ANGLE_GL_DRIVER_CHOICE_NATIVE = "native";
+ private enum AngleDriverChoice {
+ DEFAULT("default"),
+ ANGLE("angle"),
+ NATIVE("native");
+
+ public final String choice;
+
+ AngleDriverChoice(String choice) {
+ this.choice = choice;
+ }
+ }
+
+ private static final String PROPERTY_RO_ANGLE_SUPPORTED = "ro.gfx.angle.supported";
private ClassLoader mClassLoader;
private String mLibrarySearchPaths;
@@ -193,15 +203,16 @@
}
/**
- * Query to determine if ANGLE should be used
+ * Query to determine the ANGLE driver choice.
*/
- private boolean shouldUseAngle(Context context, Bundle coreSettings, String packageName) {
+ private AngleDriverChoice queryAngleChoice(Context context, Bundle coreSettings,
+ String packageName) {
if (TextUtils.isEmpty(packageName)) {
Log.v(TAG, "No package name specified; use the system driver");
- return false;
+ return AngleDriverChoice.DEFAULT;
}
- return shouldUseAngleInternal(context, coreSettings, packageName);
+ return queryAngleChoiceInternal(context, coreSettings, packageName);
}
private int getVulkanVersion(PackageManager pm) {
@@ -422,10 +433,11 @@
* forces a choice;
* 3) Use ANGLE if isAngleEnabledByGameMode() returns true;
*/
- private boolean shouldUseAngleInternal(Context context, Bundle bundle, String packageName) {
+ private AngleDriverChoice queryAngleChoiceInternal(Context context, Bundle bundle,
+ String packageName) {
// Make sure we have a good package name
if (TextUtils.isEmpty(packageName)) {
- return false;
+ return AngleDriverChoice.DEFAULT;
}
// Check the semi-global switch (i.e. once system has booted enough) for whether ANGLE
@@ -440,7 +452,7 @@
}
if (allUseAngle == ANGLE_GL_DRIVER_ALL_ANGLE_ON) {
Log.v(TAG, "Turn on ANGLE for all applications.");
- return true;
+ return AngleDriverChoice.ANGLE;
}
// Get the per-application settings lists
@@ -463,7 +475,7 @@
+ optInPackages.size() + ", "
+ "number of values: "
+ optInValues.size());
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
// See if this application is listed in the per-application settings list
@@ -471,7 +483,7 @@
if (pkgIndex < 0) {
Log.v(TAG, packageName + " is not listed in per-application setting");
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
mAngleOptInIndex = pkgIndex;
@@ -481,14 +493,14 @@
Log.v(TAG,
"ANGLE Developer option for '" + packageName + "' "
+ "set to: '" + optInValue + "'");
- if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_ANGLE)) {
- return true;
- } else if (optInValue.equals(ANGLE_GL_DRIVER_CHOICE_NATIVE)) {
- return false;
+ if (optInValue.equals(AngleDriverChoice.ANGLE.choice)) {
+ return AngleDriverChoice.ANGLE;
+ } else if (optInValue.equals(AngleDriverChoice.NATIVE.choice)) {
+ return AngleDriverChoice.NATIVE;
} else {
// The user either chose default or an invalid value; go with the default driver or what
// the game mode indicates
- return mEnabledByGameMode;
+ return mEnabledByGameMode ? AngleDriverChoice.ANGLE : AngleDriverChoice.DEFAULT;
}
}
@@ -501,10 +513,12 @@
final List<ResolveInfo> resolveInfos =
pm.queryIntentActivities(intent, PackageManager.MATCH_SYSTEM_ONLY);
if (resolveInfos.size() != 1) {
- Log.e(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ Log.v(TAG, "Invalid number of ANGLE packages. Required: 1, Found: "
+ resolveInfos.size());
- for (ResolveInfo resolveInfo : resolveInfos) {
- Log.e(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ if (DEBUG) {
+ for (ResolveInfo resolveInfo : resolveInfos) {
+ Log.d(TAG, "Found ANGLE package: " + resolveInfo.activityInfo.packageName);
+ }
}
return "";
}
@@ -539,26 +553,48 @@
}
/**
- * Determine whether ANGLE should be used, set it up if so, and pass ANGLE details down to
- * the C++ GraphicsEnv class.
+ * Determine whether ANGLE should be used, attempt to set up from apk first, if ANGLE can be
+ * set up from apk, pass ANGLE details down to the C++ GraphicsEnv class via
+ * GraphicsEnv::setAngleInfo(). If apk setup fails, attempt to set up to use system ANGLE.
+ * Return false if both fail.
*
- * If ANGLE will be used, GraphicsEnv::setAngleInfo() will be called to enable ANGLE to be
- * properly used.
- *
- * @param context
- * @param bundle
- * @param pm
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
* @param packageName - package name of the application.
- * @return true: ANGLE setup successfully
- * false: ANGLE not setup (not on allowlist, ANGLE not present, etc.)
+ * @return true: can set up to use ANGLE successfully.
+ * false: can not set up to use ANGLE (not on allowlist, ANGLE not present, etc.)
*/
- private boolean setupAngle(Context context, Bundle bundle, PackageManager pm,
+ private boolean setupAngle(Context context, Bundle bundle, PackageManager packageManager,
String packageName) {
- if (!shouldUseAngle(context, bundle, packageName)) {
+ final AngleDriverChoice angleDriverChoice = queryAngleChoice(context, bundle, packageName);
+ if (angleDriverChoice == AngleDriverChoice.DEFAULT) {
return false;
}
+ if (queryAngleChoice(context, bundle, packageName) == AngleDriverChoice.NATIVE) {
+ nativeSetAngleInfo("", true, packageName, null);
+ return false;
+ }
+
+ return setupAngleFromApk(context, bundle, packageManager, packageName)
+ || setupAngleFromSystem(context, bundle, packageName);
+ }
+
+ /**
+ * Attempt to set up ANGLE from the packaged apk, if the apk can be found, pass ANGLE details to
+ * the C++ GraphicsEnv class.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageManager - PackageManager of the application process.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use ANGLE apk.
+ * false: can not set up to use ANGLE apk (ANGLE apk not present, etc.)
+ */
+ private boolean setupAngleFromApk(Context context, Bundle bundle, PackageManager packageManager,
+ String packageName) {
ApplicationInfo angleInfo = null;
// If the developer has specified a debug package over ADB, attempt to find it
@@ -567,7 +603,7 @@
Log.v(TAG, "ANGLE debug package enabled: " + anglePkgName);
try {
// Note the debug package does not have to be pre-installed
- angleInfo = pm.getApplicationInfo(anglePkgName, 0);
+ angleInfo = packageManager.getApplicationInfo(anglePkgName, 0);
} catch (PackageManager.NameNotFoundException e) {
// If the debug package is specified but not found, abort.
Log.v(TAG, "ANGLE debug package '" + anglePkgName + "' not installed");
@@ -577,7 +613,7 @@
// Otherwise, check to see if ANGLE is properly installed
if (angleInfo == null) {
- anglePkgName = getAnglePackageName(pm);
+ anglePkgName = getAnglePackageName(packageManager);
if (TextUtils.isEmpty(anglePkgName)) {
Log.v(TAG, "Failed to find ANGLE package.");
return false;
@@ -586,7 +622,7 @@
Log.v(TAG, "ANGLE package enabled: " + anglePkgName);
try {
// Production ANGLE libraries must be pre-installed as a system app
- angleInfo = pm.getApplicationInfo(anglePkgName,
+ angleInfo = packageManager.getApplicationInfo(anglePkgName,
PackageManager.MATCH_SYSTEM_ONLY);
} catch (PackageManager.NameNotFoundException e) {
Log.v(TAG, "ANGLE package '" + anglePkgName + "' not installed");
@@ -607,15 +643,39 @@
Log.d(TAG, "ANGLE package libs: " + paths);
}
- // If we make it to here, ANGLE will be used. Call setAngleInfo() with the package name,
- // and features to use.
+ // If we make it to here, ANGLE apk will be used. Call nativeSetAngleInfo() with the
+ // application package name and ANGLE features to use.
final String[] features = getAngleEglFeatures(context, bundle);
- setAngleInfo(paths, packageName, ANGLE_GL_DRIVER_CHOICE_ANGLE, features);
+ nativeSetAngleInfo(paths, false, packageName, features);
return true;
}
/**
+ * Attempt to set up ANGLE from system, if the apk can be found, pass ANGLE details to
+ * the C++ GraphicsEnv class.
+ *
+ * @param context - Context of the application.
+ * @param bundle - Bundle of the application.
+ * @param packageName - package name of the application.
+ * @return true: can set up to use system ANGLE.
+ * false: can not set up to use system ANGLE because it doesn't exist.
+ */
+ private boolean setupAngleFromSystem(Context context, Bundle bundle, String packageName) {
+ final boolean systemAngleSupported = SystemProperties
+ .getBoolean(PROPERTY_RO_ANGLE_SUPPORTED, false);
+ if (!systemAngleSupported) {
+ return false;
+ }
+
+ // If we make it to here, system ANGLE will be used. Call nativeSetAngleInfo() with
+ // the application package name and ANGLE features to use.
+ final String[] features = getAngleEglFeatures(context, bundle);
+ nativeSetAngleInfo("system", false, packageName, features);
+ return true;
+ }
+
+ /**
* Determine if the "ANGLE In Use" dialog box should be shown.
*/
private boolean shouldShowAngleInUseDialogBox(Context context) {
@@ -651,7 +711,9 @@
final Intent intent = new Intent(ACTION_ANGLE_FOR_ANDROID_TOAST_MESSAGE);
final String anglePkg = getAnglePackageName(context.getPackageManager());
- intent.setPackage(anglePkg);
+ if (anglePkg.isEmpty()) {
+ return;
+ }
context.sendOrderedBroadcast(intent, null, new BroadcastReceiver() {
@Override
@@ -890,8 +952,8 @@
private static native void setDriverPathAndSphalLibraries(String path, String sphalLibraries);
private static native void setGpuStats(String driverPackageName, String driverVersionName,
long driverVersionCode, long driverBuildTime, String appPackageName, int vulkanVersion);
- private static native void setAngleInfo(String path, String packageName,
- String devOptIn, String[] features);
+ private static native void nativeSetAngleInfo(String path, boolean useNativeDriver,
+ String packageName, String[] features);
private static native boolean setInjectLayersPrSetDumpable();
private static native void nativeToggleAngleAsSystemDriver(boolean enabled);
diff --git a/core/java/android/os/TEST_MAPPING b/core/java/android/os/TEST_MAPPING
index cc54266..5c4aa4a 100644
--- a/core/java/android/os/TEST_MAPPING
+++ b/core/java/android/os/TEST_MAPPING
@@ -52,8 +52,20 @@
],
"name": "FrameworksServicesTests",
"options": [
- { "include-filter": "com.android.server.am.BatteryStatsServiceTest" },
- { "include-filter": "com.android.server.power.stats.BatteryStatsTests" }
+ { "include-filter": "com.android.server.am.BatteryStatsServiceTest" }
+ ]
+ },
+ {
+ "file_patterns": [
+ "BatteryStats[^/]*\\.java",
+ "BatteryUsageStats[^/]*\\.java",
+ "PowerComponents\\.java",
+ "[^/]*BatteryConsumer[^/]*\\.java"
+ ],
+ "name": "FrameworksServicesTests",
+ "options": [
+ { "include-filter": "com.android.server.power.stats" },
+ { "exclude-filter": "com.android.server.power.stats.BatteryStatsTests" }
]
},
{
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index d425bf8..820f454 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -12613,6 +12613,26 @@
public static final String MOBILE_DATA_ALWAYS_ON = "mobile_data_always_on";
/**
+ * The duration in milliseconds of each action, separated by commas. Ex:
+ *
+ * "18000,18000,18000,18000,0"
+ *
+ * See com.android.internal.telephony.data.DataStallRecoveryManager for more info
+ * @hide
+ */
+ public static final String DSRM_DURATION_MILLIS = "dsrm_duration_millis";
+
+ /**
+ * The list of DSRM enabled actions, separated by commas. Ex:
+ *
+ * "true,true,false,true,true"
+ *
+ * See com.android.internal.telephony.data.DataStallRecoveryManager for more info
+ * @hide
+ */
+ public static final String DSRM_ENABLED_ACTIONS = "dsrm_enabled_actions";
+
+ /**
* Whether the wifi data connection should remain active even when higher
* priority networks like Ethernet are active, to keep both networks.
* In the case where higher priority networks are connected, wifi will be
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 5e7f5d6..9b19937 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -26,7 +26,6 @@
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.TestApi;
import android.app.Activity;
-import android.app.ActivityTaskManager;
import android.app.AlarmManager;
import android.app.Service;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1268,9 +1267,7 @@
fetchDreamLabel(this, serviceInfo, isPreviewMode));
try {
- if (!ActivityTaskManager.getService().startDreamActivity(i)) {
- detach();
- }
+ mDreamManager.startDreamActivity(i);
} catch (SecurityException e) {
Log.w(mTag,
"Received SecurityException trying to start DreamActivity. "
diff --git a/core/java/android/service/dreams/IDreamManager.aidl b/core/java/android/service/dreams/IDreamManager.aidl
index 609425c..dd8b3de 100644
--- a/core/java/android/service/dreams/IDreamManager.aidl
+++ b/core/java/android/service/dreams/IDreamManager.aidl
@@ -17,6 +17,7 @@
package android.service.dreams;
import android.content.ComponentName;
+import android.content.Intent;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.IBinder;
@@ -45,4 +46,5 @@
void setDreamComponentsForUser(int userId, in ComponentName[] componentNames);
void setSystemDreamComponent(in ComponentName componentName);
void registerDreamOverlayService(in ComponentName componentName);
+ void startDreamActivity(in Intent intent);
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index dbc1be1..8eb02a1 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -432,8 +432,7 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
- boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
- int syncSeqId, boolean dragResizing) {
+ boolean forceLayout, int displayId, int syncSeqId, boolean dragResizing) {
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0,
mergedConfiguration);
@@ -1287,9 +1286,9 @@
visibleFrame.intersect(mInsetsState.getDisplayFrame());
WindowInsets windowInsets = mInsetsState.calculateInsets(visibleFrame,
null /* ignoringVisibilityState */, config.isScreenRound(),
- false /* alwaysConsumeSystemBars */, mLayout.softInputMode,
- mLayout.flags, SYSTEM_UI_FLAG_VISIBLE, mLayout.type,
- config.windowConfiguration.getWindowingMode(), null /* idSideMap */);
+ mLayout.softInputMode, mLayout.flags, SYSTEM_UI_FLAG_VISIBLE,
+ mLayout.type, config.windowConfiguration.getWindowingMode(),
+ null /* idSideMap */);
if (!fixedSize) {
final Rect padding = mIWallpaperEngine.mDisplayPadding;
diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java
index 6371da4..ab9cff0 100644
--- a/core/java/android/text/StaticLayout.java
+++ b/core/java/android/text/StaticLayout.java
@@ -25,7 +25,6 @@
import android.graphics.text.LineBreakConfig;
import android.graphics.text.LineBreaker;
import android.os.Build;
-import android.os.SystemProperties;
import android.text.style.LeadingMarginSpan;
import android.text.style.LeadingMarginSpan.LeadingMarginSpan2;
import android.text.style.LineHeightSpan;
@@ -33,7 +32,6 @@
import android.util.Log;
import android.util.Pools.SynchronizedPool;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.GrowingArrayUtils;
@@ -75,13 +73,6 @@
* default values.
*/
public final static class Builder {
- // The content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
- private static final int DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE = 3;
-
- // The property of content length threshold to enable LINE_BREAK_WORD_STYLE_PHRASE.
- private static final String PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE =
- "android.phrase.linecount.threshold";
-
private Builder() {}
/**
@@ -440,55 +431,11 @@
*/
@NonNull
public StaticLayout build() {
- reviseLineBreakConfig();
StaticLayout result = new StaticLayout(this);
Builder.recycle(this);
return result;
}
- private void reviseLineBreakConfig() {
- boolean autoPhraseBreaking = mLineBreakConfig.getAutoPhraseBreaking();
- int wordStyle = mLineBreakConfig.getLineBreakWordStyle();
- if (autoPhraseBreaking) {
- if (wordStyle != LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE) {
- if (shouldEnablePhraseBreaking()) {
- mLineBreakConfig = LineBreakConfig.getLineBreakConfig(
- mLineBreakConfig.getLineBreakStyle(),
- LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
- mLineBreakConfig.getAutoPhraseBreaking());
- }
- }
- }
- }
-
- private boolean shouldEnablePhraseBreaking() {
- if (TextUtils.isEmpty(mText) || mWidth <= 0) {
- return false;
- }
- int lineLimit = SystemProperties.getInt(
- PROPERTY_LINECOUNT_THRESHOLD_FOR_PHRASE,
- DEFAULT_LINECOUNT_THRESHOLD_FOR_PHRASE);
- double desiredWidth = (double) Layout.getDesiredWidth(mText, mStart,
- mEnd, mPaint, mTextDir);
- int lineCount = (int) Math.ceil(desiredWidth / mWidth);
- if (lineCount > 0 && lineCount <= lineLimit) {
- return true;
- }
- return false;
- }
-
- /**
- * Get the line break word style.
- *
- * @return The current line break word style.
- *
- * @hide
- */
- @VisibleForTesting
- public int getLineBreakWordStyle() {
- return mLineBreakConfig.getLineBreakWordStyle();
- }
-
private CharSequence mText;
private int mStart;
private int mEnd;
diff --git a/core/java/android/util/FeatureFlagUtils.java b/core/java/android/util/FeatureFlagUtils.java
index ff7d8bb..0071a0d 100644
--- a/core/java/android/util/FeatureFlagUtils.java
+++ b/core/java/android/util/FeatureFlagUtils.java
@@ -86,9 +86,6 @@
public static final String SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST =
"settings_need_connected_ble_device_for_broadcast";
- /** @hide */
- public static final String SETTINGS_AUTO_TEXT_WRAPPING = "settings_auto_text_wrapping";
-
/**
* Enable new language and keyboard settings UI
* @hide
@@ -225,7 +222,6 @@
DEFAULT_FLAGS.put(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS, "true");
DEFAULT_FLAGS.put(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME, "true");
DEFAULT_FLAGS.put(SETTINGS_NEED_CONNECTED_BLE_DEVICE_FOR_BROADCAST, "true");
- DEFAULT_FLAGS.put(SETTINGS_AUTO_TEXT_WRAPPING, "false");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_UI, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY, "true");
DEFAULT_FLAGS.put(SETTINGS_NEW_KEYBOARD_TRACKPAD, "true");
@@ -253,7 +249,6 @@
PERSISTENT_FLAGS.add(SETTINGS_SUPPORT_LARGE_SCREEN);
PERSISTENT_FLAGS.add(SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS);
PERSISTENT_FLAGS.add(SETTINGS_APP_ALLOW_DARK_THEME_ACTIVATION_AT_BEDTIME);
- PERSISTENT_FLAGS.add(SETTINGS_AUTO_TEXT_WRAPPING);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_UI);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_MODIFIER_KEY);
PERSISTENT_FLAGS.add(SETTINGS_NEW_KEYBOARD_TRACKPAD);
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
index d554514..33edc27 100644
--- a/core/java/android/view/IWindow.aidl
+++ b/core/java/android/view/IWindow.aidl
@@ -56,8 +56,7 @@
void resized(in ClientWindowFrames frames, boolean reportDraw,
in MergedConfiguration newMergedConfiguration, in InsetsState insetsState,
- boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId,
- int syncSeqId, boolean dragResizing);
+ boolean forceLayout, int displayId, int syncSeqId, boolean dragResizing);
/**
* Called when this window retrieved control over a specified set of insets sources.
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index 6d96bb94..8813c39 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -737,10 +737,8 @@
/**
* Called to get the expected window insets.
- *
- * @return {@code true} if system bars are always consumed.
*/
- boolean getWindowInsets(int displayId, in IBinder token, out InsetsState outInsetsState);
+ void getWindowInsets(int displayId, in IBinder token, out InsetsState outInsetsState);
/**
* Returns a list of {@link android.view.DisplayInfo} for the logical display. This is not
diff --git a/core/java/android/view/InsetsAnimationControlImpl.java b/core/java/android/view/InsetsAnimationControlImpl.java
index 6c5f195..4c50858 100644
--- a/core/java/android/view/InsetsAnimationControlImpl.java
+++ b/core/java/android/view/InsetsAnimationControlImpl.java
@@ -40,6 +40,7 @@
import static android.view.InsetsState.ISIDE_RIGHT;
import static android.view.InsetsState.ISIDE_TOP;
import static android.view.WindowInsets.Type.ime;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
import static android.view.inputmethod.ImeTracker.DEBUG_IME_VISIBILITY;
import static android.view.inputmethod.ImeTracker.TOKEN_NONE;
@@ -63,7 +64,6 @@
import android.view.SyncRtSurfaceTransactionApplier.SurfaceParams;
import android.view.WindowInsets.Type.InsetsType;
import android.view.WindowInsetsAnimation.Bounds;
-import android.view.WindowManager.LayoutParams;
import android.view.animation.Interpolator;
import android.view.inputmethod.ImeTracker;
@@ -401,8 +401,7 @@
private Insets getInsetsFromState(InsetsState state, Rect frame,
@Nullable @InternalInsetsSide SparseIntArray idSideMap) {
return state.calculateInsets(frame, null /* ignoringVisibilityState */,
- false /* isScreenRound */, false /* alwaysConsumeSystemBars */,
- LayoutParams.SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode*/,
+ false /* isScreenRound */, SOFT_INPUT_ADJUST_RESIZE /* legacySoftInputMode */,
0 /* legacyWindowFlags */, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, idSideMap).getInsets(mTypes);
}
diff --git a/core/java/android/view/InsetsController.java b/core/java/android/view/InsetsController.java
index 5019b85..e5d031b 100644
--- a/core/java/android/view/InsetsController.java
+++ b/core/java/android/view/InsetsController.java
@@ -797,9 +797,9 @@
}
WindowInsets insets = state.calculateInsets(mFrame, mState /* ignoringVisibilityState*/,
- mLastInsets.isRound(), mLastInsets.shouldAlwaysConsumeSystemBars(),
- mLastLegacySoftInputMode, mLastLegacyWindowFlags, mLastLegacySystemUiFlags,
- mWindowType, mLastWindowingMode, null /* idSideMap */);
+ mLastInsets.isRound(), mLastLegacySoftInputMode, mLastLegacyWindowFlags,
+ mLastLegacySystemUiFlags, mWindowType, mLastWindowingMode,
+ null /* idSideMap */);
mHost.dispatchWindowInsetsAnimationProgress(insets,
Collections.unmodifiableList(runningAnimations));
if (DEBUG) {
@@ -955,21 +955,20 @@
}
/**
- * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, boolean, int, int, int, int,
- * int, android.util.SparseIntArray)
+ * @see InsetsState#calculateInsets(Rect, InsetsState, boolean, int, int, int, int, int,
+ * android.util.SparseIntArray)
*/
@VisibleForTesting
- public WindowInsets calculateInsets(boolean isScreenRound, boolean alwaysConsumeSystemBars,
- int windowType, int windowingMode, int legacySoftInputMode, int legacyWindowFlags,
- int legacySystemUiFlags) {
+ public WindowInsets calculateInsets(boolean isScreenRound, int windowType, int windowingMode,
+ int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags) {
mWindowType = windowType;
mLastWindowingMode = windowingMode;
mLastLegacySoftInputMode = legacySoftInputMode;
mLastLegacyWindowFlags = legacyWindowFlags;
mLastLegacySystemUiFlags = legacySystemUiFlags;
mLastInsets = mState.calculateInsets(mFrame, null /* ignoringVisibilityState*/,
- isScreenRound, alwaysConsumeSystemBars, legacySoftInputMode, legacyWindowFlags,
- legacySystemUiFlags, windowType, windowingMode, null /* idSideMap */);
+ isScreenRound, legacySoftInputMode, legacyWindowFlags, legacySystemUiFlags,
+ windowType, windowingMode, null /* idSideMap */);
return mLastInsets;
}
diff --git a/core/java/android/view/InsetsSource.java b/core/java/android/view/InsetsSource.java
index e101849..6441186 100644
--- a/core/java/android/view/InsetsSource.java
+++ b/core/java/android/view/InsetsSource.java
@@ -68,10 +68,16 @@
*/
public static final int FLAG_INSETS_ROUNDED_CORNER = 1 << 1;
+ /**
+ * Controls whether the insets provided by this source should be forcibly consumed.
+ */
+ public static final int FLAG_FORCE_CONSUMING = 1 << 2;
+
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, prefix = "FLAG_", value = {
FLAG_SUPPRESS_SCRIM,
FLAG_INSETS_ROUNDED_CORNER,
+ FLAG_FORCE_CONSUMING,
})
public @interface Flags {}
@@ -328,6 +334,9 @@
if ((flags & FLAG_INSETS_ROUNDED_CORNER) != 0) {
joiner.add("INSETS_ROUNDED_CORNER");
}
+ if ((flags & FLAG_FORCE_CONSUMING) != 0) {
+ joiner.add("FORCE_CONSUMING");
+ }
return joiner.toString();
}
diff --git a/core/java/android/view/InsetsState.java b/core/java/android/view/InsetsState.java
index dceae90..828938b 100644
--- a/core/java/android/view/InsetsState.java
+++ b/core/java/android/view/InsetsState.java
@@ -16,6 +16,7 @@
package android.view;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.FLAG_INSETS_ROUNDED_CORNER;
import static android.view.InsetsStateProto.DISPLAY_CUTOUT;
import static android.view.InsetsStateProto.DISPLAY_FRAME;
@@ -39,6 +40,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.WindowConfiguration;
+import android.app.WindowConfiguration.WindowingMode;
import android.graphics.Insets;
import android.graphics.Rect;
import android.os.Parcel;
@@ -135,21 +137,26 @@
* @return The calculated insets.
*/
public WindowInsets calculateInsets(Rect frame, @Nullable InsetsState ignoringVisibilityState,
- boolean isScreenRound, boolean alwaysConsumeSystemBars,
- int legacySoftInputMode, int legacyWindowFlags, int legacySystemUiFlags,
- int windowType, @WindowConfiguration.WindowingMode int windowingMode,
+ boolean isScreenRound, int legacySoftInputMode, int legacyWindowFlags,
+ int legacySystemUiFlags, int windowType, @WindowingMode int windowingMode,
@Nullable @InternalInsetsSide SparseIntArray idSideMap) {
Insets[] typeInsetsMap = new Insets[Type.SIZE];
Insets[] typeMaxInsetsMap = new Insets[Type.SIZE];
boolean[] typeVisibilityMap = new boolean[Type.SIZE];
final Rect relativeFrame = new Rect(frame);
final Rect relativeFrameMax = new Rect(frame);
+ @InsetsType int forceConsumingTypes = 0;
@InsetsType int suppressScrimTypes = 0;
for (int i = mSources.size() - 1; i >= 0; i--) {
final InsetsSource source = mSources.valueAt(i);
+ final @InsetsType int type = source.getType();
+
+ if ((source.getFlags() & InsetsSource.FLAG_FORCE_CONSUMING) != 0) {
+ forceConsumingTypes |= type;
+ }
if ((source.getFlags() & InsetsSource.FLAG_SUPPRESS_SCRIM) != 0) {
- suppressScrimTypes |= source.getType();
+ suppressScrimTypes |= type;
}
processSource(source, relativeFrame, false /* ignoreVisibility */, typeInsetsMap,
@@ -157,7 +164,7 @@
// IME won't be reported in max insets as the size depends on the EditorInfo of the IME
// target.
- if (source.getType() != WindowInsets.Type.ime()) {
+ if (type != WindowInsets.Type.ime()) {
InsetsSource ignoringVisibilitySource = ignoringVisibilityState != null
? ignoringVisibilityState.peekSource(source.getId())
: source;
@@ -178,13 +185,13 @@
if ((legacyWindowFlags & FLAG_FULLSCREEN) != 0) {
compatInsetsTypes &= ~statusBars();
}
- if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)
- && !alwaysConsumeSystemBars) {
- compatInsetsTypes = 0;
+ if (clearsCompatInsets(windowType, legacyWindowFlags, windowingMode)) {
+ // Clear all types but forceConsumingTypes.
+ compatInsetsTypes &= forceConsumingTypes;
}
return new WindowInsets(typeInsetsMap, typeMaxInsetsMap, typeVisibilityMap, isScreenRound,
- alwaysConsumeSystemBars, suppressScrimTypes, calculateRelativeCutout(frame),
+ forceConsumingTypes, suppressScrimTypes, calculateRelativeCutout(frame),
calculateRelativeRoundedCorners(frame),
calculateRelativePrivacyIndicatorBounds(frame),
calculateRelativeDisplayShape(frame),
@@ -290,9 +297,8 @@
public Insets calculateVisibleInsets(Rect frame, int windowType, int windowingMode,
@SoftInputModeFlags int softInputMode, int windowFlags) {
- if (clearsCompatInsets(windowType, windowFlags, windowingMode)) {
- return Insets.NONE;
- }
+ final boolean clearsCompatInsets = clearsCompatInsets(
+ windowType, windowFlags, windowingMode);
final int softInputAdjustMode = softInputMode & SOFT_INPUT_MASK_ADJUST;
final int visibleInsetsTypes = softInputAdjustMode != SOFT_INPUT_ADJUST_NOTHING
? systemBars() | ime()
@@ -303,6 +309,9 @@
if ((source.getType() & visibleInsetsTypes) == 0) {
continue;
}
+ if (clearsCompatInsets && !source.hasFlags(FLAG_FORCE_CONSUMING)) {
+ continue;
+ }
insets = Insets.max(source.calculateVisibleInsets(frame), insets);
}
return insets;
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index f1cde3b..f51e3a3 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -30744,13 +30744,6 @@
final Rect mCaptionInsets = new Rect();
/**
- * In multi-window we force show the system bars. Because we don't want that the surface
- * size changes in this mode, we instead have a flag whether the system bars sizes should
- * always be consumed, so the app is treated like there are no virtual system bars at all.
- */
- boolean mAlwaysConsumeSystemBars;
-
- /**
* The internal insets given by this window. This value is
* supplied by the client (through
* {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 9316dbf..ef76f4a 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -82,7 +82,6 @@
import static android.view.WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY;
import static android.view.WindowManager.PROPERTY_COMPAT_ALLOW_SANDBOXING_VIEW_BOUNDS_APIS;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_CANCEL_AND_REDRAW;
-import static android.view.WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
import static android.view.WindowManagerGlobal.RELAYOUT_RES_SURFACE_CHANGED;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.IME_FOCUS_CONTROLLER;
import static android.view.inputmethod.InputMethodEditorTraceProto.InputMethodClientsTraceProto.ClientSideProto.INSETS_CONTROLLER;
@@ -741,7 +740,6 @@
final Rect mPendingBackDropFrame = new Rect();
- boolean mPendingAlwaysConsumeSystemBars;
private int mRelayoutSeq;
private final Rect mWinFrameInScreen = new Rect();
private final InsetsState mTempInsets = new InsetsState();
@@ -1366,9 +1364,6 @@
}
}
- mAttachInfo.mAlwaysConsumeSystemBars =
- (res & WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS) != 0;
- mPendingAlwaysConsumeSystemBars = mAttachInfo.mAlwaysConsumeSystemBars;
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls.get());
final InsetsState state = mInsetsController.getState();
@@ -1882,8 +1877,8 @@
final MergedConfiguration mergedConfiguration = (MergedConfiguration) args.arg2;
CompatibilityInfo.applyOverrideScaleIfNeeded(mergedConfiguration);
final boolean forceNextWindowRelayout = args.argi1 != 0;
- final int displayId = args.argi3;
- final boolean dragResizing = args.argi5 != 0;
+ final int displayId = args.argi2;
+ final boolean dragResizing = args.argi4 != 0;
final Rect frame = frames.frame;
final Rect displayFrame = frames.displayFrame;
@@ -1935,8 +1930,7 @@
}
mForceNextWindowRelayout |= forceNextWindowRelayout;
- mPendingAlwaysConsumeSystemBars = args.argi2 != 0;
- mSyncSeqId = args.argi4 > mSyncSeqId ? args.argi4 : mSyncSeqId;
+ mSyncSeqId = args.argi3 > mSyncSeqId ? args.argi3 : mSyncSeqId;
if (msg == MSG_RESIZED_REPORT) {
reportNextDraw("resized");
@@ -2821,8 +2815,8 @@
if (mLastWindowInsets == null || forceConstruct) {
final Configuration config = getConfiguration();
mLastWindowInsets = mInsetsController.calculateInsets(
- config.isScreenRound(), mAttachInfo.mAlwaysConsumeSystemBars,
- mWindowAttributes.type, config.windowConfiguration.getWindowingMode(),
+ config.isScreenRound(), mWindowAttributes.type,
+ config.windowConfiguration.getWindowingMode(),
mWindowAttributes.softInputMode, mWindowAttributes.flags,
(mWindowAttributes.systemUiVisibility
| mWindowAttributes.subtreeSystemUiVisibility));
@@ -3284,8 +3278,6 @@
surfaceSizeChanged = true;
mLastSurfaceSize.set(mSurfaceSize.x, mSurfaceSize.y);
}
- final boolean alwaysConsumeSystemBarsChanged =
- mPendingAlwaysConsumeSystemBars != mAttachInfo.mAlwaysConsumeSystemBars;
updateColorModeIfNeeded(lp.getColorMode());
surfaceCreated = !hadSurface && mSurface.isValid();
surfaceDestroyed = hadSurface && !mSurface.isValid();
@@ -3299,10 +3291,6 @@
if (surfaceReplaced) {
mSurfaceSequenceId++;
}
- if (alwaysConsumeSystemBarsChanged) {
- mAttachInfo.mAlwaysConsumeSystemBars = mPendingAlwaysConsumeSystemBars;
- dispatchApplyInsets = true;
- }
if (updateCaptionInsets()) {
dispatchApplyInsets = true;
}
@@ -3855,7 +3843,15 @@
mWmsRequestSyncGroupState = WMS_SYNC_PENDING;
mWmsRequestSyncGroup = new SurfaceSyncGroup("wmsSync-" + mTag, t -> {
mWmsRequestSyncGroupState = WMS_SYNC_MERGED;
- reportDrawFinished(t, seqId);
+ // See b/286355097. If the current process is not system, then invoking finishDraw on
+ // any thread is fine since once it calls into system process, finishDrawing will run
+ // on a different thread. However, when the current process is system, the finishDraw in
+ // system server will be run on the current thread, which could result in a deadlock.
+ if (mWindowSession instanceof Binder) {
+ reportDrawFinished(t, seqId);
+ } else {
+ mHandler.postAtFrontOfQueue(() -> reportDrawFinished(t, seqId));
+ }
});
if (DEBUG_BLAST) {
Log.d(mTag, "Setup new sync=" + mWmsRequestSyncGroup.getName());
@@ -8293,9 +8289,6 @@
CompatibilityInfo.applyOverrideScaleIfNeeded(mPendingMergedConfiguration);
mInsetsController.onStateChanged(mTempInsets);
mInsetsController.onControlsChanged(mTempControls.get());
-
- mPendingAlwaysConsumeSystemBars =
- (relayoutResult & RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS) != 0;
}
final int transformHint = SurfaceControl.rotationToBufferTransform(
@@ -8918,7 +8911,7 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchResized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
- boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, boolean dragResizing) {
+ int displayId, int syncSeqId, boolean dragResizing) {
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
SomeArgs args = SomeArgs.obtain();
final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
@@ -8937,10 +8930,9 @@
? new MergedConfiguration(mergedConfiguration) : mergedConfiguration;
args.arg3 = insetsState;
args.argi1 = forceLayout ? 1 : 0;
- args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
- args.argi3 = displayId;
- args.argi4 = syncSeqId;
- args.argi5 = dragResizing ? 1 : 0;
+ args.argi2 = displayId;
+ args.argi3 = syncSeqId;
+ args.argi4 = dragResizing ? 1 : 0;
msg.obj = args;
mHandler.sendMessage(msg);
@@ -10334,12 +10326,11 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
- boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
- boolean dragResizing) {
+ boolean forceLayout, int displayId, int syncSeqId, boolean dragResizing) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, insetsState,
- forceLayout, alwaysConsumeSystemBars, displayId, syncSeqId, dragResizing);
+ forceLayout, displayId, syncSeqId, dragResizing);
}
}
diff --git a/core/java/android/view/WindowInsets.java b/core/java/android/view/WindowInsets.java
index 4acaea8..7757c25 100644
--- a/core/java/android/view/WindowInsets.java
+++ b/core/java/android/view/WindowInsets.java
@@ -84,13 +84,7 @@
@Nullable private final PrivacyIndicatorBounds mPrivacyIndicatorBounds;
@Nullable private final DisplayShape mDisplayShape;
- /**
- * In multi-window we force show the navigation bar. Because we don't want that the surface size
- * changes in this mode, we instead have a flag whether the navigation bar size should always
- * be consumed, so the app is treated like there is no virtual navigation bar at all.
- */
- private final boolean mAlwaysConsumeSystemBars;
-
+ private final @InsetsType int mForceConsumingTypes;
private final @InsetsType int mSuppressScrimTypes;
private final boolean mSystemWindowInsetsConsumed;
private final boolean mStableInsetsConsumed;
@@ -117,7 +111,7 @@
static {
CONSUMED = new WindowInsets(createCompatTypeMap(null), createCompatTypeMap(null),
- createCompatVisibilityMap(createCompatTypeMap(null)), false, false, 0, null,
+ createCompatVisibilityMap(createCompatTypeMap(null)), false, 0, 0, null,
null, null, null, systemBars(), false);
}
@@ -137,7 +131,8 @@
@Nullable Insets[] typeMaxInsetsMap,
boolean[] typeVisibilityMap,
boolean isRound,
- boolean alwaysConsumeSystemBars, @InsetsType int suppressScrimTypes,
+ @InsetsType int forceConsumingTypes,
+ @InsetsType int suppressScrimTypes,
DisplayCutout displayCutout,
RoundedCorners roundedCorners,
PrivacyIndicatorBounds privacyIndicatorBounds,
@@ -155,7 +150,7 @@
mTypeVisibilityMap = typeVisibilityMap;
mIsRound = isRound;
- mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ mForceConsumingTypes = forceConsumingTypes;
mSuppressScrimTypes = suppressScrimTypes;
mCompatInsetsTypes = compatInsetsTypes;
mCompatIgnoreVisibility = compatIgnoreVisibility;
@@ -178,7 +173,7 @@
this(src.mSystemWindowInsetsConsumed ? null : src.mTypeInsetsMap,
src.mStableInsetsConsumed ? null : src.mTypeMaxInsetsMap,
src.mTypeVisibilityMap, src.mIsRound,
- src.mAlwaysConsumeSystemBars, src.mSuppressScrimTypes,
+ src.mForceConsumingTypes, src.mSuppressScrimTypes,
displayCutoutCopyConstructorArgument(src),
src.mRoundedCorners,
src.mPrivacyIndicatorBounds,
@@ -235,7 +230,7 @@
/** @hide */
@UnsupportedAppUsage
public WindowInsets(Rect systemWindowInsets) {
- this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, false, 0,
+ this(createCompatTypeMap(systemWindowInsets), null, new boolean[SIZE], false, 0, 0,
null, null, null, null, systemBars(), false /* compatIgnoreVisibility */);
}
@@ -556,7 +551,7 @@
return new WindowInsets(mSystemWindowInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap,
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
null /* displayCutout */, mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape,
mCompatInsetsTypes, mCompatIgnoreVisibility);
}
@@ -607,7 +602,7 @@
public WindowInsets consumeSystemWindowInsets() {
return new WindowInsets(null, null,
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
// If the system window insets types contain displayCutout, we should also consume
// it.
(mCompatInsetsTypes & displayCutout()) != 0
@@ -895,8 +890,8 @@
/**
* @hide
*/
- public boolean shouldAlwaysConsumeSystemBars() {
- return mAlwaysConsumeSystemBars;
+ public @InsetsType int getForceConsumingTypes() {
+ return mForceConsumingTypes;
}
/**
@@ -930,6 +925,8 @@
result.append("\n ");
result.append(mDisplayShape != null ? "displayShape=" + mDisplayShape : "");
result.append("\n ");
+ result.append("forceConsumingTypes=" + Type.toString(mForceConsumingTypes));
+ result.append("\n ");
result.append("suppressScrimTypes=" + Type.toString(mSuppressScrimTypes));
result.append("\n ");
result.append("compatInsetsTypes=" + Type.toString(mCompatInsetsTypes));
@@ -1027,7 +1024,7 @@
? null
: insetInsets(mTypeMaxInsetsMap, left, top, right, bottom),
mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes,
mDisplayCutoutConsumed
? null
: mDisplayCutout == null
@@ -1050,7 +1047,7 @@
WindowInsets that = (WindowInsets) o;
return mIsRound == that.mIsRound
- && mAlwaysConsumeSystemBars == that.mAlwaysConsumeSystemBars
+ && mForceConsumingTypes == that.mForceConsumingTypes
&& mSuppressScrimTypes == that.mSuppressScrimTypes
&& mSystemWindowInsetsConsumed == that.mSystemWindowInsetsConsumed
&& mStableInsetsConsumed == that.mStableInsetsConsumed
@@ -1068,7 +1065,7 @@
public int hashCode() {
return Objects.hash(Arrays.hashCode(mTypeInsetsMap), Arrays.hashCode(mTypeMaxInsetsMap),
Arrays.hashCode(mTypeVisibilityMap), mIsRound, mDisplayCutout, mRoundedCorners,
- mAlwaysConsumeSystemBars, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
+ mForceConsumingTypes, mSuppressScrimTypes, mSystemWindowInsetsConsumed,
mStableInsetsConsumed, mDisplayCutoutConsumed, mPrivacyIndicatorBounds,
mDisplayShape);
}
@@ -1134,7 +1131,7 @@
private DisplayShape mDisplayShape = DisplayShape.NONE;
private boolean mIsRound;
- private boolean mAlwaysConsumeSystemBars;
+ private @InsetsType int mForceConsumingTypes;
private @InsetsType int mSuppressScrimTypes;
private PrivacyIndicatorBounds mPrivacyIndicatorBounds = new PrivacyIndicatorBounds();
@@ -1162,7 +1159,7 @@
mDisplayCutout = displayCutoutCopyConstructorArgument(insets);
mRoundedCorners = insets.mRoundedCorners;
mIsRound = insets.mIsRound;
- mAlwaysConsumeSystemBars = insets.mAlwaysConsumeSystemBars;
+ mForceConsumingTypes = insets.mForceConsumingTypes;
mSuppressScrimTypes = insets.mSuppressScrimTypes;
mPrivacyIndicatorBounds = insets.mPrivacyIndicatorBounds;
mDisplayShape = insets.mDisplayShape;
@@ -1432,8 +1429,8 @@
/** @hide */
@NonNull
- public Builder setAlwaysConsumeSystemBars(boolean alwaysConsumeSystemBars) {
- mAlwaysConsumeSystemBars = alwaysConsumeSystemBars;
+ public Builder setForceConsumingTypes(@InsetsType int forceConsumingTypes) {
+ mForceConsumingTypes = forceConsumingTypes;
return this;
}
@@ -1453,7 +1450,7 @@
public WindowInsets build() {
return new WindowInsets(mSystemInsetsConsumed ? null : mTypeInsetsMap,
mStableInsetsConsumed ? null : mTypeMaxInsetsMap, mTypeVisibilityMap,
- mIsRound, mAlwaysConsumeSystemBars, mSuppressScrimTypes, mDisplayCutout,
+ mIsRound, mForceConsumingTypes, mSuppressScrimTypes, mDisplayCutout,
mRoundedCorners, mPrivacyIndicatorBounds, mDisplayShape, systemBars(),
false /* compatIgnoreVisibility */);
}
diff --git a/core/java/android/view/WindowManagerGlobal.java b/core/java/android/view/WindowManagerGlobal.java
index 99a4f6b..c9368f1 100644
--- a/core/java/android/view/WindowManagerGlobal.java
+++ b/core/java/android/view/WindowManagerGlobal.java
@@ -79,16 +79,9 @@
public static final int RELAYOUT_RES_SURFACE_RESIZED = 1 << 2;
/**
- * In multi-window we force show the system bars. Because we don't want that the surface size
- * changes in this mode, we instead have a flag whether the system bar sizes should always be
- * consumed, so the app is treated like there is no virtual system bars at all.
- */
- public static final int RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS = 1 << 3;
-
- /**
* The window manager has told the window it cannot draw this frame and should retry again.
*/
- public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 4;
+ public static final int RELAYOUT_RES_CANCEL_AND_REDRAW = 1 << 3;
/**
* Flag for relayout: the client will be later giving
@@ -99,13 +92,7 @@
public static final int ADD_FLAG_IN_TOUCH_MODE = 0x1;
public static final int ADD_FLAG_APP_VISIBLE = 0x2;
- public static final int ADD_FLAG_USE_BLAST = 0x8;
-
- /**
- * Like {@link #RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS}, but as a "hint" when adding the
- * window.
- */
- public static final int ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS = 0x4;
+ public static final int ADD_FLAG_USE_BLAST = 0x4;
public static final int ADD_OKAY = 0;
public static final int ADD_BAD_APP_TOKEN = -1;
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 7d3d283..1efac4d 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -631,8 +631,8 @@
mTmpFrames.displayFrame.set(mTmpFrames.frame);
mTmpConfig.setConfiguration(mConfiguration, mConfiguration);
s.mClient.resized(mTmpFrames, false /* reportDraw */, mTmpConfig, state,
- false /* forceLayout */, false /* alwaysConsumeSystemBars */, s.mDisplayId,
- Integer.MAX_VALUE, false /* dragResizing */);
+ false /* forceLayout */, s.mDisplayId, Integer.MAX_VALUE,
+ false /* dragResizing */);
} catch (RemoteException e) {
// Too bad
}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index 40b060a..3f308e6 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -2657,17 +2657,6 @@
}
}
- if (windowGainingFocus == null) {
- windowGainingFocus = view.getWindowToken();
- if (windowGainingFocus == null) {
- Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
- return false;
- }
- startInputFlags = getStartInputFlags(view, startInputFlags);
- softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
- windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
- }
-
// Now we need to get an input connection from the served view.
// This is complicated in a couple ways: we can't be holding our lock
// when calling out to the view, and we need to make sure we call into
@@ -2690,6 +2679,17 @@
return false;
}
+ if (windowGainingFocus == null) {
+ windowGainingFocus = view.getWindowToken();
+ if (windowGainingFocus == null) {
+ Log.e(TAG, "ABORT input: ServedView must be attached to a Window");
+ return false;
+ }
+ startInputFlags = getStartInputFlags(view, startInputFlags);
+ softInputMode = view.getViewRootImpl().mWindowAttributes.softInputMode;
+ windowFlags = view.getViewRootImpl().mWindowAttributes.flags;
+ }
+
// Okay we are now ready to call into the served view and have it
// do its stuff.
// Life is good: let's hook everything up!
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 438b974..27d8a8f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -156,7 +156,6 @@
import android.util.ArraySet;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
-import android.util.FeatureFlagUtils;
import android.util.IntArray;
import android.util.Log;
import android.util.SparseIntArray;
@@ -831,11 +830,6 @@
private int mLineBreakStyle = DEFAULT_LINE_BREAK_STYLE;
private int mLineBreakWordStyle = DEFAULT_LINE_BREAK_WORD_STYLE;
- // The auto option for LINE_BREAK_WORD_STYLE_PHRASE may not be applied in recycled view due to
- // one-way flag flipping. This is a tentative limitation during experiment and will not have the
- // issue once this is finalized to LINE_BREAK_WORD_STYLE_PHRASE_AUTO option.
- private boolean mUserSpeficiedLineBreakwordStyle = false;
-
// This is used to reflect the current user preference for changing font weight and making text
// more bold.
private int mFontWeightAdjustment;
@@ -1546,9 +1540,6 @@
break;
case com.android.internal.R.styleable.TextView_lineBreakWordStyle:
- if (a.hasValue(attr)) {
- mUserSpeficiedLineBreakwordStyle = true;
- }
mLineBreakWordStyle = a.getInt(attr,
LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE);
break;
@@ -4350,7 +4341,6 @@
break;
case com.android.internal.R.styleable.TextAppearance_lineBreakWordStyle:
attributes.mHasLineBreakWordStyle = true;
- mUserSpeficiedLineBreakwordStyle = true;
attributes.mLineBreakWordStyle =
appearance.getInt(attr, attributes.mLineBreakWordStyle);
break;
@@ -5086,7 +5076,6 @@
* @param lineBreakWordStyle The line-break word style for the text.
*/
public void setLineBreakWordStyle(@LineBreakConfig.LineBreakWordStyle int lineBreakWordStyle) {
- mUserSpeficiedLineBreakwordStyle = true;
if (mLineBreakWordStyle != lineBreakWordStyle) {
mLineBreakWordStyle = lineBreakWordStyle;
if (mLayout != null) {
@@ -5122,12 +5111,8 @@
* @see PrecomputedText
*/
public @NonNull PrecomputedText.Params getTextMetricsParams() {
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
return new PrecomputedText.Params(new TextPaint(mTextPaint),
- LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle,
- autoPhraseBreaking),
+ LineBreakConfig.getLineBreakConfig(mLineBreakStyle, mLineBreakWordStyle),
getTextDirectionHeuristic(),
mBreakStrategy, mHyphenationFrequency);
}
@@ -5147,7 +5132,6 @@
LineBreakConfig lineBreakConfig = params.getLineBreakConfig();
mLineBreakStyle = lineBreakConfig.getLineBreakStyle();
mLineBreakWordStyle = lineBreakConfig.getLineBreakWordStyle();
- mUserSpeficiedLineBreakwordStyle = true;
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -7077,13 +7061,10 @@
if (mTextDir == null) {
mTextDir = getTextDirectionHeuristic();
}
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
final @PrecomputedText.Params.CheckResultUsableResult int checkResult =
precomputed.getParams().checkResultUsable(getPaint(), mTextDir, mBreakStrategy,
mHyphenationFrequency, LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
switch (checkResult) {
case PrecomputedText.Params.UNUSABLE:
throw new IllegalArgumentException(
@@ -10640,9 +10621,6 @@
}
// TODO: code duplication with makeSingleLayout()
if (mHintLayout == null) {
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mHint, 0,
mHint.length(), mTextPaint, hintWidth)
.setAlignment(alignment)
@@ -10655,7 +10633,7 @@
.setJustificationMode(mJustificationMode)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
if (shouldEllipsize) {
builder.setEllipsize(mEllipsize)
.setEllipsizedWidth(ellipsisWidth);
@@ -10759,9 +10737,6 @@
}
}
if (result == null) {
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
StaticLayout.Builder builder = StaticLayout.Builder.obtain(mTransformed,
0, mTransformed.length(), mTextPaint, wantWidth)
.setAlignment(alignment)
@@ -10774,7 +10749,7 @@
.setJustificationMode(mJustificationMode)
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
if (shouldEllipsize) {
builder.setEllipsize(effectiveEllipsize)
.setEllipsizedWidth(ellipsisWidth);
@@ -11132,9 +11107,6 @@
final StaticLayout.Builder layoutBuilder = StaticLayout.Builder.obtain(
text, 0, text.length(), mTempTextPaint, Math.round(availableSpace.right));
- final boolean autoPhraseBreaking =
- !mUserSpeficiedLineBreakwordStyle && FeatureFlagUtils.isEnabled(mContext,
- FeatureFlagUtils.SETTINGS_AUTO_TEXT_WRAPPING);
layoutBuilder.setAlignment(getLayoutAlignment())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
@@ -11145,7 +11117,7 @@
.setMaxLines(mMaxMode == LINES ? mMaximum : Integer.MAX_VALUE)
.setTextDirection(getTextDirectionHeuristic())
.setLineBreakConfig(LineBreakConfig.getLineBreakConfig(
- mLineBreakStyle, mLineBreakWordStyle, autoPhraseBreaking));
+ mLineBreakStyle, mLineBreakWordStyle));
final StaticLayout layout = layoutBuilder.build();
diff --git a/core/java/android/window/WindowMetricsController.java b/core/java/android/window/WindowMetricsController.java
index 954f686..d08d5cf 100644
--- a/core/java/android/window/WindowMetricsController.java
+++ b/core/java/android/window/WindowMetricsController.java
@@ -112,15 +112,14 @@
Rect bounds, boolean isScreenRound, int windowingMode) {
try {
final InsetsState insetsState = new InsetsState();
- final boolean alwaysConsumeSystemBars = WindowManagerGlobal.getWindowManagerService()
- .getWindowInsets(displayId, token, insetsState);
+ WindowManagerGlobal.getWindowManagerService().getWindowInsets(
+ displayId, token, insetsState);
final float overrideInvScale = CompatibilityInfo.getOverrideInvertedScale();
if (overrideInvScale != 1f) {
insetsState.scale(overrideInvScale);
}
return insetsState.calculateInsets(bounds, null /* ignoringVisibilityState */,
- isScreenRound, alwaysConsumeSystemBars, SOFT_INPUT_ADJUST_NOTHING,
- 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
+ isScreenRound, SOFT_INPUT_ADJUST_NOTHING, 0 /* flags */, SYSTEM_UI_FLAG_VISIBLE,
WindowManager.LayoutParams.INVALID_WINDOW_TYPE, windowingMode,
null /* idSideMap */);
} catch (RemoteException e) {
@@ -145,13 +144,13 @@
for (int i = 0; i < possibleDisplayInfos.size(); i++) {
currentDisplayInfo = possibleDisplayInfos.get(i);
- // Calculate max bounds for this rotation and state.
- Rect maxBounds = new Rect(0, 0, currentDisplayInfo.logicalWidth,
- currentDisplayInfo.logicalHeight);
+ // Calculate max bounds for natural rotation and state.
+ Rect maxBounds = new Rect(0, 0, currentDisplayInfo.getNaturalWidth(),
+ currentDisplayInfo.getNaturalHeight());
- // Calculate insets for the rotated max bounds.
+ // Calculate insets for the natural max bounds.
final boolean isScreenRound = (currentDisplayInfo.flags & Display.FLAG_ROUND) != 0;
- // Initialize insets based upon display rotation. Note any window-provided insets
+ // Initialize insets based on Surface.ROTATION_0. Note any window-provided insets
// will not be set.
windowInsets = getWindowInsetsFromServerForDisplay(
currentDisplayInfo.displayId, null /* token */,
diff --git a/core/java/com/android/internal/app/IntentForwarderActivity.java b/core/java/com/android/internal/app/IntentForwarderActivity.java
index 7452daa..65b5979 100644
--- a/core/java/com/android/internal/app/IntentForwarderActivity.java
+++ b/core/java/com/android/internal/app/IntentForwarderActivity.java
@@ -56,6 +56,7 @@
import android.os.UserManager;
import android.provider.Settings;
import android.telecom.TelecomManager;
+import android.util.Log;
import android.util.Slog;
import android.view.View;
import android.widget.Button;
@@ -124,16 +125,19 @@
String className = intentReceived.getComponent().getClassName();
final int targetUserId;
final String userMessage;
+ final UserInfo managedProfile;
if (className.equals(FORWARD_INTENT_TO_PARENT)) {
userMessage = getForwardToPersonalMessage();
targetUserId = getProfileParent();
+ managedProfile = null;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
.setSubtype(MetricsEvent.PARENT_PROFILE));
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
userMessage = getForwardToWorkMessage();
- targetUserId = getManagedProfile();
+ managedProfile = getManagedProfile();
+ targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
getMetricsLogger().write(
new LogMaker(MetricsEvent.ACTION_SWITCH_SHARE_PROFILE)
@@ -142,6 +146,7 @@
Slog.wtf(TAG, IntentForwarderActivity.class.getName() + " cannot be called directly");
userMessage = null;
targetUserId = UserHandle.USER_NULL;
+ managedProfile = null;
}
if (targetUserId == UserHandle.USER_NULL) {
// This covers the case where there is no parent / managed profile.
@@ -185,27 +190,49 @@
finish();
// When switching to the work profile, ask the user for consent before launching
} else if (className.equals(FORWARD_INTENT_TO_MANAGED_PROFILE)) {
- maybeShowUserConsentMiniResolver(result, newIntent, targetUserId);
+ maybeShowUserConsentMiniResolver(result, newIntent, managedProfile);
}
}, getApplicationContext().getMainExecutor());
}
private void maybeShowUserConsentMiniResolver(
- ResolveInfo target, Intent launchIntent, int targetUserId) {
+ ResolveInfo target, Intent launchIntent, UserInfo managedProfile) {
if (target == null || isIntentForwarderResolveInfo(target) || !isDeviceProvisioned()) {
finish();
return;
}
- if (launchIntent.getBooleanExtra(EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
- && getCallingPackage() != null
- && PERMISSION_GRANTED == getPackageManager().checkPermission(
- INTERACT_ACROSS_USERS, getCallingPackage())) {
+ int targetUserId = managedProfile == null ? UserHandle.USER_NULL : managedProfile.id;
+ String callingPackage = getCallingPackage();
+ boolean privilegedCallerAskedToSkipUserConsent =
+ launchIntent.getBooleanExtra(
+ EXTRA_SKIP_USER_CONFIRMATION, /* defaultValue= */ false)
+ && callingPackage != null
+ && PERMISSION_GRANTED == getPackageManager().checkPermission(
+ INTERACT_ACROSS_USERS, callingPackage);
+
+ DevicePolicyManager devicePolicyManager =
+ getSystemService(DevicePolicyManager.class);
+ ComponentName profileOwnerName = devicePolicyManager.getProfileOwnerAsUser(targetUserId);
+ boolean intentToLaunchProfileOwner = profileOwnerName != null
+ && profileOwnerName.getPackageName().equals(target.getComponentInfo().packageName);
+
+ if (privilegedCallerAskedToSkipUserConsent || intentToLaunchProfileOwner) {
+ Log.i("IntentForwarderActivity", String.format(
+ "Skipping user consent for redirection into the managed profile for intent [%s]"
+ + ", privilegedCallerAskedToSkipUserConsent=[%s]"
+ + ", intentToLaunchProfileOwner=[%s]",
+ launchIntent, privilegedCallerAskedToSkipUserConsent,
+ intentToLaunchProfileOwner));
startActivityAsCaller(launchIntent, targetUserId);
finish();
return;
}
+ Log.i("IntentForwarderActivity", String.format(
+ "Showing user consent for redirection into the managed profile for intent [%s] and "
+ + " calling package [%s]",
+ launchIntent, callingPackage));
int layoutId = R.layout.miniresolver;
setContentView(layoutId);
@@ -245,8 +272,7 @@
View telephonyInfo = findViewById(R.id.miniresolver_info_section);
- DevicePolicyManager devicePolicyManager =
- getSystemService(DevicePolicyManager.class);
+
// Additional information section is work telephony specific. Therefore, it is only shown
// for telephony related intents, when all sim subscriptions are in the work profile.
if ((isDialerIntent(launchIntent) || isTextMessageIntent(launchIntent))
@@ -507,20 +533,18 @@
}
/**
- * Returns the userId of the managed profile for this device or UserHandle.USER_NULL if there is
- * no managed profile.
+ * Returns the managed profile for this device or null if there is no managed profile.
*
- * TODO: Remove the assumption that there is only one managed profile
- * on the device.
+ * TODO: Remove the assumption that there is only one managed profile on the device.
*/
- private int getManagedProfile() {
+ @Nullable private UserInfo getManagedProfile() {
List<UserInfo> relatedUsers = mInjector.getUserManager().getProfiles(UserHandle.myUserId());
for (UserInfo userInfo : relatedUsers) {
- if (userInfo.isManagedProfile()) return userInfo.id;
+ if (userInfo.isManagedProfile()) return userInfo;
}
Slog.wtf(TAG, FORWARD_INTENT_TO_MANAGED_PROFILE
+ " has been called, but there is no managed profile");
- return UserHandle.USER_NULL;
+ return null;
}
/**
diff --git a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
index 1a38049..66e3333 100644
--- a/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
+++ b/core/java/com/android/internal/inputmethod/InputMethodPrivilegedOperations.java
@@ -20,7 +20,6 @@
import android.annotation.DrawableRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.inputmethodservice.InputMethodService;
import android.net.Uri;
import android.os.IBinder;
import android.os.RemoteException;
@@ -106,10 +105,14 @@
*
* @param vis visibility flags
* @param backDisposition disposition flags
+ * @see android.inputmethodservice.InputMethodService#IME_ACTIVE
+ * @see android.inputmethodservice.InputMethodService#IME_VISIBLE
+ * @see android.inputmethodservice.InputMethodService#IME_INVISIBLE
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT
+ * @see android.inputmethodservice.InputMethodService#BACK_DISPOSITION_ADJUST_NOTHING
*/
@AnyThread
- public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ public void setImeWindowStatusAsync(int vis, int backDisposition) {
final IInputMethodPrivilegedOperations ops = mOps.getAndWarnIfNull();
if (ops == null) {
return;
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index af1fdd7..bb86801 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -232,7 +232,7 @@
private boolean mLastHasRightStableInset = false;
private boolean mLastHasLeftStableInset = false;
private int mLastWindowFlags = 0;
- private boolean mLastShouldAlwaysConsumeSystemBars = false;
+ private @InsetsType int mLastForceConsumingTypes = 0;
private @InsetsType int mLastSuppressScrimTypes = 0;
private int mRootScrollY = 0;
@@ -1111,19 +1111,19 @@
: controller.getSystemBarsAppearance();
if (insets != null) {
- mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
+ mLastForceConsumingTypes = insets.getForceConsumingTypes();
- final boolean clearsCompatInsets =
- clearsCompatInsets(attrs.type, attrs.flags,
- getResources().getConfiguration().windowConfiguration
- .getWindowingMode())
- && !mLastShouldAlwaysConsumeSystemBars;
+ @InsetsType int compatInsetsTypes =
+ WindowInsets.Type.systemBars() | WindowInsets.Type.displayCutout();
+ if (clearsCompatInsets(attrs.type, attrs.flags,
+ getResources().getConfiguration().windowConfiguration.getWindowingMode())) {
+ compatInsetsTypes &= mLastForceConsumingTypes;
+ }
final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
WindowInsets.Type.systemBars());
- final Insets systemInsets = clearsCompatInsets
+ final Insets systemInsets = compatInsetsTypes == 0
? Insets.NONE
- : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
- | WindowInsets.Type.displayCutout()), stableBarInsets);
+ : Insets.min(insets.getInsets(compatInsetsTypes), stableBarInsets);
mLastTopInset = systemInsets.top;
mLastBottomInset = systemInsets.bottom;
mLastRightInset = systemInsets.right;
@@ -1208,7 +1208,8 @@
&& (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
&& decorFitsSystemWindows
&& !hideNavigation)
- || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
+ || ((mLastForceConsumingTypes & WindowInsets.Type.navigationBars()) != 0
+ && hideNavigation);
boolean consumingNavBar =
((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
@@ -1224,13 +1225,15 @@
boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
|| (attrs.flags & FLAG_FULLSCREEN) != 0
|| (requestedVisibleTypes & WindowInsets.Type.statusBars()) == 0;
- boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
- && decorFitsSystemWindows
- && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
- && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
- && mForceWindowDrawsBarBackgrounds
- && mLastTopInset != 0
- || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
+ boolean consumingStatusBar =
+ ((sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
+ && decorFitsSystemWindows
+ && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
+ && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
+ && mForceWindowDrawsBarBackgrounds
+ && mLastTopInset != 0)
+ || ((mLastForceConsumingTypes & WindowInsets.Type.statusBars()) != 0
+ && fullscreen);
int consumedTop = consumingStatusBar ? mLastTopInset : 0;
int consumedRight = consumingNavBar ? mLastRightInset : 0;
@@ -1434,9 +1437,9 @@
private void updateColorViewInt(final ColorViewState state, int color, int dividerColor,
int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
boolean force, @InsetsType int requestedVisibleTypes) {
+ final @InsetsType int type = state.attributes.insetsType;
state.present = state.attributes.isPresent(
- (requestedVisibleTypes & state.attributes.insetsType) != 0
- || mLastShouldAlwaysConsumeSystemBars,
+ (requestedVisibleTypes & type) != 0 || (mLastForceConsumingTypes & type) != 0,
mWindow.getAttributes().flags, force);
boolean show = state.attributes.isVisible(state.present, color,
mWindow.getAttributes().flags, force);
diff --git a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
index 8b9a991..4f827cd 100644
--- a/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
+++ b/core/java/com/android/internal/statusbar/RegisterStatusBarResult.java
@@ -16,7 +16,6 @@
package com.android.internal.statusbar;
-import android.inputmethodservice.InputMethodService;
import android.os.IBinder;
import android.os.Parcel;
import android.os.Parcelable;
@@ -32,9 +31,7 @@
public final int mDisabledFlags1; // switch[0]
public final int mAppearance; // switch[1]
public final AppearanceRegion[] mAppearanceRegions; // switch[2]
- @InputMethodService.ImeWindowVisibility
public final int mImeWindowVis; // switch[3]
- @InputMethodService.BackDispositionMode
public final int mImeBackDisposition; // switch[4]
public final boolean mShowImeSwitcher; // switch[5]
public final int mDisabledFlags2; // switch[6]
@@ -47,12 +44,10 @@
public final LetterboxDetails[] mLetterboxDetails;
public RegisterStatusBarResult(ArrayMap<String, StatusBarIcon> icons, int disabledFlags1,
- int appearance, AppearanceRegion[] appearanceRegions,
- @InputMethodService.ImeWindowVisibility int imeWindowVis,
- @InputMethodService.BackDispositionMode int imeBackDisposition, boolean showImeSwitcher,
- int disabledFlags2, IBinder imeToken, boolean navbarColorManagedByIme, int behavior,
- int requestedVisibleTypes, String packageName, int transientBarTypes,
- LetterboxDetails[] letterboxDetails) {
+ int appearance, AppearanceRegion[] appearanceRegions, int imeWindowVis,
+ int imeBackDisposition, boolean showImeSwitcher, int disabledFlags2, IBinder imeToken,
+ boolean navbarColorManagedByIme, int behavior, int requestedVisibleTypes,
+ String packageName, int transientBarTypes, LetterboxDetails[] letterboxDetails) {
mIcons = new ArrayMap<>(icons);
mDisabledFlags1 = disabledFlags1;
mAppearance = appearance;
diff --git a/core/java/com/android/internal/view/BaseIWindow.java b/core/java/com/android/internal/view/BaseIWindow.java
index 600058e..0ac7765 100644
--- a/core/java/com/android/internal/view/BaseIWindow.java
+++ b/core/java/com/android/internal/view/BaseIWindow.java
@@ -53,7 +53,7 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState, boolean forceLayout,
- boolean alwaysConsumeSystemBars, int displayId, int seqId, boolean dragResizing) {
+ int displayId, int seqId, boolean dragResizing) {
if (reportDraw) {
try {
mSession.finishDrawing(this, null /* postDrawTransaction */, seqId);
diff --git a/core/jni/android_os_GraphicsEnvironment.cpp b/core/jni/android_os_GraphicsEnvironment.cpp
index d94b982..8fc30d1 100644
--- a/core/jni/android_os_GraphicsEnvironment.cpp
+++ b/core/jni/android_os_GraphicsEnvironment.cpp
@@ -16,11 +16,12 @@
#define LOG_TAG "GraphicsEnvironment"
-#include <vector>
-
#include <graphicsenv/GraphicsEnv.h>
#include <nativehelper/ScopedUtfChars.h>
#include <nativeloader/native_loader.h>
+
+#include <vector>
+
#include "core_jni_helpers.h"
namespace {
@@ -49,11 +50,10 @@
appPackageNameChars.c_str(), vulkanVersion);
}
-void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jstring packageName,
- jstring devOptIn, jobjectArray featuresObj) {
+void setAngleInfo_native(JNIEnv* env, jobject clazz, jstring path, jboolean useNativeDriver,
+ jstring packageName, jobjectArray featuresObj) {
ScopedUtfChars pathChars(env, path);
ScopedUtfChars packageNameChars(env, packageName);
- ScopedUtfChars devOptInChars(env, devOptIn);
std::vector<std::string> features;
if (featuresObj != nullptr) {
@@ -73,8 +73,8 @@
}
}
- android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), packageNameChars.c_str(),
- devOptInChars.c_str(), features);
+ android::GraphicsEnv::getInstance().setAngleInfo(pathChars.c_str(), useNativeDriver,
+ packageNameChars.c_str(), features);
}
void setLayerPaths_native(JNIEnv* env, jobject clazz, jobject classLoader, jstring layerPaths) {
@@ -118,8 +118,7 @@
reinterpret_cast<void*>(setGpuStats_native)},
{"setInjectLayersPrSetDumpable", "()Z",
reinterpret_cast<void*>(setInjectLayersPrSetDumpable_native)},
- {"setAngleInfo",
- "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;)V",
+ {"nativeSetAngleInfo", "(Ljava/lang/String;ZLjava/lang/String;[Ljava/lang/String;)V",
reinterpret_cast<void*>(setAngleInfo_native)},
{"setLayerPaths", "(Ljava/lang/ClassLoader;Ljava/lang/String;)V",
reinterpret_cast<void*>(setLayerPaths_native)},
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 2f9f6ae..a01c7b6 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6094,7 +6094,7 @@
<!-- @hide @SystemApi Allows an application to observe usage time of apps. The app can register
for callbacks when apps reach a certain usage time limit, etc. -->
<permission android:name="android.permission.OBSERVE_APP_USAGE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @hide @TestApi @SystemApi Allows an application to change the app idle state of an app.
<p>Not for use by third-party applications. -->
@@ -6724,7 +6724,7 @@
it will be ignored.
@hide -->
<permission android:name="android.permission.MODIFY_DAY_NIGHT_MODE"
- android:protectionLevel="signature|privileged" />
+ android:protectionLevel="signature|privileged|role" />
<!-- @SystemApi Allows entering or exiting car mode using a specified priority.
This permission is required to use UiModeManager while specifying a priority for the calling
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 04fef58..f795bd7 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -3792,7 +3792,7 @@
keyboard is connected -->
<string name="show_ime">Keep it on screen while physical keyboard is active</string>
<!-- Title of the physical keyboard category in the input method selector [CHAR LIMIT=30] -->
- <string name="hardware">Show virtual keyboard</string>
+ <string name="hardware">Use on-screen keyboard</string>
<!-- Title of the notification to prompt the user to configure physical keyboard settings. [CHAR LIMIT=NOTIF_TITLE] -->
<string name="select_keyboard_layout_notification_title">Configure <xliff:g id="device_name" example="Foobar USB Keyboard">%s</xliff:g></string>
diff --git a/core/tests/coretests/src/android/text/StaticLayoutTest.java b/core/tests/coretests/src/android/text/StaticLayoutTest.java
index 925da49..0ebf03f 100644
--- a/core/tests/coretests/src/android/text/StaticLayoutTest.java
+++ b/core/tests/coretests/src/android/text/StaticLayoutTest.java
@@ -24,7 +24,6 @@
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
-import android.graphics.text.LineBreakConfig;
import android.os.LocaleList;
import android.platform.test.annotations.Presubmit;
import android.text.Layout.Alignment;
@@ -926,24 +925,4 @@
assertEquals(0, layout.getHeight(true));
assertEquals(2, layout.getLineCount());
}
-
- @Test
- public void testBuilder_autoPhraseBreaking() {
- {
- // setAutoPhraseBreaking true
- LineBreakConfig lineBreakConfig = new LineBreakConfig.Builder()
- .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_STYLE_NONE)
- .setLineBreakWordStyle(LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE)
- .setAutoPhraseBreaking(true)
- .build();
- final String text = "これが正解。";
- // Obtain.
- StaticLayout.Builder builder = StaticLayout.Builder.obtain(text, 0,
- text.length(), mDefaultPaint, DEFAULT_OUTER_WIDTH);
- builder.setLineBreakConfig(lineBreakConfig);
- builder.build();
- assertEquals(LineBreakConfig.LINE_BREAK_WORD_STYLE_PHRASE,
- builder.getLineBreakWordStyle());
- }
- }
}
diff --git a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
index f45db23..a9c0e41 100644
--- a/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
+++ b/core/tests/coretests/src/android/view/ImeInsetsSourceConsumerTest.java
@@ -81,7 +81,6 @@
Insets.of(10, 10, 10, 10), rect, rect, rect, rect));
mController.calculateInsets(
false,
- false,
TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mImeConsumer = mController.getImeSourceConsumer();
diff --git a/core/tests/coretests/src/android/view/InsetsControllerTest.java b/core/tests/coretests/src/android/view/InsetsControllerTest.java
index 0692052..76e4a6b 100644
--- a/core/tests/coretests/src/android/view/InsetsControllerTest.java
+++ b/core/tests/coretests/src/android/view/InsetsControllerTest.java
@@ -171,7 +171,6 @@
mController.onStateChanged(state);
mController.calculateInsets(
false,
- false,
TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
SOFT_INPUT_ADJUST_RESIZE, 0, 0);
mController.onFrameChanged(new Rect(0, 0, 100, 100));
diff --git a/core/tests/coretests/src/android/view/InsetsStateTest.java b/core/tests/coretests/src/android/view/InsetsStateTest.java
index fde1a6d..6b32350 100644
--- a/core/tests/coretests/src/android/view/InsetsStateTest.java
+++ b/core/tests/coretests/src/android/view/InsetsStateTest.java
@@ -100,7 +100,7 @@
.setVisible(true);
SparseIntArray typeSideMap = new SparseIntArray();
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
typeSideMap);
assertEquals(Insets.of(0, 100, 0, 100), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 100), insets.getInsets(Type.all()));
@@ -119,8 +119,7 @@
.setFrame(new Rect(0, 100, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
- null);
+ SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(100, insets.getStableInsetBottom());
assertEquals(Insets.of(0, 0, 0, 100), insets.getInsetsIgnoringVisibility(systemBars()));
assertEquals(Insets.of(0, 0, 0, 200), insets.getSystemWindowInsets());
@@ -138,7 +137,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -153,7 +152,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(Type.statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(Type.navigationBars()));
@@ -168,8 +167,7 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
- null);
+ SOFT_INPUT_ADJUST_NOTHING, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
assertEquals(100, insets.getInsets(ime()).bottom);
assertTrue(insets.isVisible(ime()));
@@ -184,10 +182,10 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
+ SOFT_INPUT_ADJUST_NOTHING, 0, SYSTEM_UI_FLAG_LAYOUT_STABLE, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
@@ -199,10 +197,10 @@
.setFrame(new Rect(0, 0, 100, 100))
.setVisible(false);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_FULLSCREEN, SYSTEM_UI_FLAG_LAYOUT_STABLE,
TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
- insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
SOFT_INPUT_ADJUST_NOTHING, 0, 0 /* legacySystemUiFlags */, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
@@ -214,19 +212,19 @@
.setFrame(new Rect(0, 0, 100, 100))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
0 /* legacySystemUiFlags */, TYPE_SYSTEM_ERROR, WINDOWING_MODE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
0 /* legacySystemUiFlags */, TYPE_WALLPAPER, WINDOWING_MODE_UNDEFINED, null);
assertEquals(100, insets.getSystemWindowInsetTop());
insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
+ SOFT_INPUT_ADJUST_NOTHING, FLAG_LAYOUT_NO_LIMITS,
0 /* legacySystemUiFlags */, TYPE_APPLICATION, WINDOWING_MODE_FREEFORM, null);
assertEquals(100, insets.getSystemWindowInsetTop());
}
@@ -268,7 +266,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -283,7 +281,7 @@
.setFrame(new Rect(80, 0, 100, 300))
.setVisible(true);
WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
- false, 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
+ 0, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(Insets.of(0, 100, 20, 0), insets.getSystemWindowInsets());
assertEquals(Insets.of(0, 100, 0, 0), insets.getInsets(statusBars()));
assertEquals(Insets.of(0, 0, 20, 0), insets.getInsets(navigationBars()));
@@ -298,7 +296,7 @@
.setFrame(new Rect(0, 200, 100, 300))
.setVisible(true);
mState.removeSource(ID_IME);
- WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false, false,
+ WindowInsets insets = mState.calculateInsets(new Rect(0, 0, 100, 300), null, false,
SOFT_INPUT_ADJUST_RESIZE, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED, null);
assertEquals(0, insets.getSystemWindowInsetBottom());
}
@@ -530,7 +528,7 @@
new Rect(0, 0, 1, 2),
new Rect(197, 296, 200, 300),
new Rect(197, 296, 200, 300)));
- DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false, false,
+ DisplayCutout cutout = mState.calculateInsets(new Rect(1, 1, 199, 300), null, false,
SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION, WINDOWING_MODE_UNDEFINED,
new SparseIntArray()).getDisplayCutout();
assertEquals(0, cutout.getSafeInsetLeft());
@@ -556,7 +554,7 @@
new RoundedCorner(POSITION_BOTTOM_RIGHT, 20, 180, 380),
new RoundedCorner(POSITION_BOTTOM_LEFT, 20, 20, 380)));
WindowInsets windowInsets = mState.calculateInsets(new Rect(1, 2, 197, 396), null, false,
- false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, new SparseIntArray());
assertEquals(new RoundedCorner(POSITION_TOP_LEFT, 10, 9, 8),
windowInsets.getRoundedCorner(POSITION_TOP_LEFT));
@@ -573,7 +571,7 @@
mState.setDisplayFrame(new Rect(0, 0, 200, 400));
mState.setDisplayShape(DisplayShape.createDefaultDisplayShape(200, 400, false));
WindowInsets windowInsets = mState.calculateInsets(new Rect(10, 20, 200, 400), null, false,
- false, SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
+ SOFT_INPUT_ADJUST_UNSPECIFIED, 0, 0, TYPE_APPLICATION,
WINDOWING_MODE_UNDEFINED, new SparseIntArray());
final DisplayShape expect =
diff --git a/core/tests/coretests/src/android/view/WindowInsetsTest.java b/core/tests/coretests/src/android/view/WindowInsetsTest.java
index b4ba23c..69abf5f 100644
--- a/core/tests/coretests/src/android/view/WindowInsetsTest.java
+++ b/core/tests/coretests/src/android/view/WindowInsetsTest.java
@@ -40,14 +40,14 @@
@Test
public void systemWindowInsets_afterConsuming_isConsumed() {
assertTrue(new WindowInsets(WindowInsets.createCompatTypeMap(new Rect(1, 2, 3, 4)), null,
- null, false, false, 0, null, null, null, null,
+ null, false, 0, 0, null, null, null, null,
WindowInsets.Type.systemBars(), false)
.consumeSystemWindowInsets().isConsumed());
}
@Test
public void multiNullConstructor_isConsumed() {
- assertTrue(new WindowInsets(null, null, null, false, false, 0, null, null, null, null,
+ assertTrue(new WindowInsets(null, null, null, false, 0, 0, null, null, null, null,
WindowInsets.Type.systemBars(), false).isConsumed());
}
@@ -63,7 +63,7 @@
boolean[] visible = new boolean[SIZE];
WindowInsets.assignCompatInsets(maxInsets, new Rect(0, 10, 0, 0));
WindowInsets.assignCompatInsets(insets, new Rect(0, 0, 0, 0));
- WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, false,
+ WindowInsets windowInsets = new WindowInsets(insets, maxInsets, visible, false, 0,
0, null, null, null, DisplayShape.NONE, systemBars(),
true /* compatIgnoreVisibility */);
assertEquals(Insets.of(0, 10, 0, 0), windowInsets.getSystemWindowInsets());
diff --git a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
index a1a4265..84dd274 100644
--- a/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
+++ b/core/tests/coretests/src/com/android/internal/widget/ActionBarOverlayLayoutTest.java
@@ -169,7 +169,7 @@
private WindowInsets insetsWith(Insets content, DisplayCutout cutout) {
return new WindowInsets(WindowInsets.createCompatTypeMap(content.toRect()), null, null,
- false, false, 0, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
+ false, 0, 0, cutout, null, null, null, WindowInsets.Type.systemBars(), false);
}
private ViewGroup createViewGroupWithId(int id) {
diff --git a/graphics/java/android/graphics/text/LineBreakConfig.java b/graphics/java/android/graphics/text/LineBreakConfig.java
index d0327159..0c493f5 100644
--- a/graphics/java/android/graphics/text/LineBreakConfig.java
+++ b/graphics/java/android/graphics/text/LineBreakConfig.java
@@ -94,11 +94,6 @@
private @LineBreakWordStyle int mLineBreakWordStyle =
LineBreakConfig.LINE_BREAK_WORD_STYLE_NONE;
- // Whether or not enabling phrase breaking automatically.
- // TODO(b/226012260): Remove this and add LINE_BREAK_WORD_STYLE_PHRASE_AUTO after
- // the experiment.
- private boolean mAutoPhraseBreaking = false;
-
/**
* Builder constructor.
*/
@@ -128,22 +123,12 @@
}
/**
- * Enables or disables the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE}.
- *
- * @hide
- */
- public @NonNull Builder setAutoPhraseBreaking(boolean autoPhraseBreaking) {
- mAutoPhraseBreaking = autoPhraseBreaking;
- return this;
- }
-
- /**
* Builds a {@link LineBreakConfig} instance.
*
* @return The {@code LineBreakConfig} instance.
*/
public @NonNull LineBreakConfig build() {
- return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle, mAutoPhraseBreaking);
+ return new LineBreakConfig(mLineBreakStyle, mLineBreakWordStyle);
}
}
@@ -164,23 +149,6 @@
.build();
}
- /**
- * Create the LineBreakConfig instance.
- *
- * @param lineBreakStyle the line break style for text wrapping.
- * @param lineBreakWordStyle the line break word style for text wrapping.
- * @return the {@link LineBreakConfig} instance. *
- * @hide
- */
- public static @NonNull LineBreakConfig getLineBreakConfig(@LineBreakStyle int lineBreakStyle,
- @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
- LineBreakConfig.Builder builder = new LineBreakConfig.Builder();
- return builder.setLineBreakStyle(lineBreakStyle)
- .setLineBreakWordStyle(lineBreakWordStyle)
- .setAutoPhraseBreaking(autoPhraseBreaking)
- .build();
- }
-
/** @hide */
public static final LineBreakConfig NONE =
new Builder().setLineBreakStyle(LINE_BREAK_STYLE_NONE)
@@ -188,7 +156,6 @@
private final @LineBreakStyle int mLineBreakStyle;
private final @LineBreakWordStyle int mLineBreakWordStyle;
- private final boolean mAutoPhraseBreaking;
/**
* Constructor with line-break parameters.
@@ -197,10 +164,9 @@
* {@code LineBreakConfig} instance.
*/
private LineBreakConfig(@LineBreakStyle int lineBreakStyle,
- @LineBreakWordStyle int lineBreakWordStyle, boolean autoPhraseBreaking) {
+ @LineBreakWordStyle int lineBreakWordStyle) {
mLineBreakStyle = lineBreakStyle;
mLineBreakWordStyle = lineBreakWordStyle;
- mAutoPhraseBreaking = autoPhraseBreaking;
}
/**
@@ -221,17 +187,6 @@
return mLineBreakWordStyle;
}
- /**
- * Used to identify if the automation of {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
- *
- * @return The result that records whether or not the automation of
- * {@link LINE_BREAK_WORD_STYLE_PHRASE} is enabled.
- * @hide
- */
- public boolean getAutoPhraseBreaking() {
- return mAutoPhraseBreaking;
- }
-
@Override
public boolean equals(Object o) {
if (o == null) return false;
@@ -239,8 +194,7 @@
if (!(o instanceof LineBreakConfig)) return false;
LineBreakConfig that = (LineBreakConfig) o;
return (mLineBreakStyle == that.mLineBreakStyle)
- && (mLineBreakWordStyle == that.mLineBreakWordStyle)
- && (mAutoPhraseBreaking == that.mAutoPhraseBreaking);
+ && (mLineBreakWordStyle == that.mLineBreakWordStyle);
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
index 5e42782..586a794 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/SystemWindows.java
@@ -347,8 +347,7 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration newMergedConfiguration, InsetsState insetsState,
- boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int syncSeqId,
- boolean dragResizing) {}
+ boolean forceLayout, int displayId, int syncSeqId, boolean dragResizing) {}
@Override
public void insetsControlChanged(InsetsState insetsState,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index 24aaa9b..73eb62a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -339,19 +339,36 @@
}
// This means an expand happened before enter-pip finished and we are now "merging" a
// no-op transition that happens to match our exit-pip.
+ // Or that the keyguard is up and preventing the transition from applying, in which case we
+ // want to manually reset pip. (b/283783868)
boolean cancelled = false;
if (mPipAnimationController.getCurrentAnimator() != null) {
mPipAnimationController.getCurrentAnimator().cancel();
+ mPipAnimationController.resetAnimatorState();
cancelled = true;
}
+
// Unset exitTransition AFTER cancel so that finishResize knows we are merging.
mExitTransition = null;
- if (!cancelled || aborted) return;
+ if (!cancelled) return;
final ActivityManager.RunningTaskInfo taskInfo = mPipOrganizer.getTaskInfo();
if (taskInfo != null) {
- startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
- mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
- new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ if (aborted) {
+ // keyguard case - the transition got aborted, so we want to reset state and
+ // windowing mode before reapplying the resize transaction
+ sendOnPipTransitionFinished(TRANSITION_DIRECTION_LEAVE_PIP);
+ mPipOrganizer.onExitPipFinished(taskInfo);
+
+ WindowContainerTransaction wct = new WindowContainerTransaction();
+ mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
+ wct.setBounds(taskInfo.token, null);
+ mPipOrganizer.applyFinishBoundsResize(wct, TRANSITION_DIRECTION_LEAVE_PIP, false);
+ } else {
+ // merge case
+ startExpandAnimation(taskInfo, mPipOrganizer.getSurfaceControl(),
+ mPipBoundsState.getBounds(), mPipBoundsState.getBounds(),
+ new Rect(mExitDestinationBounds), Surface.ROTATION_0, null /* startT */);
+ }
}
mExitDestinationBounds.setEmpty();
mCurrentPipTaskToken = null;
@@ -567,7 +584,16 @@
mPipBoundsState.getDisplayBounds());
mFinishCallback = (wct, wctCB) -> {
mPipOrganizer.onExitPipFinished(taskInfo);
- if (!Transitions.SHELL_TRANSITIONS_ROTATION && toFullscreen) {
+
+ // TODO(b/286346098): remove the OPEN app flicker completely
+ // not checking if we go to fullscreen helps avoid getting pip into an inconsistent
+ // state after the flicker occurs. This is a temp solution until flicker is removed.
+ if (!Transitions.SHELL_TRANSITIONS_ROTATION) {
+ // will help to debug the case when we are not exiting to fullscreen
+ if (!toFullscreen) {
+ ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
+ "%s: startExitAnimation() not exiting to fullscreen", TAG);
+ }
wct = wct != null ? wct : new WindowContainerTransaction();
wct.setBounds(pipTaskToken, null);
mPipOrganizer.applyWindowingModeChangeOnExit(wct, TRANSITION_DIRECTION_LEAVE_PIP);
@@ -831,7 +857,7 @@
}
final Rect destinationBounds = mPipBoundsAlgorithm.getEntryDestinationBounds();
- final Rect currentBounds = taskInfo.configuration.windowConfiguration.getBounds();
+ final Rect currentBounds = pipChange.getStartAbsBounds();
int rotationDelta = deltaRotation(startRotation, endRotation);
Rect sourceHintRect = PipBoundsAlgorithm.getValidSourceHintRect(
taskInfo.pictureInPictureParams, currentBounds, destinationBounds);
@@ -1050,7 +1076,7 @@
// When the PIP window is visible and being a part of the transition, such as display
// rotation, we need to update its bounds and rounded corner.
final SurfaceControl leash = pipChange.getLeash();
- final Rect destBounds = mPipBoundsState.getBounds();
+ final Rect destBounds = mPipOrganizer.getCurrentOrAnimatingBounds();
final boolean isInPip = mPipTransitionState.isInPip();
mSurfaceTransactionHelper
.crop(startTransaction, leash, destBounds)
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 843e5af..837f118 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
@@ -567,7 +567,8 @@
&& taskInfo.configuration.windowConfiguration.isAlwaysOnTop()) {
// Tasks that are always on top (e.g. bubbles), will handle their own transition
// as they are on top of everything else. So cancel the merge here.
- cancel("task #" + taskInfo.taskId + " is always_on_top");
+ cancel(false /* toHome */, false /* withScreenshots */,
+ "task #" + taskInfo.taskId + " is always_on_top");
return;
}
final boolean isRootTask = taskInfo != null
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index acc1c5e..bf70d48 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2853,18 +2853,24 @@
}
}
+ final ArrayMap<Integer, SurfaceControl> dismissingTasks = new ArrayMap<>();
+ for (int i = info.getChanges().size() - 1; i >= 0; --i) {
+ final TransitionInfo.Change change = info.getChanges().get(i);
+ final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
+ if (taskInfo == null) continue;
+ if (getStageOfTask(taskInfo) != null
+ || getSplitItemPosition(change.getLastParent()) != SPLIT_POSITION_UNDEFINED) {
+ dismissingTasks.put(taskInfo.taskId, change.getLeash());
+ }
+ }
+
+
if (shouldBreakPairedTaskInRecents(dismissReason)) {
// Notify recents if we are exiting in a way that breaks the pair, and disable further
// updates to splits in the recents until we enter split again
mRecentTasks.ifPresent(recentTasks -> {
- for (int i = info.getChanges().size() - 1; i >= 0; --i) {
- final TransitionInfo.Change change = info.getChanges().get(i);
- final ActivityManager.RunningTaskInfo taskInfo = change.getTaskInfo();
- if (taskInfo != null && (getStageOfTask(taskInfo) != null
- || getSplitItemPosition(change.getLastParent())
- != SPLIT_POSITION_UNDEFINED)) {
- recentTasks.removeSplitPair(taskInfo.taskId);
- }
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ recentTasks.removeSplitPair(dismissingTasks.keyAt(i));
}
});
}
@@ -2882,6 +2888,10 @@
t.hide(toStage == STAGE_TYPE_MAIN ? mSideStage.mRootLeash : mMainStage.mRootLeash);
t.setPosition(toStage == STAGE_TYPE_MAIN
? mMainStage.mRootLeash : mSideStage.mRootLeash, 0, 0);
+ } else {
+ for (int i = dismissingTasks.keySet().size() - 1; i >= 0; --i) {
+ finishT.hide(dismissingTasks.valueAt(i));
+ }
}
if (toStage == STAGE_TYPE_UNDEFINED) {
@@ -2891,7 +2901,7 @@
}
// Hide divider and dim layer on transition finished.
- setDividerVisibility(false, finishT);
+ setDividerVisibility(false, t);
finishT.hide(mMainStage.mDimLayer);
finishT.hide(mSideStage.mDimLayer);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
index c964df1..1d879a13 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/TaskSnapshotWindow.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.startingsurface;
+import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.graphics.Color.WHITE;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -77,6 +78,13 @@
@NonNull Runnable clearWindowHandler) {
final ActivityManager.RunningTaskInfo runningTaskInfo = info.taskInfo;
final int taskId = runningTaskInfo.taskId;
+
+ // if we're in PIP we don't want to create the snapshot
+ if (runningTaskInfo.getWindowingMode() == WINDOWING_MODE_PINNED) {
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
+ "did not create taskSnapshot due to being in PIP");
+ return null;
+ }
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_STARTING_WINDOW,
"create taskSnapshot surface for task: %d", taskId);
@@ -206,8 +214,7 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, InsetsState insetsState,
- boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId, int seqId,
- boolean dragResizing) {
+ boolean forceLayout, int displayId, int seqId, boolean dragResizing) {
final TaskSnapshotWindow snapshot = mOuter.get();
if (snapshot == null) {
return;
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 a28ce55..d9edde1 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
@@ -25,6 +25,7 @@
import static com.android.wm.shell.common.split.SplitScreenConstants.FLAG_IS_DIVIDER_BAR;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
+import static com.android.wm.shell.pip.PipAnimationController.ANIM_TYPE_ALPHA;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;
import static com.android.wm.shell.splitscreen.SplitScreenController.EXIT_REASON_CHILD_TASK_ENTER_PIP;
import static com.android.wm.shell.util.TransitionUtil.isOpeningType;
@@ -500,6 +501,7 @@
}
}
+ mPipHandler.setEnterAnimationType(ANIM_TYPE_ALPHA);
mPipHandler.startEnterAnimation(pipChange, startTransaction, finishTransaction,
finishCB);
// Dispatch the rest of the transition normally. This will most-likely be taken by
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 d8a8877..7565996 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
@@ -27,6 +27,7 @@
import static android.view.WindowManager.TRANSIT_TO_BACK;
import static android.view.WindowManager.TRANSIT_TO_FRONT;
import static android.view.WindowManager.fixScale;
+import static android.window.TransitionInfo.FLAG_IS_BEHIND_STARTING_WINDOW;
import static android.window.TransitionInfo.FLAG_IS_OCCLUDED;
import static android.window.TransitionInfo.FLAG_IS_WALLPAPER;
import static android.window.TransitionInfo.FLAG_MOVED_TO_TOP;
@@ -728,11 +729,15 @@
final int changeSize = info.getChanges().size();
boolean taskChange = false;
boolean transferStartingWindow = false;
+ int noAnimationBehindStartingWindow = 0;
boolean allOccluded = changeSize > 0;
for (int i = changeSize - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
taskChange |= change.getTaskInfo() != null;
transferStartingWindow |= change.hasFlags(FLAG_STARTING_WINDOW_TRANSFER_RECIPIENT);
+ if (change.hasAllFlags(FLAG_IS_BEHIND_STARTING_WINDOW | FLAG_NO_ANIMATION)) {
+ noAnimationBehindStartingWindow++;
+ }
if (!change.hasFlags(FLAG_IS_OCCLUDED)) {
allOccluded = false;
}
@@ -740,9 +745,11 @@
// There does not need animation when:
// A. Transfer starting window. Apply transfer starting window directly if there is no other
// task change. Since this is an activity->activity situation, we can detect it by selecting
- // transitions with only 2 changes where neither are tasks and one is a starting-window
- // recipient.
- if (!taskChange && transferStartingWindow && changeSize == 2
+ // transitions with only 2 changes where
+ // 1. neither are tasks, and
+ // 2. one is a starting-window recipient, or all change is behind starting window.
+ if (!taskChange && (transferStartingWindow || noAnimationBehindStartingWindow == changeSize)
+ && changeSize == 2
// B. It's visibility change if the TRANSIT_TO_BACK/TO_FRONT happened when all
// changes are underneath another change.
|| ((info.getType() == TRANSIT_TO_BACK || info.getType() == TRANSIT_TO_FRONT)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
similarity index 95%
rename from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
rename to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
index 27eaa40..fd56a6e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/SplitScreenUtils.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2022 The Android Open Source Project
+ * Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker
import android.app.Instrumentation
import android.graphics.Point
@@ -40,8 +40,6 @@
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
-import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
internal object SplitScreenUtils {
@@ -114,13 +112,12 @@
}
fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
) {
val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
@@ -326,14 +323,14 @@
dividerBar.drag(
Point(
if (dragToRight) {
- displayBounds.width * 4 / 5
+ displayBounds.right
} else {
- displayBounds.width * 1 / 5
+ displayBounds.left
},
if (dragToBottom) {
- displayBounds.height * 4 / 5
+ displayBounds.bottom
} else {
- displayBounds.height * 1 / 5
+ displayBounds.top
}
)
)
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
new file mode 100644
index 0000000..6fe88ca
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/appcompat/QuickSwitchLauncherToLetterboxAppTest.kt
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.appcompat
+
+import android.platform.test.annotations.Postsubmit
+import android.platform.test.annotations.RequiresDevice
+import android.tools.common.flicker.assertions.FlickerTest
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test quick switching to letterboxed app from launcher
+ *
+ * To run this test: `atest WMShellFlickerTestsOther:QuickSwitchLauncherToLetterboxAppTest`
+ *
+ * Actions:
+ * ```
+ * Launch a letterboxed app
+ * Navigate home to show launcher
+ * Swipe right from the bottom of the screen to quick switch back to the app
+ * ```
+ */
+
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+open class QuickSwitchLauncherToLetterboxAppTest(flicker: LegacyFlickerTest) :
+ BaseAppCompat(flicker) {
+
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+
+ tapl.setExpectedRotation(flicker.scenario.startRotation.value)
+
+ letterboxApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withHomeActivityVisible()
+ .withWindowSurfaceDisappeared(letterboxApp)
+ .waitForAndVerify()
+
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds ?: error("Display not found")
+ }
+ transitions {
+ tapl.workspace.quickSwitchToPreviousApp()
+ wmHelper
+ .StateSyncBuilder()
+ .withFullScreenApp(letterboxApp)
+ .withNavOrTaskBarVisible()
+ .withStatusBarVisible()
+ .waitForAndVerify()
+ }
+ teardown { letterboxApp.exit(wmHelper) }
+ }
+
+ /**
+ * Checks that [letterboxApp] is the top window at the end of the transition once we have fully
+ * quick switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithAppBeingOnTop() {
+ flicker.assertWmEnd { this.isAppWindowOnTop(letterboxApp) }
+ }
+
+ /** Checks that the transition starts with the home activity being tagged as visible. */
+ @Postsubmit
+ @Test
+ fun startsWithHomeActivityFlaggedVisible() {
+ flicker.assertWmStart { this.isHomeActivityVisible() }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] windows
+ * filling/covering exactly display size
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherWindowsCoverFullScreen() {
+ flicker.assertWmStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] layers
+ * filling/covering exactly the display size.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherLayersCoverFullScreen() {
+ flicker.assertLayersStart {
+ this.visibleRegion(ComponentNameMatcher.LAUNCHER).coversExactly(startDisplayBounds)
+ }
+ }
+
+ /**
+ * Checks that the transition starts with the [ComponentNameMatcher.LAUNCHER] being the top
+ * window.
+ */
+ @Postsubmit
+ @Test
+ fun startsWithLauncherBeingOnTop() {
+ flicker.assertWmStart { this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER) }
+ }
+
+ /**
+ * Checks that the transition ends with the home activity being flagged as not visible. By this
+ * point we should have quick switched away from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun endsWithHomeActivityFlaggedInvisible() {
+ flicker.assertWmEnd { this.isHomeActivityInvisible() }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s window starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowBecomesAndStaysVisible() {
+ flicker.assertWm {
+ this.isAppWindowInvisible(letterboxApp)
+ .then()
+ .isAppWindowVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that [letterboxApp]'s layer starts off invisible and becomes visible at some point
+ * before the end of the transition and then stays visible until the end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerBecomesAndStaysVisible() {
+ flicker.assertLayers { this.isInvisible(letterboxApp).then().isVisible(letterboxApp) }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherWindowBecomesAndStaysInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowNotOnTop(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer starts off visible and becomes
+ * invisible at some point before the end of the transition and then stays invisible until the
+ * end of the transition.
+ */
+ @Postsubmit
+ @Test
+ fun launcherLayerBecomesAndStaysInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isInvisible(ComponentNameMatcher.LAUNCHER)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] window is visible at least until the app
+ * window is visible. Ensures that at any point, either the launcher or [letterboxApp] windows
+ * are at least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appWindowIsVisibleOnceLauncherWindowIsInvisible() {
+ flicker.assertWm {
+ this.isAppWindowOnTop(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isAppWindowVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isAppWindowVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LAUNCHER] layer is visible at least until the app layer
+ * is visible. Ensures that at any point, either the launcher or [letterboxApp] layers are at
+ * least partially visible.
+ */
+ @Postsubmit
+ @Test
+ fun appLayerIsVisibleOnceLauncherLayerIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ }
+ }
+
+ /**
+ * Checks that the [ComponentNameMatcher.LETTERBOX] layer is visible as soon as the
+ * [letterboxApp] layer is visible at the end of the transition once we have fully quick
+ * switched from the launcher back to the [letterboxApp].
+ */
+ @Postsubmit
+ @Test
+ fun appAndLetterboxLayersBothVisibleOnceLauncherIsInvisible() {
+ flicker.assertLayers {
+ this.isVisible(ComponentNameMatcher.LAUNCHER)
+ .then()
+ .isVisible(ComponentNameMatcher.SNAPSHOT, isOptional = true)
+ .then()
+ .isVisible(letterboxApp)
+ .isVisible(ComponentNameMatcher.LETTERBOX) }
+ }
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun taskBarLayerIsVisibleAtStartAndEnd() = super.taskBarLayerIsVisibleAtStartAndEnd()
+
+ /** {@inheritDoc} */
+ @Postsubmit
+ @Test
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams(): Collection<FlickerTest> {
+ return LegacyFlickerTestFactory.nonRotationTests(
+ supportedNavigationModes = listOf(NavBar.MODE_GESTURAL),
+ supportedRotations = listOf(Rotation.ROTATION_90)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
index 36bbafb..8a85374 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/AutoEnterPipFromSplitScreenOnGoToHomeTest.kt
@@ -16,12 +16,9 @@
package com.android.wm.shell.flicker.pip
-import android.app.Instrumentation
-import android.os.SystemClock
import android.platform.test.annotations.Presubmit
import android.tools.common.NavBar
import android.tools.common.Rotation
-import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
@@ -29,13 +26,9 @@
import android.tools.device.helpers.WindowUtils
import android.tools.device.traces.parsers.toFlickerComponent
import androidx.test.filters.RequiresDevice
-import androidx.test.uiautomator.By
-import androidx.test.uiautomator.BySelector
-import androidx.test.uiautomator.UiObject2
-import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.FixMethodOrder
import org.junit.Test
@@ -73,13 +66,11 @@
AutoEnterPipOnGoToHomeTest(flicker) {
private val portraitDisplayBounds = WindowUtils.getDisplayBounds(Rotation.ROTATION_0)
/** Second app used to enter split screen mode */
- protected val secondAppForSplitScreen = getSplitScreenApp(instrumentation)
- fun getSplitScreenApp(instrumentation: Instrumentation): StandardAppHelper =
- SimpleAppHelper(
- instrumentation,
- ActivityOptions.SplitScreen.Primary.LABEL,
- ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
- )
+ private val secondAppForSplitScreen = SimpleAppHelper(
+ instrumentation,
+ ActivityOptions.SplitScreen.Primary.LABEL,
+ ActivityOptions.SplitScreen.Primary.COMPONENT.toFlickerComponent()
+ )
/** Defines the transition used to run the test */
override val transition: FlickerBuilder.() -> Unit
@@ -88,14 +79,7 @@
secondAppForSplitScreen.launchViaIntent(wmHelper)
pipApp.launchViaIntent(wmHelper)
tapl.goHome()
- enterSplitScreen()
- // wait until split screen is established
- wmHelper
- .StateSyncBuilder()
- .withWindowSurfaceAppeared(pipApp)
- .withWindowSurfaceAppeared(secondAppForSplitScreen)
- .withSplitDividerVisible()
- .waitForAndVerify()
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, pipApp, secondAppForSplitScreen)
pipApp.enableAutoEnterForPipActivity()
}
teardown {
@@ -107,46 +91,6 @@
transitions { tapl.goHome() }
}
- // TODO(b/285400227) merge the code in a common utility - this is copied from SplitScreenUtils
- private val TIMEOUT_MS = 3_000L
- private val overviewSnapshotSelector: BySelector
- get() = By.res(LAUNCHER_UI_PACKAGE_NAME, "snapshot")
- private fun enterSplitScreen() {
- // Note: The initial split position in landscape is different between tablet and phone.
- // In landscape, tablet will let the first app split to right side, and phone will
- // split to left side.
- if (tapl.isTablet) {
- // TAPL's currentTask on tablet is sometimes not what we expected if the overview
- // contains more than 3 task views. We need to use uiautomator directly to find the
- // second task to split.
- tapl.workspace.switchToOverview().overviewActions.clickSplit()
- val snapshots =
- tapl.device.wait(Until.findObjects(overviewSnapshotSelector), TIMEOUT_MS)
- if (snapshots == null || snapshots.size < 1) {
- error("Fail to find a overview snapshot to split.")
- }
-
- // Find the second task in the upper right corner in split select mode by sorting
- // 'left' in descending order and 'top' in ascending order.
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t2.getVisibleBounds().left - t1.getVisibleBounds().left
- }
- snapshots.sortWith { t1: UiObject2, t2: UiObject2 ->
- t1.getVisibleBounds().top - t2.getVisibleBounds().top
- }
- snapshots[0].click()
- } else {
- tapl.workspace
- .switchToOverview()
- .currentTask
- .tapMenu()
- .tapSplitMenuItem()
- .currentTask
- .open()
- }
- SystemClock.sleep(TIMEOUT_MS)
- }
-
@Presubmit
@Test
override fun pipOverlayLayerAppearThenDisappear() {
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
new file mode 100644
index 0000000..76ad6b9
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/CopyContentInSplit.kt
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class CopyContentInSplit
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val textEditApp = SplitScreenUtils.getIme(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, textEditApp)
+ }
+
+ @Test
+ open fun copyContentInSplit() {
+ SplitScreenUtils.copyContentInSplit(instrumentation, device, primaryApp, textEditApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
new file mode 100644
index 0000000..25182b4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByDivider.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByDivider() {
+ if (tapl.isTablet) {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = false,
+ dragToBottom = true
+ )
+ } else {
+ SplitScreenUtils.dragDividerToDismissSplit(
+ device,
+ wmHelper,
+ dragToRight = true,
+ dragToBottom = true
+ )
+ }
+ wmHelper.StateSyncBuilder().withFullScreenApp(secondaryApp).waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
new file mode 100644
index 0000000..000b628
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DismissSplitScreenByGoHome.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DismissSplitScreenByGoHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dismissSplitScreenByGoHome() {
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.kt
new file mode 100644
index 0000000..dd9ff3c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/DragDividerToResize.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.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class DragDividerToResize
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun dragDividerToResize() {
+ SplitScreenUtils.dragDividerToResizeAndWait(device, wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
new file mode 100644
index 0000000..4bbb9aa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromAllApps.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromAllApps
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromAllApps() {
+ tapl.launchedAppState.taskbar
+ .openAllApps()
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
new file mode 100644
index 0000000..a2b7526
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromNotification.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromNotification
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val sendNotificationApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ // Send a notification
+ sendNotificationApp.launchViaIntent(wmHelper)
+ sendNotificationApp.postNotification(wmHelper)
+ tapl.goHome()
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromNotification() {
+ SplitScreenUtils.dragFromNotificationToSplit(instrumentation, device, wmHelper)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, sendNotificationApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ sendNotificationApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
new file mode 100644
index 0000000..1ccd813
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromShortcut.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Assume
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromShortcut
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ Assume.assumeTrue(tapl.isTablet)
+
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromShortcut() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .openDeepShortcutMenu()
+ .getMenuItem("Split Screen Secondary Activity")
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+
+ // TODO: Do we want this check in here? Add to the other tests?
+ // flicker.splitScreenEntered(
+ // primaryApp,
+ // secondaryApp,
+ // fromOtherApp = false,
+ // appExistAtStart = false
+ // )
+ }
+
+ @After
+ fun teardwon() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
new file mode 100644
index 0000000..664786b
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenByDragFromTaskbar.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenByDragFromTaskbar
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ tapl.goHome()
+ SplitScreenUtils.createShortcutOnHotseatIfNotExist(tapl, secondaryApp.appName)
+ primaryApp.launchViaIntent(wmHelper)
+ }
+
+ @Test
+ open fun enterSplitScreenByDragFromTaskbar() {
+ tapl.launchedAppState.taskbar
+ .getAppIcon(secondaryApp.appName)
+ .dragToSplitscreen(secondaryApp.`package`, primaryApp.`package`)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
new file mode 100644
index 0000000..88fd084
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/EnterSplitScreenFromOverview.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class EnterSplitScreenFromOverview
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ primaryApp.launchViaIntent(wmHelper)
+ secondaryApp.launchViaIntent(wmHelper)
+ tapl.goHome()
+ wmHelper
+ .StateSyncBuilder()
+ .withAppTransitionIdle()
+ .withHomeActivityVisible()
+ .waitForAndVerify()
+ }
+
+ @Test
+ open fun enterSplitScreenFromOverview() {
+ SplitScreenUtils.splitFromOverview(tapl, device)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
similarity index 87%
copy from libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
copy to libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
index 27eaa40..83a18e8 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenUtils.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SplitScreenUtils.kt
@@ -14,20 +14,30 @@
* limitations under the License.
*/
-package com.android.wm.shell.flicker.splitscreen
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
import android.app.Instrumentation
import android.graphics.Point
import android.os.SystemClock
+import android.platform.test.rule.NavigationModeRule
+import android.platform.test.rule.PressHomeRule
+import android.platform.test.rule.UnlockScreenRule
+import android.tools.common.NavBar
+import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.common.traces.component.IComponentMatcher
import android.tools.common.traces.component.IComponentNameMatcher
+import android.tools.device.apphelpers.MessagingAppHelper
import android.tools.device.apphelpers.StandardAppHelper
+import android.tools.device.flicker.rules.ChangeDisplayOrientationRule
+import android.tools.device.flicker.rules.LaunchAppRule
+import android.tools.device.flicker.rules.RemoveAllTasksButHomeRule
import android.tools.device.traces.parsers.WindowManagerStateHelper
import android.tools.device.traces.parsers.toFlickerComponent
import android.view.InputDevice
import android.view.MotionEvent
import android.view.ViewConfiguration
+import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.By
import androidx.test.uiautomator.BySelector
import androidx.test.uiautomator.UiDevice
@@ -39,12 +49,12 @@
import com.android.server.wm.flicker.helpers.NotificationAppHelper
import com.android.server.wm.flicker.helpers.SimpleAppHelper
import com.android.server.wm.flicker.testapp.ActivityOptions
-import com.android.server.wm.flicker.testapp.ActivityOptions.SplitScreen.Primary
import com.android.wm.shell.flicker.LAUNCHER_UI_PACKAGE_NAME
import com.android.wm.shell.flicker.SYSTEM_UI_PACKAGE_NAME
import org.junit.Assert.assertNotNull
+import org.junit.rules.RuleChain
-internal object SplitScreenUtils {
+object SplitScreenUtils {
private const val TIMEOUT_MS = 3_000L
private const val DRAG_DURATION_MS = 1_000L
private const val NOTIFICATION_SCROLLER = "notification_stack_scroller"
@@ -63,6 +73,30 @@
private val overviewSnapshotSelector: BySelector
get() = By.res(LAUNCHER_UI_PACKAGE_NAME, OVERVIEW_SNAPSHOT)
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+
+ fun testSetupRule(navigationMode: () -> NavBar, rotation: () -> Rotation): RuleChain {
+ return RuleChain.outerRule(UnlockScreenRule())
+ .around(
+ NavigationModeRule(
+ navigationMode().value,
+ /* changeNavigationModeAfterTest */ false
+ )
+ )
+ .around(
+ LaunchAppRule(MessagingAppHelper(instrumentation), clearCacheAfterParsing = false)
+ )
+ .around(RemoveAllTasksButHomeRule())
+ .around(
+ ChangeDisplayOrientationRule(
+ rotation(),
+ resetOrientationAfterTest = false,
+ clearCacheAfterParsing = false
+ )
+ )
+ .around(PressHomeRule())
+ }
+
fun getPrimary(instrumentation: Instrumentation): StandardAppHelper =
SimpleAppHelper(
instrumentation,
@@ -113,17 +147,6 @@
waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
}
- fun enterSplitViaIntent(
- wmHelper: WindowManagerStateHelper,
- primaryApp: StandardAppHelper,
- secondaryApp: StandardAppHelper
- ) {
- val stringExtras = mapOf(Primary.EXTRA_LAUNCH_ADJACENT to "true")
- primaryApp.launchViaIntent(wmHelper, null, null,
- stringExtras)
- waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
- }
-
fun splitFromOverview(tapl: LauncherInstrumentation, device: UiDevice) {
// Note: The initial split position in landscape is different between tablet and phone.
// In landscape, tablet will let the first app split to right side, and phone will
@@ -159,6 +182,17 @@
SystemClock.sleep(TIMEOUT_MS)
}
+ fun enterSplitViaIntent(
+ wmHelper: WindowManagerStateHelper,
+ primaryApp: StandardAppHelper,
+ secondaryApp: StandardAppHelper
+ ) {
+ val stringExtras =
+ mapOf(ActivityOptions.SplitScreen.Primary.EXTRA_LAUNCH_ADJACENT to "true")
+ primaryApp.launchViaIntent(wmHelper, null, null, stringExtras)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
fun dragFromNotificationToSplit(
instrumentation: Instrumentation,
device: UiDevice,
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
new file mode 100644
index 0000000..e5501f4
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchAppByDoubleTapDivider.kt
@@ -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.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.graphics.Point
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.helpers.WindowUtils
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchAppByDoubleTapDivider
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+ tapl.workspace.switchToOverview().dismissAllTasks()
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun switchAppByDoubleTapDivider() {
+ SplitScreenUtils.doubleTapDividerToSwitch(device)
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+
+ waitForLayersToSwitch(wmHelper)
+ waitForWindowsToSwitch(wmHelper)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+
+ private fun waitForWindowsToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appWindowsSwitched") {
+ val primaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ primaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppWindow =
+ it.wmState.visibleWindows.firstOrNull { window ->
+ secondaryApp.windowMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryAppWindow.frame.right <= primaryAppWindow.frame.left
+ } else {
+ primaryAppWindow.frame.right <= secondaryAppWindow.frame.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ } else {
+ primaryAppWindow.frame.bottom <= secondaryAppWindow.frame.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun waitForLayersToSwitch(wmHelper: WindowManagerStateHelper) {
+ wmHelper
+ .StateSyncBuilder()
+ .add("appLayersSwitched") {
+ val primaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ primaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+ val secondaryAppLayer =
+ it.layerState.visibleLayers.firstOrNull { window ->
+ secondaryApp.layerMatchesAnyOf(window)
+ }
+ ?: return@add false
+
+ val primaryVisibleRegion = primaryAppLayer.visibleRegion?.bounds ?: return@add false
+ val secondaryVisibleRegion =
+ secondaryAppLayer.visibleRegion?.bounds ?: return@add false
+
+ if (isLandscape(rotation)) {
+ return@add if (isTablet()) {
+ secondaryVisibleRegion.right <= primaryVisibleRegion.left
+ } else {
+ primaryVisibleRegion.right <= secondaryVisibleRegion.left
+ }
+ } else {
+ return@add if (isTablet()) {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ } else {
+ primaryVisibleRegion.bottom <= secondaryVisibleRegion.top
+ }
+ }
+ }
+ .waitForAndVerify()
+ }
+
+ private fun isLandscape(rotation: Rotation): Boolean {
+ val displayBounds = WindowUtils.getDisplayBounds(rotation)
+ return displayBounds.width > displayBounds.height
+ }
+
+ private fun isTablet(): Boolean {
+ val sizeDp: Point = device.displaySizeDp
+ val LARGE_SCREEN_DP_THRESHOLD = 600
+ return sizeDp.x >= LARGE_SCREEN_DP_THRESHOLD && sizeDp.y >= LARGE_SCREEN_DP_THRESHOLD
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
new file mode 100644
index 0000000..b3f1e87
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromAnotherApp.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromAnotherApp
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getNonResizeable(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ thirdApp.launchViaIntent(wmHelper)
+ wmHelper.StateSyncBuilder().withWindowSurfaceAppeared(thirdApp).waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromAnotherApp() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
new file mode 100644
index 0000000..d112116
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromHome.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromHome
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromHome() {
+ tapl.workspace.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
new file mode 100644
index 0000000..9ab924c
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBackToSplitFromRecent.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBackToSplitFromRecent
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+
+ tapl.goHome()
+ wmHelper.StateSyncBuilder().withHomeActivityVisible().waitForAndVerify()
+ }
+
+ @Test
+ open fun switchBackToSplitFromRecent() {
+ tapl.workspace.switchToOverview().currentTask.open()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
new file mode 100644
index 0000000..b694dfa
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/SwitchBetweenSplitPairs.kt
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class SwitchBetweenSplitPairs
+@JvmOverloads
+constructor(val rotation: Rotation = Rotation.ROTATION_0) {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+ private val thirdApp = SplitScreenUtils.getIme(instrumentation)
+ private val fourthApp = SplitScreenUtils.getSendNotification(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule = SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { rotation })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(rotation.value)
+
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, primaryApp, secondaryApp)
+ SplitScreenUtils.enterSplit(wmHelper, tapl, device, thirdApp, fourthApp)
+ SplitScreenUtils.waitForSplitComplete(wmHelper, thirdApp, fourthApp)
+ }
+
+ @Test
+ open fun switchBetweenSplitPairs() {
+ tapl.launchedAppState.quickSwitchToPreviousApp()
+ SplitScreenUtils.waitForSplitComplete(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ thirdApp.exit(wmHelper)
+ fourthApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
new file mode 100644
index 0000000..f78b788
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/service/splitscreen/scenarios/UnlockKeyguardToSplitScreen.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.wm.shell.flicker.service.splitscreen.scenarios
+
+import android.app.Instrumentation
+import android.tools.common.NavBar
+import android.tools.common.Rotation
+import android.tools.device.traces.parsers.WindowManagerStateHelper
+import androidx.test.platform.app.InstrumentationRegistry
+import androidx.test.uiautomator.UiDevice
+import com.android.launcher3.tapl.LauncherInstrumentation
+import org.junit.After
+import org.junit.Before
+import org.junit.Ignore
+import org.junit.Rule
+import org.junit.Test
+
+@Ignore("Base Test Class")
+abstract class UnlockKeyguardToSplitScreen {
+ private val instrumentation: Instrumentation = InstrumentationRegistry.getInstrumentation()
+ private val tapl = LauncherInstrumentation()
+ private val wmHelper = WindowManagerStateHelper(instrumentation)
+ private val device = UiDevice.getInstance(instrumentation)
+ private val primaryApp = SplitScreenUtils.getPrimary(instrumentation)
+ private val secondaryApp = SplitScreenUtils.getSecondary(instrumentation)
+
+ @Rule
+ @JvmField
+ val testSetupRule =
+ SplitScreenUtils.testSetupRule({ NavBar.MODE_GESTURAL }, { Rotation.ROTATION_0 })
+
+ @Before
+ fun setup() {
+ tapl.setEnableRotation(true)
+ tapl.setExpectedRotation(Rotation.ROTATION_0.value)
+
+ SplitScreenUtils.enterSplitViaIntent(wmHelper, primaryApp, secondaryApp)
+ }
+
+ @Test
+ open fun unlockKeyguardToSplitScreen() {
+ device.sleep()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ device.wakeUp()
+ device.pressMenu()
+ wmHelper.StateSyncBuilder().withAppTransitionIdle().waitForAndVerify()
+ }
+
+ @After
+ fun teardown() {
+ primaryApp.exit(wmHelper)
+ secondaryApp.exit(wmHelper)
+ }
+}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
index 580b153..d3434a5 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/SplitScreenBase.kt
@@ -21,6 +21,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTest
import com.android.server.wm.flicker.helpers.setRotation
import com.android.wm.shell.flicker.BaseBenchmarkTest
+import com.android.wm.shell.flicker.SplitScreenUtils
abstract class SplitScreenBase(flicker: LegacyFlickerTest) : BaseBenchmarkTest(flicker) {
protected val context: Context = instrumentation.context
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
index d1ca9ea..9c68aa4 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/CopyContentInSplitBenchmark.kt
@@ -25,7 +25,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
index 73acb1f..21ac783 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByDividerBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
index 86ffd2a..931bff6 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DismissSplitScreenByGoHomeBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenDismissed
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
index dfde3b6..7fa2c0b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/DragDividerToResizeBenchmark.kt
@@ -24,7 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
index d13e413..952051f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromAllAppsBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
index 1d41669..1de1c0c 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromNotificationBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
index b4bafa7..929c7ea 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromShortcutBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
index da44ecd..9f829c9 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenByDragFromTaskbarBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.Assume
import org.junit.Before
import org.junit.FixMethodOrder
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
index af06d6d..1d5518f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/EnterSplitScreenFromOverviewBenchmark.kt
@@ -25,7 +25,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
index 23156b5..a7fb93e 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchAppByDoubleTapDividerBenchmark.kt
@@ -28,7 +28,7 @@
import android.tools.device.traces.parsers.WindowManagerStateHelper
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
index 2d810d3..8358aff 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromAnotherAppBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
index f6df1e4..b63c765 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromHomeBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
index ba46bdc..ce5a409b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBackToSplitFromRecentBenchmark.kt
@@ -26,7 +26,7 @@
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitScreenEntered
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
index 0d871e5..9821bfa 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/SwitchBetweenSplitPairsBenchmark.kt
@@ -24,7 +24,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.Test
import org.junit.runner.RunWith
@@ -64,8 +64,7 @@
thisTransition(this)
}
- @PlatinumTest(focusArea = "sysui")
- @Presubmit @Test open fun cujCompleted() {}
+ @PlatinumTest(focusArea = "sysui") @Presubmit @Test open fun cujCompleted() {}
companion object {
@Parameterized.Parameters(name = "{0}")
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
index 7952b71..4fc4627 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/splitscreen/benchmark/UnlockKeyguardToSplitScreenBenchmark.kt
@@ -23,7 +23,7 @@
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
import androidx.test.filters.RequiresDevice
import com.android.wm.shell.flicker.splitscreen.SplitScreenBase
-import com.android.wm.shell.flicker.splitscreen.SplitScreenUtils
+import com.android.wm.shell.flicker.SplitScreenUtils
import org.junit.FixMethodOrder
import org.junit.runner.RunWith
import org.junit.runners.MethodSorters
diff --git a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
index 2231ce1..e46d34e 100644
--- a/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
+++ b/media/java/android/media/projection/IMediaProjectionWatcherCallback.aidl
@@ -17,9 +17,22 @@
package android.media.projection;
import android.media.projection.MediaProjectionInfo;
+import android.view.ContentRecordingSession;
/** {@hide} */
oneway interface IMediaProjectionWatcherCallback {
void onStart(in MediaProjectionInfo info);
void onStop(in MediaProjectionInfo info);
+ /**
+ * Called when the {@link ContentRecordingSession} was set for the current media
+ * projection.
+ *
+ * @param info always present and contains information about the media projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ void onRecordingSessionSet(
+ in MediaProjectionInfo info,
+ in @nullable ContentRecordingSession session
+ );
}
diff --git a/media/java/android/media/projection/MediaProjectionManager.java b/media/java/android/media/projection/MediaProjectionManager.java
index 5703c42..5a68c53 100644
--- a/media/java/android/media/projection/MediaProjectionManager.java
+++ b/media/java/android/media/projection/MediaProjectionManager.java
@@ -29,6 +29,7 @@
import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
+import android.view.ContentRecordingSession;
import android.view.Surface;
import java.util.Map;
@@ -300,7 +301,22 @@
/** @hide */
public static abstract class Callback {
public abstract void onStart(MediaProjectionInfo info);
+
public abstract void onStop(MediaProjectionInfo info);
+
+ /**
+ * Called when the {@link ContentRecordingSession} was set for the current media
+ * projection.
+ *
+ * @param info always present and contains information about the media projection host.
+ * @param session the recording session for the current media projection. Can be
+ * {@code null} when the recording will stop.
+ */
+ public void onRecordingSessionSet(
+ @NonNull MediaProjectionInfo info,
+ @Nullable ContentRecordingSession session
+ ) {
+ }
}
/** @hide */
@@ -335,5 +351,13 @@
}
});
}
+
+ @Override
+ public void onRecordingSessionSet(
+ @NonNull final MediaProjectionInfo info,
+ @Nullable final ContentRecordingSession session
+ ) {
+ mHandler.post(() -> mCallback.onRecordingSessionSet(info, session));
+ }
}
}
diff --git a/packages/SettingsLib/res/layout/dialog_with_icon.xml b/packages/SettingsLib/res/layout/dialog_with_icon.xml
index 2f30508..3586dcb 100644
--- a/packages/SettingsLib/res/layout/dialog_with_icon.xml
+++ b/packages/SettingsLib/res/layout/dialog_with_icon.xml
@@ -33,13 +33,13 @@
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/dialog_with_icon_title"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
style="@style/DialogWithIconTitle"/>
<TextView
android:id="@+id/dialog_with_icon_message"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
style="@style/TextAppearanceSmall"/>
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index b5e4fa3..af06d73 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -243,7 +243,9 @@
return mHelper != null ? mHelper.packageName : null;
}
- public void updateState(@NonNull String packageName, int uid, boolean isEnabled) {
+ /** Updates enabled state based on associated package. */
+ public void updateState(
+ @NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {
mHelper.updatePackageDetails(packageName, uid);
if (mAppOpsManager == null) {
mAppOpsManager = getContext().getSystemService(AppOpsManager.class);
@@ -254,7 +256,9 @@
final boolean ecmEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- if (isEnabled) {
+ if (!isEnableAllowed && !isEnabled) {
+ setEnabled(false);
+ } else if (isEnabled) {
setEnabled(true);
} else if (appOpsAllowed && isDisabledByAppOps()) {
setEnabled(true);
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
index 2e6bb53..f522fd1 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/CachedBluetoothDevice.java
@@ -583,7 +583,7 @@
*/
public void setName(String name) {
// Prevent getName() to be set to null if setName(null) is called
- if (name == null || TextUtils.equals(name, getName())) {
+ if (TextUtils.isEmpty(name) || TextUtils.equals(name, getName())) {
return;
}
mDevice.setAlias(name);
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
index cd6609e..963bd9d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/BluetoothMediaDevice.java
@@ -15,6 +15,8 @@
*/
package com.android.settingslib.media;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
@@ -22,6 +24,7 @@
import android.media.AudioManager;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import com.android.settingslib.R;
import com.android.settingslib.bluetooth.BluetoothUtils;
@@ -39,7 +42,13 @@
BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, device, routerManager, info, packageName, null);
+ }
+
+ BluetoothMediaDevice(Context context, CachedBluetoothDevice device,
+ MediaRouter2Manager routerManager, MediaRoute2Info info, String packageName,
+ RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mCachedDevice = device;
mAudioManager = context.getSystemService(AudioManager.class);
initDeviceRecord();
@@ -58,6 +67,12 @@
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public Drawable getIcon() {
return BluetoothUtils.isAdvancedUntetheredDevice(mCachedDevice.getDevice())
? mContext.getDrawable(R.drawable.ic_earbuds_advanced)
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index f2abf87..1728e40 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -91,6 +91,7 @@
MediaRouter2Manager mRouterManager;
@VisibleForTesting
String mPackageName;
+ boolean mIsScanning = false;
private MediaDevice mCurrentConnectedDevice;
private LocalBluetoothManager mBluetoothManager;
@@ -110,22 +111,29 @@
@Override
public void startScan() {
- mMediaDevices.clear();
- mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
- mRouterManager.registerScanRequest();
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
- && !TextUtils.isEmpty(mPackageName)) {
- RouteListingPreference routeListingPreference =
- mRouterManager.getRouteListingPreference(mPackageName);
- Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference, mPreferenceItemMap);
+ if (!mIsScanning) {
+ mMediaDevices.clear();
+ mRouterManager.registerCallback(mExecutor, mMediaRouterCallback);
+ mRouterManager.registerScanRequest();
+ mIsScanning = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
+ && !TextUtils.isEmpty(mPackageName)) {
+ RouteListingPreference routeListingPreference =
+ mRouterManager.getRouteListingPreference(mPackageName);
+ Api34Impl.onRouteListingPreferenceUpdated(routeListingPreference,
+ mPreferenceItemMap);
+ }
+ refreshDevices();
}
- refreshDevices();
}
@Override
public void stopScan() {
- mRouterManager.unregisterCallback(mMediaRouterCallback);
- mRouterManager.unregisterScanRequest();
+ if (mIsScanning) {
+ mRouterManager.unregisterCallback(mMediaRouterCallback);
+ mRouterManager.unregisterScanRequest();
+ mIsScanning = false;
+ }
}
/**
@@ -560,8 +568,10 @@
case TYPE_HDMI:
case TYPE_WIRED_HEADSET:
case TYPE_WIRED_HEADPHONES:
- mediaDevice =
- new PhoneMediaDevice(mContext, mRouterManager, route, mPackageName);
+ mediaDevice = mPreferenceItemMap.containsKey(route.getId()) ? new PhoneMediaDevice(
+ mContext, mRouterManager, route, mPackageName,
+ mPreferenceItemMap.get(route.getId())) : new PhoneMediaDevice(mContext,
+ mRouterManager, route, mPackageName);
break;
case TYPE_HEARING_AID:
case TYPE_BLUETOOTH_A2DP:
@@ -571,8 +581,11 @@
final CachedBluetoothDevice cachedDevice =
mBluetoothManager.getCachedDeviceManager().findDevice(device);
if (cachedDevice != null) {
- mediaDevice = new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
- route, mPackageName);
+ mediaDevice = mPreferenceItemMap.containsKey(route.getId())
+ ? new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
+ route, mPackageName, mPreferenceItemMap.get(route.getId()))
+ : new BluetoothMediaDevice(mContext, cachedDevice, mRouterManager,
+ route, mPackageName);
}
break;
case TYPE_REMOTE_AUDIO_VIDEO_RECEIVER:
@@ -701,20 +714,19 @@
List<MediaRoute2Info> selectedRouteInfos, List<MediaRoute2Info> infolist,
List<RouteListingPreference.Item> preferenceRouteListing) {
final List<MediaRoute2Info> sortedInfoList = new ArrayList<>(selectedRouteInfos);
+ infolist.removeAll(selectedRouteInfos);
+ sortedInfoList.addAll(infolist.stream().filter(
+ MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
for (RouteListingPreference.Item item : preferenceRouteListing) {
for (MediaRoute2Info info : infolist) {
if (item.getRouteId().equals(info.getId())
- && !selectedRouteInfos.contains(info)) {
+ && !selectedRouteInfos.contains(info)
+ && !info.isSystemRoute()) {
sortedInfoList.add(info);
break;
}
}
}
- if (sortedInfoList.size() != infolist.size()) {
- infolist.removeAll(sortedInfoList);
- sortedInfoList.addAll(infolist.stream().filter(
- MediaRoute2Info::isSystemRoute).collect(Collectors.toList()));
- }
return sortedInfoList;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
index 34519c9..accd88c 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/PhoneMediaDevice.java
@@ -24,10 +24,13 @@
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADSET;
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
+import android.media.RouteListingPreference;
import androidx.annotation.VisibleForTesting;
@@ -51,7 +54,12 @@
PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
String packageName) {
- super(context, routerManager, info, packageName, null);
+ this(context, routerManager, info, packageName, null);
+ }
+
+ PhoneMediaDevice(Context context, MediaRouter2Manager routerManager, MediaRoute2Info info,
+ String packageName, RouteListingPreference.Item item) {
+ super(context, routerManager, info, packageName, item);
mDeviceIconUtil = new DeviceIconUtil();
initDeviceRecord();
}
@@ -86,6 +94,12 @@
}
@Override
+ public int getSelectionBehavior() {
+ // We don't allow apps to override the selection behavior of system routes.
+ return SELECTION_BEHAVIOR_TRANSFER;
+ }
+
+ @Override
public String getSummary() {
return mSummary;
}
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 6444f3b..4b61ff1 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
@@ -1015,6 +1015,13 @@
}
@Test
+ public void setName_setDeviceNameIsEmpty() {
+ mCachedDevice.setName("");
+
+ verify(mDevice, never()).setAlias(any());
+ }
+
+ @Test
public void getProfileConnectionState_nullProfile_returnDisconnected() {
assertThat(mCachedDevice.getProfileConnectionState(null)).isEqualTo(
BluetoothProfile.STATE_DISCONNECTED);
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
index 39780f3..7b8815e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/InfoMediaManagerTest.java
@@ -114,6 +114,23 @@
}
@Test
+ public void stopScan_notStartFirst_notCallsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager, never()).unregisterScanRequest();
+ }
+
+ @Test
+ public void stopScan_startFirst_callsUnregister() {
+ mInfoMediaManager.mRouterManager = mRouterManager;
+ mInfoMediaManager.startScan();
+ mInfoMediaManager.stopScan();
+
+ verify(mRouterManager).unregisterScanRequest();
+ }
+
+ @Test
public void onRouteAdded_getAvailableRoutes_shouldAddMediaDevice() {
final List<RoutingSessionInfo> routingSessionInfos = new ArrayList<>();
final RoutingSessionInfo sessionInfo = mock(RoutingSessionInfo.class);
@@ -327,11 +344,12 @@
routeListingPreference);
mInfoMediaManager.mMediaRouterCallback.onRoutesUpdated();
- assertThat(mInfoMediaManager.mMediaDevices).hasSize(3);
+ assertThat(mInfoMediaManager.mMediaDevices).hasSize(4);
assertThat(mInfoMediaManager.mMediaDevices.get(0).getId()).isEqualTo(TEST_ID);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_4);
- assertThat(mInfoMediaManager.mMediaDevices.get(1).isSuggestedDevice()).isTrue();
- assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_3);
+ assertThat(mInfoMediaManager.mMediaDevices.get(1).getId()).isEqualTo(TEST_ID_1);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).getId()).isEqualTo(TEST_ID_4);
+ assertThat(mInfoMediaManager.mMediaDevices.get(2).isSuggestedDevice()).isTrue();
+ assertThat(mInfoMediaManager.mMediaDevices.get(3).getId()).isEqualTo(TEST_ID_3);
}
@Test
@@ -405,8 +423,13 @@
when(availableInfo3.getClientPackageName()).thenReturn(packageName);
availableRoutes.add(availableInfo3);
- when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(
- availableRoutes);
+ final MediaRoute2Info availableInfo4 = mock(MediaRoute2Info.class);
+ when(availableInfo4.getId()).thenReturn(TEST_ID_1);
+ when(availableInfo4.isSystemRoute()).thenReturn(true);
+ when(availableInfo4.getClientPackageName()).thenReturn(TEST_PACKAGE_NAME);
+ availableRoutes.add(availableInfo4);
+
+ when(mRouterManager.getAvailableRoutes(packageName)).thenReturn(availableRoutes);
return availableRoutes;
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
index c058a61..f22e090 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/media/MediaDeviceTest.java
@@ -19,6 +19,9 @@
import static android.media.MediaRoute2Info.TYPE_BUILTIN_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_REMOTE_SPEAKER;
import static android.media.MediaRoute2Info.TYPE_WIRED_HEADPHONES;
+import static android.media.RouteListingPreference.Item.SELECTION_BEHAVIOR_GO_TO_APP;
+
+import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
import static com.google.common.truth.Truth.assertThat;
@@ -32,6 +35,7 @@
import android.media.MediaRoute2Info;
import android.media.MediaRouter2Manager;
import android.media.NearbyDevice;
+import android.media.RouteListingPreference;
import android.os.Parcel;
import com.android.settingslib.bluetooth.A2dpProfile;
@@ -110,6 +114,8 @@
@Mock
private MediaRouter2Manager mMediaRouter2Manager;
+ private RouteListingPreference.Item mItem;
+
private BluetoothMediaDevice mBluetoothMediaDevice1;
private BluetoothMediaDevice mBluetoothMediaDevice2;
private BluetoothMediaDevice mBluetoothMediaDevice3;
@@ -497,4 +503,21 @@
assertThat(mBluetoothMediaDevice1.getFeatures().size()).isEqualTo(0);
}
+
+ @Test
+ public void getSelectionBehavior_setItemWithSelectionBehaviorOnSystemRoute_returnTransfer() {
+ mItem = new RouteListingPreference.Item.Builder(DEVICE_ADDRESS_1)
+ .setSelectionBehavior(SELECTION_BEHAVIOR_GO_TO_APP)
+ .build();
+ mBluetoothMediaDevice1 = new BluetoothMediaDevice(mContext, mCachedDevice1,
+ mMediaRouter2Manager, null /* MediaRoute2Info */, TEST_PACKAGE_NAME, mItem);
+ mPhoneMediaDevice =
+ new PhoneMediaDevice(mContext, mMediaRouter2Manager, mPhoneRouteInfo,
+ TEST_PACKAGE_NAME, mItem);
+
+ assertThat(mBluetoothMediaDevice1.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ assertThat(mPhoneMediaDevice.getSelectionBehavior()).isEqualTo(
+ SELECTION_BEHAVIOR_TRANSFER);
+ }
}
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index dd8eb3b..c740423 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -345,6 +345,8 @@
Settings.Global.MIN_DURATION_BETWEEN_RECOVERY_STEPS_IN_MS,
Settings.Global.MOBILE_DATA, // Candidate for backup?
Settings.Global.MOBILE_DATA_ALWAYS_ON,
+ Settings.Global.DSRM_DURATION_MILLIS,
+ Settings.Global.DSRM_ENABLED_ACTIONS,
Settings.Global.MODE_RINGER,
Settings.Global.MULTI_SIM_DATA_CALL_SUBSCRIPTION,
Settings.Global.MULTI_SIM_SMS_PROMPT,
diff --git a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
index d4a81f9..ac1ef15 100644
--- a/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
+++ b/packages/SystemUI/compose/core/src/com/android/compose/animation/Expandable.kt
@@ -70,8 +70,10 @@
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.dp
-import androidx.lifecycle.ViewTreeLifecycleOwner
-import androidx.lifecycle.ViewTreeViewModelStoreOwner
+import androidx.lifecycle.findViewTreeLifecycleOwner
+import androidx.lifecycle.findViewTreeViewModelStoreOwner
+import androidx.lifecycle.setViewTreeLifecycleOwner
+import androidx.lifecycle.setViewTreeViewModelStoreOwner
import com.android.systemui.animation.Expandable
import com.android.systemui.animation.LaunchAnimator
import kotlin.math.max
@@ -368,13 +370,10 @@
context,
overlay,
)
- ViewTreeLifecycleOwner.set(
- overlayViewGroup,
- ViewTreeLifecycleOwner.get(composeViewRoot),
- )
- ViewTreeViewModelStoreOwner.set(
- overlayViewGroup,
- ViewTreeViewModelStoreOwner.get(composeViewRoot),
+
+ overlayViewGroup.setViewTreeLifecycleOwner(composeViewRoot.findViewTreeLifecycleOwner())
+ overlayViewGroup.setViewTreeViewModelStoreOwner(
+ composeViewRoot.findViewTreeViewModelStoreOwner()
)
ViewTreeSavedStateRegistryOwner.set(
overlayViewGroup,
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
index 75bf281..13acde2 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/qs/footer/ui/compose/FooterActions.kt
@@ -121,8 +121,8 @@
}
}
- val backgroundColor = colorAttr(R.attr.underSurfaceColor)
- val contentColor = LocalAndroidColorScheme.current.deprecated.textColorPrimary
+ val backgroundColor = colorAttr(R.attr.underSurface)
+ val contentColor = LocalAndroidColorScheme.current.onSurface
val backgroundTopRadius = dimensionResource(R.dimen.qs_corner_radius)
val backgroundModifier =
remember(
@@ -268,7 +268,7 @@
val interactionSource = remember { MutableInteractionSource() }
Expandable(
- color = colorAttr(R.attr.offStateColor),
+ color = colorAttr(R.attr.shadeInactive),
shape = CircleShape,
onClick = onClick,
interactionSource = interactionSource,
@@ -287,7 +287,7 @@
number.toString(),
modifier = Modifier.align(Alignment.Center),
style = MaterialTheme.typography.bodyLarge,
- color = LocalAndroidColorScheme.current.deprecated.textColorPrimary,
+ color = colorAttr(R.attr.onShadeInactiveVariant),
// TODO(b/242040009): This should only use a standard text style instead and
// should not override the text size.
fontSize = 18.sp,
@@ -305,7 +305,7 @@
@Composable
private fun NewChangesDot(modifier: Modifier = Modifier) {
val contentDescription = stringResource(R.string.fgs_dot_content_description)
- val color = LocalAndroidColorScheme.current.deprecated.colorAccentTertiary
+ val color = LocalAndroidColorScheme.current.tertiary
Canvas(modifier.size(12.dp).semantics { this.contentDescription = contentDescription }) {
drawCircle(color)
@@ -323,10 +323,9 @@
) {
Expandable(
shape = CircleShape,
- color = colorAttr(R.attr.underSurfaceColor),
- contentColor = LocalAndroidColorScheme.current.deprecated.textColorSecondary,
- borderStroke =
- BorderStroke(1.dp, LocalAndroidColorScheme.current.deprecated.colorBackground),
+ color = colorAttr(R.attr.underSurface),
+ contentColor = LocalAndroidColorScheme.current.onSurfaceVariant,
+ borderStroke = BorderStroke(1.dp, colorAttr(R.attr.onShadeActive)),
modifier = modifier.padding(horizontal = 4.dp),
onClick = onClick,
) {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 648ef03..d208404 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -34,7 +34,7 @@
import com.android.systemui.animation.TextAnimator
import com.android.systemui.customization.R
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import java.io.PrintWriter
import java.util.Calendar
import java.util.Locale
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 1443465..12f7452 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
@@ -24,11 +24,11 @@
import android.util.Log
import androidx.annotation.OpenForTesting
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogMessage
import com.android.systemui.log.LogMessageImpl
-import com.android.systemui.log.MessageInitializer
-import com.android.systemui.log.MessagePrinter
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessageInitializer
+import com.android.systemui.log.core.MessagePrinter
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockId
import com.android.systemui.plugins.ClockMetadata
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
index 6fc52536..a4f4e13 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/ConstantStringsLoggerImpl.kt
@@ -16,6 +16,7 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
import com.google.errorprone.annotations.CompileTimeConstant
class ConstantStringsLoggerImpl(val buffer: LogBuffer, val tag: String) : ConstantStringsLogger {
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
index 2007e76..e0051f5 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogBuffer.kt
@@ -19,6 +19,11 @@
import android.os.Trace
import android.util.Log
import com.android.systemui.common.buffer.RingBuffer
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessageBuffer
+import com.android.systemui.log.core.MessageInitializer
+import com.android.systemui.log.core.MessagePrinter
import com.google.errorprone.annotations.CompileTimeConstant
import java.io.PrintWriter
import java.util.concurrent.ArrayBlockingQueue
@@ -73,7 +78,7 @@
private val maxSize: Int,
private val logcatEchoTracker: LogcatEchoTracker,
private val systrace: Boolean = true,
-) {
+) : MessageBuffer {
private val buffer = RingBuffer(maxSize) { LogMessageImpl.create() }
private val echoMessageQueue: BlockingQueue<LogMessage>? =
@@ -174,11 +179,11 @@
* store any relevant data on the message and then call [commit].
*/
@Synchronized
- fun obtain(
+ override fun obtain(
tag: String,
level: LogLevel,
messagePrinter: MessagePrinter,
- exception: Throwable? = null,
+ exception: Throwable?,
): LogMessage {
if (!mutable) {
return FROZEN_MESSAGE
@@ -195,7 +200,7 @@
* have finished filling in its data fields. The message will be echoed to logcat if necessary.
*/
@Synchronized
- fun commit(message: LogMessage) {
+ override fun commit(message: LogMessage) {
if (!mutable) {
return
}
@@ -292,11 +297,5 @@
}
}
-/**
- * A function that will be called immediately to store relevant data on the log message. The value
- * of `this` will be the LogMessage to be initialized.
- */
-typealias MessageInitializer = LogMessage.() -> Unit
-
private const val TAG = "LogBuffer"
private val FROZEN_MESSAGE = LogMessageImpl.create()
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
index 5e10f78..33cc199 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogMessageImpl.kt
@@ -16,6 +16,10 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
+import com.android.systemui.log.core.MessagePrinter
+
/** Recyclable implementation of [LogMessage]. */
data class LogMessageImpl(
override var level: LogLevel,
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
index 55f3a73..ae717df 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTracker.kt
@@ -16,6 +16,8 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+
/** Keeps track of which [LogBuffer] messages should also appear in logcat. */
interface LogcatEchoTracker {
/** Whether [bufferName] should echo messages of [level] or higher to logcat. */
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
index d0ad28f..9ff48ca 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerDebug.kt
@@ -23,6 +23,7 @@
import android.os.Looper
import android.os.Trace
import android.provider.Settings
+import com.android.systemui.log.core.LogLevel
/**
* Version of [LogcatEchoTracker] for debuggable builds
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
index 56966773..044d97f 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/LogcatEchoTrackerProd.kt
@@ -16,6 +16,8 @@
package com.android.systemui.log
+import com.android.systemui.log.core.LogLevel
+
/** Production version of [LogcatEchoTracker] that isn't configurable. */
class LogcatEchoTrackerProd : LogcatEchoTracker {
override val logInBackgroundThread = false
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt
similarity index 95%
rename from packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt
rename to packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt
index 7d9647a..d30d8e9 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogLevel.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogLevel.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log
+package com.android.systemui.log.core
import android.util.Log
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt
similarity index 92%
rename from packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt
rename to packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt
index 8c3988b..3bd6473 100644
--- a/packages/SystemUI/log/src/com/android/systemui/log/LogMessage.kt
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/LogMessage.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.log
+package com.android.systemui.log.core
import android.icu.text.SimpleDateFormat
import java.io.PrintWriter
@@ -67,6 +67,12 @@
}
/**
+ * A function that will be called immediately to store relevant data on the log message. The value
+ * of `this` will be the LogMessage to be initialized.
+ */
+typealias MessageInitializer = LogMessage.() -> Unit
+
+/**
* A function that will be called if and when the message needs to be dumped to logcat or a bug
* report. It should read the data stored by the initializer and convert it to a human-readable
* string. The value of `this` will be the LogMessage to be printed. **IMPORTANT:** The printer
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
new file mode 100644
index 0000000..5729ab2
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/Logger.kt
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+import com.google.errorprone.annotations.CompileTimeConstant
+
+/** Logs messages to the [MessageBuffer] with [tag]. */
+open class Logger(val buffer: MessageBuffer, val tag: String) {
+ /**
+ * Logs a message to the buffer.
+ *
+ * The actual string of the log message is not constructed until it is needed. To accomplish
+ * this, logging a message is a two-step process. First, a fresh instance of [LogMessage] is
+ * obtained and is passed to the [messageInitializer]. The initializer stores any relevant data
+ * on the message's fields. The message is then inserted into the buffer where it waits until it
+ * is either pushed out by newer messages or it needs to printed. If and when this latter moment
+ * occurs, the [messagePrinter] function is called on the message. It reads whatever data the
+ * initializer stored and converts it to a human-readable log message.
+ *
+ * @param level Which level to log the message at, both to the buffer and to logcat if it's
+ * echoed. In general, a module should split most of its logs into either INFO or DEBUG level.
+ * INFO level should be reserved for information that other parts of the system might care
+ * about, leaving the specifics of code's day-to-day operations to DEBUG.
+ * @param messagePrinter A function that will be called if and when the message needs to be
+ * dumped to logcat or a bug report. It should read the data stored by the initializer and
+ * convert it to a human-readable string. The value of `this` will be the [LogMessage] to be
+ * printed. **IMPORTANT:** The printer should ONLY ever reference fields on the [LogMessage]
+ * and NEVER any variables in its enclosing scope. Otherwise, the runtime will need to
+ * allocate a new instance of the printer for each call, thwarting our attempts at avoiding
+ * any sort of allocation.
+ * @param exception Provide any exception that need to be logged. This is saved as
+ * [LogMessage.exception]
+ * @param messageInitializer A function that will be called immediately to store relevant data
+ * on the log message. The value of `this` will be the [LogMessage] to be initialized.
+ */
+ @JvmOverloads
+ inline fun log(
+ level: LogLevel,
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) {
+ val message = buffer.obtain(tag, level, messagePrinter, exception)
+ messageInitializer(message)
+ buffer.commit(message)
+ }
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer. Use sparingly.
+ *
+ * This is for simpler use-cases where [message] is a compile time string constant. For
+ * use-cases where the log message is built during runtime, use the [log] overloaded method that
+ * takes in an initializer and a message printer.
+ *
+ * Buffers are limited by the number of entries, so logging more frequently will limit the time
+ * window that the [MessageBuffer] covers in a bug report. Richer logs, on the other hand, make
+ * a bug report more actionable, so using the [log] with a [MessagePrinter] to add more details
+ * to every log may do more to improve overall logging than adding more logs with this method.
+ */
+ @JvmOverloads
+ fun log(
+ level: LogLevel,
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(level, { str1!! }, exception) { str1 = message }
+
+ /**
+ * Logs a message to the buffer at [LogLevel.VERBOSE].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun v(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.VERBOSE, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.VERBOSE]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun v(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.VERBOSE, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.DEBUG].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun d(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.DEBUG, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.DEBUG]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun d(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.DEBUG, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.INFO].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun i(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.INFO, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.INFO]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun i(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.INFO, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WARNING].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun w(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WARNING, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WARNING]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun w(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WARNING, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.ERROR].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun e(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.ERROR, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.ERROR]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun e(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.ERROR, message, exception)
+
+ /**
+ * Logs a message to the buffer at [LogLevel.WTF].
+ *
+ * @see log
+ */
+ @JvmOverloads
+ inline fun wtf(
+ noinline messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ messageInitializer: MessageInitializer,
+ ) = log(LogLevel.WTF, messagePrinter, exception, messageInitializer)
+
+ /**
+ * Logs a compile-time string constant [message] to the log buffer at [LogLevel.WTF]. Use
+ * sparingly.
+ *
+ * @see log
+ */
+ @JvmOverloads
+ fun wtf(
+ @CompileTimeConstant message: String,
+ exception: Throwable? = null,
+ ) = log(LogLevel.WTF, message, exception)
+}
diff --git a/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
new file mode 100644
index 0000000..bb91633
--- /dev/null
+++ b/packages/SystemUI/log/src/com/android/systemui/log/core/MessageBuffer.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.log.core
+
+/**
+ * [MessageBuffer] is an interface that represents a buffer of log messages, and provides methods to
+ * [obtain] a log message and [commit] it to the buffer.
+ */
+interface MessageBuffer {
+ /**
+ * Obtains the next [LogMessage] from the buffer.
+ *
+ * After calling [obtain], the caller must store any relevant data on the message and then call
+ * [commit].
+ */
+ fun obtain(
+ tag: String,
+ level: LogLevel,
+ messagePrinter: MessagePrinter,
+ exception: Throwable? = null,
+ ): LogMessage
+
+ /**
+ * After acquiring a log message via [obtain], call this method to signal to the buffer that
+ * data fields have been filled.
+ */
+ fun commit(message: LogMessage)
+}
diff --git a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
index 1811c02..64c0f99 100644
--- a/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
+++ b/packages/SystemUI/plugin/bcsmartspace/src/com/android/systemui/plugins/BcSmartspaceDataPlugin.java
@@ -128,6 +128,16 @@
void setDozeAmount(float amount);
/**
+ * Set if dozing is true or false
+ */
+ default void setDozing(boolean dozing) {}
+
+ /**
+ * Set if split shade enabled
+ */
+ default void setSplitShadeEnabled(boolean enabled) {}
+
+ /**
* Set the current keyguard bypass enabled status.
*/
default void setKeyguardBypassEnabled(boolean enabled) {}
diff --git a/packages/SystemUI/res-keyguard/color/shade_disabled.xml b/packages/SystemUI/res-keyguard/color/shade_disabled.xml
new file mode 100644
index 0000000..241f203
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/color/shade_disabled.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:color="@android:color/system_neutral1_500" android:lStar="4" />
+</selector>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
index a7ffe9c..c09607d 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_number_button.xml
@@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
android:layout_gravity="center"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?attr/onShadeInactiveVariant"
android:textSize="18sp"/>
<ImageView
android:id="@+id/new_dot"
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
index 6fe7d39..1c31f1d 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions_text_button.xml
@@ -33,7 +33,7 @@
android:layout_marginEnd="12dp"
android:contentDescription="@null"
android:src="@drawable/ic_info_outline"
- android:tint="?android:attr/textColorSecondary" />
+ android:tint="?attr/onSurfaceVariant" />
<TextView
android:id="@+id/text"
@@ -43,7 +43,7 @@
android:maxLines="1"
android:ellipsize="end"
android:textAppearance="@style/TextAppearance.QS.SecurityFooter"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textColor="?attr/onSurfaceVariant"/>
<ImageView
android:id="@+id/new_dot"
@@ -62,5 +62,5 @@
android:contentDescription="@null"
android:src="@*android:drawable/ic_chevron_end"
android:autoMirrored="true"
- android:tint="?android:attr/textColorSecondary" />
+ android:tint="?attr/onSurfaceVariant" />
</com.android.systemui.animation.view.LaunchableLinearLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml b/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml
new file mode 100644
index 0000000..360ef26
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/udfps_keyguard_view.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<com.android.systemui.biometrics.UdfpsKeyguardView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/udfps_animation_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <!-- Add fingerprint views here. See udfps_keyguard_view_internal.xml. -->
+
+</com.android.systemui.biometrics.UdfpsKeyguardView>
diff --git a/packages/SystemUI/res/drawable/brightness_mirror_background.xml b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
index 2095103..b5c181b 100644
--- a/packages/SystemUI/res/drawable/brightness_mirror_background.xml
+++ b/packages/SystemUI/res/drawable/brightness_mirror_background.xml
@@ -15,6 +15,6 @@
~ limitations under the License
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
- <solid android:color="?attr/underSurfaceColor" />
+ <solid android:color="?attr/underSurface" />
<corners android:radius="@dimen/rounded_slider_background_rounded_corner" />
</shape>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
index 569ee76..95c7778c 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_drawable.xml
@@ -24,7 +24,7 @@
<shape>
<size android:height="@dimen/rounded_slider_track_width" />
<corners android:radius="@dimen/rounded_slider_track_corner_radius" />
- <solid android:color="?attr/offStateColor" />
+ <solid android:color="?attr/shadeInactive" />
</shape>
</inset>
</item>
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index 4d9188c..2ea90c7 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -22,7 +22,7 @@
android:height="@dimen/rounded_slider_height">
<shape>
<size android:height="@dimen/rounded_slider_height" />
- <solid android:color="?priv-android:attr/colorAccentPrimary" />
+ <solid android:color="?attr/shadeActive" />
<corners android:radius="@dimen/rounded_slider_corner_radius"/>
</shape>
</item>
@@ -34,7 +34,7 @@
android:right="@dimen/rounded_slider_icon_inset">
<com.android.systemui.util.AlphaTintDrawableWrapper
android:drawable="@drawable/ic_brightness"
- android:tint="?android:attr/textColorPrimaryInverse"
+ android:tint="?attr/onShadeActive"
/>
</item>
</layer-list>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/fgs_dot.xml b/packages/SystemUI/res/drawable/fgs_dot.xml
index 3669e1d..0881d7c 100644
--- a/packages/SystemUI/res/drawable/fgs_dot.xml
+++ b/packages/SystemUI/res/drawable/fgs_dot.xml
@@ -19,5 +19,5 @@
android:shape="oval"
android:width="12dp"
android:height="12dp">
- <solid android:color="?androidprv:attr/colorAccentTertiary" />
+ <solid android:color="?attr/tertiary" />
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
index ea0aafd..e138d09 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_background_primary.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
index ef950fe..f1a24aa 100644
--- a/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
+++ b/packages/SystemUI/res/drawable/qs_customizer_toolbar.xml
@@ -15,6 +15,6 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
</shape>
</inset>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
index 14cb1de9..c4e45bf 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled.xml
@@ -28,7 +28,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
- <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/materialColorPrimary"/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
android:top="@dimen/dialog_button_vertical_padding"
android:right="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
index 0544b871..1590daa 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_filled_large.xml
@@ -26,7 +26,7 @@
<item>
<shape android:shape="rectangle">
<corners android:radius="18dp"/>
- <solid android:color="?androidprv:attr/colorAccentPrimary"/>
+ <solid android:color="?androidprv:attr/materialColorPrimaryFixed"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
index a47299d..b0dc652 100644
--- a/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
+++ b/packages/SystemUI/res/drawable/qs_dialog_btn_outline.xml
@@ -29,7 +29,7 @@
<shape android:shape="rectangle">
<corners android:radius="?android:attr/buttonCornerRadius"/>
<solid android:color="@android:color/transparent"/>
- <stroke android:color="?androidprv:attr/colorAccentPrimaryVariant"
+ <stroke android:color="?androidprv:attr/materialColorPrimary"
android:width="1dp"
/>
<padding android:left="@dimen/dialog_button_horizontal_padding"
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
index c8c36b0..4a5d4af 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -28,7 +28,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?attr/offStateColor"/>
+ <solid android:color="?attr/shadeInactive"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
index 6a365000..a8c0349 100644
--- a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -28,7 +28,7 @@
</item>
<item>
<shape android:shape="rectangle">
- <solid android:color="?android:attr/colorAccent"/>
+ <solid android:color="?attr/shadeActive"/>
<corners android:radius="@dimen/qs_footer_action_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
index c9517cd9..a7e8762 100644
--- a/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
+++ b/packages/SystemUI/res/drawable/qs_footer_actions_background.xml
@@ -15,7 +15,7 @@
-->
<inset xmlns:android="http://schemas.android.com/apk/res/android">
<shape>
- <solid android:color="?attr/underSurfaceColor"/>
+ <solid android:color="?attr/underSurface"/>
<corners android:topLeftRadius="@dimen/qs_corner_radius"
android:topRightRadius="@dimen/qs_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/qs_security_footer_background.xml b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
index 381af50..0b0055b 100644
--- a/packages/SystemUI/res/drawable/qs_security_footer_background.xml
+++ b/packages/SystemUI/res/drawable/qs_security_footer_background.xml
@@ -29,7 +29,7 @@
<item>
<shape android:shape="rectangle">
<stroke android:width="1dp"
- android:color="?android:attr/colorBackground"/>
+ android:color="?attr/shadeInactive"/>
<corners android:radius="@dimen/qs_security_footer_corner_radius"/>
</shape>
</item>
diff --git a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
index 88f13b4..ca7df86 100644
--- a/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
+++ b/packages/SystemUI/res/layout/alert_dialog_title_systemui.xml
@@ -42,7 +42,7 @@
android:layout_marginBottom="16dp"
android:scaleType="fitCenter"
android:src="@null"
- android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ android:tint="?androidprv:attr/materialColorPrimary"
/>
<TextView
diff --git a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
index 7105721..21e0d2c 100644
--- a/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
+++ b/packages/SystemUI/res/layout/media_output_list_item_advanced.xml
@@ -164,7 +164,6 @@
/>
<ImageView
android:id="@+id/media_output_item_end_click_icon"
- android:src="@drawable/media_output_status_edit_session"
android:layout_width="24dp"
android:layout_height="24dp"
android:focusable="false"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 745cfc6..b8f4c0f 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -53,6 +53,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
+ android:tint="?attr/shadeActive"
android:visibility="gone" />
<FrameLayout
@@ -70,7 +71,7 @@
android:focusable="true"
android:padding="@dimen/qs_footer_icon_padding"
android:src="@*android:drawable/ic_mode_edit"
- android:tint="?android:attr/textColorPrimary" />
+ android:tint="?attr/onSurfaceVariant" />
</FrameLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index c124aea..974cad3 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -54,6 +54,6 @@
android:focusable="false"
android:importantForAccessibility="no"
android:textAppearance="@style/TextAppearance.QS.TileLabel.Secondary"
- android:textColor="?android:attr/textColorSecondary"/>
+ android:textColor="?attr/onShadeInactive"/>
</com.android.systemui.qs.tileimpl.IgnorableChildLinearLayout>
diff --git a/packages/SystemUI/res/layout/screen_record_dialog.xml b/packages/SystemUI/res/layout/screen_record_dialog.xml
index bbf3adf..f6ce70d 100644
--- a/packages/SystemUI/res/layout/screen_record_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_record_dialog.xml
@@ -15,6 +15,7 @@
limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -47,7 +48,7 @@
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
android:fontFamily="@*android:string/config_headlineFontFamily"
android:text="@string/screenrecord_permission_dialog_title"
android:layout_marginTop="22dp"
@@ -56,8 +57,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
+ android:textAppearance="@style/TextAppearance.Dialog.Body.Message"
android:gravity="center"
android:layout_marginBottom="20dp"/>
@@ -81,6 +81,7 @@
android:minHeight="48dp"
android:layout_weight="1"
android:popupBackground="@drawable/screenrecord_spinner_background"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:dropDownWidth="274dp"
android:prompt="@string/screenrecord_audio_label"/>
<Switch
@@ -117,7 +118,7 @@
android:text="@string/screenrecord_taps_label"
android:textAppearance="?android:attr/textAppearanceMedium"
android:fontFamily="@*android:string/config_headlineFontFamily"
- android:textColor="?android:attr/textColorPrimary"
+ android:textColor="?androidprv:attr/materialColorOnSurface"
android:importantForAccessibility="no"/>
<Switch
android:layout_width="wrap_content"
diff --git a/packages/SystemUI/res/layout/screen_share_dialog.xml b/packages/SystemUI/res/layout/screen_share_dialog.xml
index ab522a3..9af46c5 100644
--- a/packages/SystemUI/res/layout/screen_share_dialog.xml
+++ b/packages/SystemUI/res/layout/screen_share_dialog.xml
@@ -36,14 +36,13 @@
android:layout_width="@dimen/screenrecord_logo_size"
android:layout_height="@dimen/screenrecord_logo_size"
android:src="@drawable/ic_media_projection_permission"
- android:tint="?androidprv:attr/colorAccentPrimaryVariant"
+ android:tint="?androidprv:attr/materialColorPrimary"
android:importantForAccessibility="no"/>
<TextView
android:id="@+id/screen_share_dialog_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:textAppearance="?android:attr/textAppearanceLarge"
- android:fontFamily="@*android:string/config_headlineFontFamily"
+ android:textAppearance="@style/TextAppearance.Dialog.Title"
android:layout_marginTop="@dimen/screenrecord_title_margin_top"
android:gravity="center"/>
<Spinner
@@ -64,8 +63,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/screenrecord_permission_dialog_warning_entire_screen"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:textColorSecondary"
+ style="@style/TextAppearance.Dialog.Body.Message"
android:gravity="start"
android:lineHeight="@dimen/screenrecord_warning_line_height"/>
diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
index 00af7f4..530d752 100644
--- a/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
+++ b/packages/SystemUI/res/layout/udfps_keyguard_view_legacy.xml
@@ -16,8 +16,7 @@
-->
<com.android.systemui.biometrics.UdfpsKeyguardViewLegacy
xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:id="@+id/udfps_animation_view"
+ android:id="@+id/udfps_animation_view_legacy"
android:layout_width="match_parent"
android:layout_height="match_parent">
diff --git a/packages/SystemUI/res/values/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index 3a1d1a8..d693631 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -118,8 +118,25 @@
<attr name="wallpaperTextColorSecondary" format="reference|color" />
<attr name="wallpaperTextColorAccent" format="reference|color" />
<attr name="backgroundProtectedStyle" format="reference" />
- <attr name="offStateColor" format="reference|color" />
- <attr name="underSurfaceColor" format="reference|color" />
+
+ <!-- color attribute tokens for QS -->
+ <attr name="isQsTheme" format="boolean" />
+ <attr name="underSurface" format="reference|color"/>
+ <attr name="shadeActive" format="reference|color" />
+ <attr name="onShadeActive" format="reference|color" />
+ <attr name="onShadeActiveVariant" format="reference|color" />
+ <attr name="shadeInactive" format="reference|color" />
+ <attr name="onShadeInactive" format="reference|color" />
+ <attr name="onShadeInactiveVariant" format="reference|color" />
+ <attr name="shadeDisabled" format="reference|color" />
+ <attr name="surfaceBright" format="reference|color" />
+ <attr name="scHigh" format="reference|color" />
+ <attr name="tertiary" format="reference|color" />
+ <attr name="onSurface" format="reference|color" />
+ <attr name="onSurfaceVariant" format="reference|color" />
+ <attr name="outline" format="reference|color" />
+ <attr name="primary" format="reference|color" />
+
<declare-styleable name="SmartReplyView">
<attr name="spacing" format="dimension" />
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index cb5342a..fd74c7e 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -83,7 +83,7 @@
<style name="TextAppearance.QS">
<item name="android:textStyle">normal</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onShadeInactive</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
</style>
@@ -93,7 +93,7 @@
<style name="TextAppearance.QS.DetailItemSecondary">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">?attr/shadeActive</item>
</style>
<style name="TextAppearance.QS.Introduction">
@@ -117,11 +117,11 @@
<style name="TextAppearance.QS.DataUsage.Usage">
<item name="android:textSize">@dimen/qs_data_usage_usage_text_size</item>
- <item name="android:textColor">?android:attr/colorAccent</item>
+ <item name="android:textColor">?attr/shadeActive</item>
</style>
<style name="TextAppearance.QS.DataUsage.Secondary">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onShadeInactiveVariant</item>
</style>
<style name="TextAppearance.QS.TileLabel">
@@ -137,31 +137,31 @@
<style name="TextAppearance.QS.UserSwitcher">
<item name="android:textSize">@dimen/qs_tile_text_size</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
</style>
<!-- This is hard coded to be sans-serif-condensed to match the icons -->
<style name="TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onSurface</item>
<item name="android:textSize">14sp</item>
<item name="android:letterSpacing">0.01</item>
</style>
<style name="TextAppearance.QS.SecurityFooter" parent="@style/TextAppearance.QS.Status">
<item name="android:fontFamily">@*android:string/config_bodyFontFamily</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurface</item>
</style>
<style name="TextAppearance.QS.Status.Carriers" />
<style name="TextAppearance.QS.Status.Carriers.NoCarrierText">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="TextAppearance.QS.Status.Build">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="TextAppearance.DeviceManagementDialog.Title" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle"/>
@@ -278,10 +278,10 @@
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
- <item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
+ <item name="android:textAppearance">@style/TextAppearance.Dialog.Title</item>
</style>
- <style name="TextAppearance.DeviceManagementDialog.Content" parent="@*android:style/TextAppearance.DeviceDefault.Subhead"/>
+ <style name="TextAppearance.DeviceManagementDialog.Content" parent="@style/TextAppearance.Dialog.Body.Message"/>
<style name="BaseBrightnessDialogContainer" parent="@style/Theme.SystemUI">
<item name="android:layout_width">match_parent</item>
@@ -371,14 +371,30 @@
</style>
<style name="Theme.SystemUI.QuickSettings" parent="@*android:style/Theme.DeviceDefault">
+ <item name="isQsTheme">true</item>
<item name="lightIconTheme">@style/QSIconTheme</item>
<item name="darkIconTheme">@style/QSIconTheme</item>
<item name="android:colorError">@*android:color/error_color_material_dark</item>
<item name="android:windowIsFloating">true</item>
<item name="android:homeAsUpIndicator">@drawable/ic_arrow_back</item>
- <item name="offStateColor">@color/material_dynamic_neutral20</item>
- <item name="underSurfaceColor">@color/material_dynamic_neutral0</item>
- <item name="android:colorBackground">@color/material_dynamic_neutral10</item>
+
+ <item name="surfaceBright">?androidprv:attr/materialColorSurfaceBright</item>
+ <item name="android:colorBackground">?attr/surfaceBright</item>
+ <item name="scHigh">?androidprv:attr/materialColorSurfaceContainerHigh</item>
+ <item name="primary">?androidprv:attr/materialColorPrimary</item>
+ <item name="tertiary">?androidprv:attr/materialColorTertiary</item>
+ <item name="onSurface">?androidprv:attr/materialColorOnSurface</item>
+ <item name="onSurfaceVariant">?androidprv:attr/materialColorOnSurfaceVariant</item>
+ <item name="outline">?androidprv:attr/materialColorOutline</item>
+
+ <item name="shadeActive">@color/material_dynamic_primary90</item>
+ <item name="onShadeActive">@color/material_dynamic_primary10</item>
+ <item name="onShadeActiveVariant">@color/material_dynamic_primary30</item>
+ <item name="shadeInactive">@color/material_dynamic_neutral20</item>
+ <item name="onShadeInactive">@color/material_dynamic_neutral90</item>
+ <item name="onShadeInactiveVariant">@color/material_dynamic_neutral_variant80</item>
+ <item name="shadeDisabled">@color/shade_disabled</item>
+ <item name="underSurface">@color/material_dynamic_neutral0</item>
<item name="android:itemTextAppearance">@style/Control.MenuItem</item>
</style>
@@ -390,8 +406,15 @@
<item name="android:windowBackground">@android:color/transparent</item>
</style>
- <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@android:style/Theme.DeviceDefault.Dialog">
+ <style name="Theme.SystemUI.QuickSettings.Dialog" parent="@style/Theme.SystemUI.Dialog.QuickSettings">
+ </style>
+
+ <!-- Parent style overrides style in the dot inheritance -->
+ <style name="Theme.SystemUI.Dialog.QuickSettings" parent="@style/Theme.SystemUI.QuickSettings">
<item name="android:dialogCornerRadius">@dimen/notification_corner_radius</item>
+ <item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
+ <item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
+ <item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.QuickSettings</item>
</style>
<!-- Overridden by values-television/styles.xml with tv-specific settings -->
@@ -406,7 +429,7 @@
<item name="android:buttonBarPositiveButtonStyle">@style/Widget.Dialog.Button</item>
<item name="android:buttonBarNegativeButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
<item name="android:buttonBarNeutralButtonStyle">@style/Widget.Dialog.Button.BorderButton</item>
- <item name="android:colorBackground">?androidprv:attr/colorSurface</item>
+ <item name="android:colorBackground">?androidprv:attr/materialColorSurfaceBright</item>
<item name="android:alertDialogStyle">@style/ScrollableAlertDialogStyle</item>
<item name="android:buttonBarStyle">@style/ButtonBarStyle</item>
<item name="android:buttonBarButtonStyle">@style/Widget.Dialog.Button.Large</item>
@@ -605,11 +628,11 @@
<item name="android:letterSpacing">0.01</item>
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?attr/onSurfaceVariant</item>
</style>
<style name="QSCustomizeToolbar" parent="@*android:style/Widget.DeviceDefault.Toolbar">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?attr/onSurface</item>
<item name="android:elevation">10dp</item>
</style>
@@ -1055,7 +1078,7 @@
</style>
<style name="TextAppearance.Dialog.Title" parent="@android:style/TextAppearance.DeviceDefault.Large">
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
<item name="android:textSize">@dimen/dialog_title_text_size</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">32sp</item>
@@ -1064,7 +1087,7 @@
</style>
<style name="TextAppearance.Dialog.Body" parent="@android:style/TextAppearance.DeviceDefault.Medium">
- <item name="android:textColor">?android:attr/textColorSecondary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurfaceVariant</item>
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">@*android:string/config_headlineFontFamily</item>
<item name="android:lineHeight">20sp</item>
@@ -1092,7 +1115,7 @@
<style name="Widget.Dialog.Button">
<item name="android:buttonCornerRadius">28dp</item>
<item name="android:background">@drawable/qs_dialog_btn_filled</item>
- <item name="android:textColor">?androidprv:attr/textColorOnAccent</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimary</item>
<item name="android:textSize">14sp</item>
<item name="android:lineHeight">20sp</item>
<item name="android:fontFamily">@*android:string/config_bodyFontFamilyMedium</item>
@@ -1102,12 +1125,18 @@
<style name="Widget.Dialog.Button.BorderButton">
<item name="android:background">@drawable/qs_dialog_btn_outline</item>
- <item name="android:textColor">?android:attr/textColorPrimary</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnSurface</item>
</style>
<style name="Widget.Dialog.Button.Large">
<item name="android:background">@drawable/qs_dialog_btn_filled_large</item>
<item name="android:minHeight">56dp</item>
+ <item name="android:textColor">?androidprv:attr/materialColorOnPrimaryFixed</item>
+ </style>
+
+ <style name="Widget.Dialog.Button.QuickSettings">
+ <item name="android:textColor">?attr/primary</item>
+ <item name="android:background">?android:attr/selectableItemBackground</item>
</style>
<style name="MainSwitch.Settingslib" parent="@android:style/Theme.DeviceDefault">
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 2e6c485..751a3f8 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -104,8 +104,7 @@
* @return updated set of flags from InputMethodService based off {@param oldHints}
* Leaves original hints unmodified
*/
- public static int calculateBackDispositionHints(int oldHints,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public static int calculateBackDispositionHints(int oldHints, int backDisposition,
boolean imeShown, boolean showImeSwitcher) {
int hints = oldHints;
switch (backDisposition) {
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index 3c447a8..ca064ef 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -324,7 +324,7 @@
* Returns whether the specified sysui state is such that the back gesture should be
* disabled.
*/
- public static boolean isBackGestureDisabled(int sysuiStateFlags) {
+ public static boolean isBackGestureDisabled(int sysuiStateFlags, boolean forTrackpad) {
// Always allow when the bouncer/global actions/voice session is showing (even on top of
// the keyguard)
if ((sysuiStateFlags & SYSUI_STATE_BOUNCER_SHOWING) != 0
@@ -335,16 +335,23 @@
if ((sysuiStateFlags & SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY) != 0) {
sysuiStateFlags &= ~SYSUI_STATE_NAV_BAR_HIDDEN;
}
+
+ return (sysuiStateFlags & getBackGestureDisabledMask(forTrackpad)) != 0;
+ }
+
+ private static int getBackGestureDisabledMask(boolean forTrackpad) {
// Disable when in immersive, or the notifications are interactive
- int disableFlags = SYSUI_STATE_NAV_BAR_HIDDEN | SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ int disableFlags = SYSUI_STATE_STATUS_BAR_KEYGUARD_SHOWING;
+ if (!forTrackpad) {
+ disableFlags |= SYSUI_STATE_NAV_BAR_HIDDEN;
+ }
// EdgeBackGestureHandler ignores Back gesture when SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED.
// To allow Shade to respond to Back, we're bypassing this check (behind a flag).
if (!ALLOW_BACK_GESTURE_IN_SHADE) {
disableFlags |= SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
}
-
- return (sysuiStateFlags & disableFlags) != 0;
+ return disableFlags;
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index 84a2c25..91937af 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -42,7 +42,7 @@
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.KeyguardLargeClockLog
import com.android.systemui.log.dagger.KeyguardSmallClockLog
import com.android.systemui.plugins.ClockController
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 360f755..4793b4f 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -29,7 +29,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewScope;
import com.android.systemui.R;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.shared.clocks.DefaultClockController;
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 62a2f90..6c98376 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -40,7 +40,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.KeyguardClockLog;
import com.android.systemui.plugins.ClockController;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -352,6 +352,13 @@
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean splitShadeEnabled) {
+ mSmartspaceController.setSplitShadeEnabled(splitShadeEnabled);
+ }
+
+ /**
* Set which clock should be displayed on the keyguard. The other one will be automatically
* hidden.
*/
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
index 6f59684..3e16d55 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardPinViewController.java
@@ -43,15 +43,6 @@
private long mPinLength;
private boolean mDisabledAutoConfirmation;
- /**
- * Responsible for identifying if PIN hinting is to be enabled or not
- */
- private boolean mIsPinHinting;
-
- /**
- * Responsible for identifying if auto confirm is enabled or not in Settings
- */
- private boolean mIsAutoPinConfirmEnabledInSettings;
protected KeyguardPinViewController(KeyguardPINView view,
KeyguardUpdateMonitor keyguardUpdateMonitor,
@@ -72,9 +63,6 @@
mFeatureFlags = featureFlags;
mBackspaceKey = view.findViewById(R.id.delete_button);
mPinLength = mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser());
- mIsPinHinting = mPinLength == DEFAULT_PIN_LENGTH;
- mIsAutoPinConfirmEnabledInSettings = mLockPatternUtils.isAutoPinConfirmEnabled(
- KeyguardUpdateMonitor.getCurrentUser());
}
@Override
@@ -94,7 +82,7 @@
protected void onUserInput() {
super.onUserInput();
- if (mIsAutoPinConfirmEnabledInSettings) {
+ if (isAutoPinConfirmEnabledInSettings()) {
updateAutoConfirmationState();
if (mPasswordEntry.getText().length() == mPinLength
&& mOkButton.getVisibility() == View.INVISIBLE) {
@@ -142,7 +130,7 @@
* Updates the visibility of the OK button for auto confirm feature
*/
private void updateOKButtonVisibility() {
- if (mIsPinHinting && !mDisabledAutoConfirmation) {
+ if (isAutoPinConfirmEnabledInSettings() && !mDisabledAutoConfirmation) {
mOkButton.setVisibility(View.INVISIBLE);
} else {
mOkButton.setVisibility(View.VISIBLE);
@@ -154,9 +142,10 @@
* Visibility changes are only for auto confirmation configuration.
*/
private void updateBackSpaceVisibility() {
+ boolean isAutoConfirmation = isAutoPinConfirmEnabledInSettings();
mBackspaceKey.setTransparentMode(/* isTransparentMode= */
- mIsAutoPinConfirmEnabledInSettings && !mDisabledAutoConfirmation);
- if (mIsAutoPinConfirmEnabledInSettings) {
+ isAutoConfirmation && !mDisabledAutoConfirmation);
+ if (isAutoConfirmation) {
if (mPasswordEntry.getText().length() > 0
|| mDisabledAutoConfirmation) {
mBackspaceKey.setVisibility(View.VISIBLE);
@@ -166,8 +155,24 @@
}
}
/** Updates whether to use pin hinting or not. */
- private void updatePinHinting() {
- mPasswordEntry.setIsPinHinting(mIsAutoPinConfirmEnabledInSettings && mIsPinHinting
+ void updatePinHinting() {
+ mPasswordEntry.setIsPinHinting(isAutoPinConfirmEnabledInSettings() && isPinHinting()
&& !mDisabledAutoConfirmation);
}
+
+ /**
+ * Responsible for identifying if PIN hinting is to be enabled or not
+ */
+ private boolean isPinHinting() {
+ return mLockPatternUtils.getPinLength(KeyguardUpdateMonitor.getCurrentUser())
+ == DEFAULT_PIN_LENGTH;
+ }
+
+ /**
+ * Responsible for identifying if auto confirm is enabled or not in Settings
+ */
+ private boolean isAutoPinConfirmEnabledInSettings() {
+ //Checks if user has enabled the auto confirm in Settings
+ return mLockPatternUtils.isAutoPinConfirmEnabled(KeyguardUpdateMonitor.getCurrentUser());
+ }
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 880f242..458ca2b 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -544,7 +544,6 @@
public void setOnDismissAction(ActivityStarter.OnDismissAction action, Runnable cancelAction) {
if (mCancelAction != null) {
mCancelAction.run();
- mCancelAction = null;
}
mDismissAction = action;
mCancelAction = cancelAction;
@@ -782,9 +781,10 @@
case SimPuk:
// Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
- if (securityMode == SecurityMode.None || mLockPatternUtils.isLockScreenDisabled(
- KeyguardUpdateMonitor.getCurrentUser())) {
- finish = true;
+ boolean isLockscreenDisabled = mLockPatternUtils.isLockScreenDisabled(
+ KeyguardUpdateMonitor.getCurrentUser());
+ if (securityMode == SecurityMode.None || isLockscreenDisabled) {
+ finish = isLockscreenDisabled;
eventSubtype = BOUNCER_DISMISS_SIM;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM;
} else {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
index 00500d6..6854c97 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardStatusViewController.java
@@ -323,6 +323,13 @@
}
/**
+ * Set if the split shade is enabled
+ */
+ public void setSplitShadeEnabled(boolean enabled) {
+ mKeyguardClockSwitchController.setSplitShadeEnabled(enabled);
+ }
+
+ /**
* Updates the alignment of the KeyguardStatusView and animates the transition if requested.
*/
public void updateAlignment(
@@ -350,6 +357,9 @@
}
mInteractionJankMonitor.begin(mView, CUJ_LOCKSCREEN_CLOCK_MOVE_ANIMATION);
+ /* This transition blocks any layout changes while running. For that reason
+ * special logic with setting visibility was added to {@link BcSmartspaceView#setDozing}
+ * for split shade to avoid jump of the media object. */
ChangeBounds transition = new ChangeBounds();
if (splitShadeEnabled) {
// Excluding media from the transition on split-shade, as it doesn't transition
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index c4ea45d..518baec 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -303,6 +303,11 @@
private static final ComponentName FALLBACK_HOME_COMPONENT = new ComponentName(
"com.android.settings", "com.android.settings.FallbackHome");
+ private static final List<Integer> ABSENT_SIM_STATE_LIST = Arrays.asList(
+ TelephonyManager.SIM_STATE_ABSENT,
+ TelephonyManager.SIM_STATE_UNKNOWN,
+ TelephonyManager.SIM_STATE_NOT_READY);
+
private final Context mContext;
private final UserTracker mUserTracker;
private final KeyguardUpdateMonitorLogger mLogger;
@@ -3742,8 +3747,7 @@
mLogger.logSimState(subId, slotId, state);
boolean becameAbsent = false;
- if (!SubscriptionManager.isValidSubscriptionId(subId)
- && state != TelephonyManager.SIM_STATE_UNKNOWN) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) {
mLogger.w("invalid subId in handleSimStateChange()");
/* Only handle No SIM(ABSENT) and Card Error(CARD_IO_ERROR) due to
* handleServiceStateChange() handle other case */
@@ -3761,11 +3765,11 @@
}
} else if (state == TelephonyManager.SIM_STATE_CARD_IO_ERROR) {
updateTelephonyCapable(true);
- } else {
- return;
}
}
+ becameAbsent |= ABSENT_SIM_STATE_LIST.contains(state);
+
SimData data = mSimDatas.get(subId);
final boolean changed;
if (data == null) {
@@ -3778,7 +3782,7 @@
data.subId = subId;
data.slotId = slotId;
}
- if ((changed || becameAbsent) || state == TelephonyManager.SIM_STATE_UNKNOWN) {
+ if ((changed || becameAbsent)) {
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
if (cb != null) {
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
index 61af722..71f78c3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardVisibilityHelper.java
@@ -23,7 +23,7 @@
import com.android.app.animation.Interpolators;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.PropertyAnimator;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconView.java b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
index d1fffaa..76b073e 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconView.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconView.java
@@ -32,7 +32,6 @@
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
-import androidx.annotation.VisibleForTesting;
import com.android.internal.graphics.ColorUtils;
import com.android.settingslib.Utils;
@@ -126,7 +125,6 @@
/**
* Set the location of the lock icon.
*/
- @VisibleForTesting
public void setCenterLocation(@NonNull Point center, float radius, int drawablePadding) {
mLockIconCenter = center;
mRadius = radius;
diff --git a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
index 239a0cc..e255f5c 100644
--- a/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
+++ b/packages/SystemUI/src/com/android/keyguard/LockIconViewController.java
@@ -60,6 +60,7 @@
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.model.TransitionStep;
@@ -387,15 +388,17 @@
private void updateLockIconLocation() {
final float scaleFactor = mAuthController.getScaleFactor();
final int scaledPadding = (int) (mDefaultPaddingPx * scaleFactor);
- if (mUdfpsSupported) {
- mView.setCenterLocation(mAuthController.getUdfpsLocation(),
- mAuthController.getUdfpsRadius(), scaledPadding);
- } else {
- mView.setCenterLocation(
- new Point((int) mWidthPixels / 2,
- (int) (mHeightPixels
- - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
+ if (!mFeatureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ if (mUdfpsSupported) {
+ mView.setCenterLocation(mAuthController.getUdfpsLocation(),
+ mAuthController.getUdfpsRadius(), scaledPadding);
+ } else {
+ mView.setCenterLocation(
+ new Point((int) mWidthPixels / 2,
+ (int) (mHeightPixels
+ - ((mBottomPaddingPx + sLockIconRadiusPx) * scaleFactor))),
sLockIconRadiusPx * scaleFactor, scaledPadding);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
index e7295ef..54738c6 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricMessageDeferralLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.BiometricLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
index c00b2c6..cfad614 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/BiometricUnlockLogger.kt
@@ -19,9 +19,9 @@
import android.hardware.biometrics.BiometricSourceType
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.BiometricLog
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_DISMISS_BOUNCER
import com.android.systemui.statusbar.phone.BiometricUnlockController.MODE_NONE
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
index 1978715..d02b72f 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/CarrierTextManagerLogger.kt
@@ -19,7 +19,7 @@
import androidx.annotation.IntDef
import com.android.keyguard.CarrierTextManager.CarrierTextCallbackInfo
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.CarrierTextManagerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
index 8b925b1..bddf3b0 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardLogger.kt
@@ -19,7 +19,7 @@
import com.android.systemui.biometrics.AuthRippleController
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.KeyguardLog
import com.android.systemui.statusbar.KeyguardIndicationController
import com.google.errorprone.annotations.CompileTimeConstant
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
index be6d753..eec5b3e 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/KeyguardUpdateMonitorLogger.kt
@@ -33,12 +33,12 @@
import com.android.keyguard.TrustGrantFlags
import com.android.settingslib.fuelgauge.BatteryStatus
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.VERBOSE
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
index cb764a8..57b5db1 100644
--- a/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
+++ b/packages/SystemUI/src/com/android/keyguard/logging/TrustRepositoryLogger.kt
@@ -21,8 +21,8 @@
import com.android.systemui.keyguard.shared.model.TrustManagedModel
import com.android.systemui.keyguard.shared.model.TrustModel
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.KeyguardUpdateMonitorLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
index 670c1fa..c465585 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorHwcLayer.kt
@@ -56,9 +56,14 @@
) : DisplayCutoutBaseView(context) {
val colorMode: Int
private val useInvertedAlphaColor: Boolean
- private val color: Int
+ private var color: Int = Color.BLACK
+ set(value) {
+ field = value
+ paint.color = value
+ }
+
private val bgColor: Int
- private val cornerFilter: ColorFilter
+ private var cornerFilter: ColorFilter
private val cornerBgFilter: ColorFilter
private val clearPaint: Paint
@JvmField val transparentRect: Rect = Rect()
@@ -109,10 +114,16 @@
override fun onAttachedToWindow() {
super.onAttachedToWindow()
parent.requestTransparentRegion(this)
+ updateColors()
+ }
+
+ private fun updateColors() {
if (!debug) {
viewRootImpl.setDisplayDecoration(true)
}
+ cornerFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)
+
if (useInvertedAlphaColor) {
paint.set(clearPaint)
} else {
@@ -121,6 +132,21 @@
}
}
+ fun setDebugColor(color: Int) {
+ if (!debug) {
+ return
+ }
+
+ if (this.color == color) {
+ return
+ }
+
+ this.color = color
+
+ updateColors()
+ invalidate()
+ }
+
override fun onUpdate() {
parent.requestTransparentRegion(this)
}
@@ -367,7 +393,7 @@
/**
* Update the rounded corner drawables.
*/
- fun updateRoundedCornerDrawable(top: Drawable, bottom: Drawable) {
+ fun updateRoundedCornerDrawable(top: Drawable?, bottom: Drawable?) {
roundedCornerDrawableTop = top
roundedCornerDrawableBottom = bottom
updateRoundedCornerDrawableBounds()
diff --git a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 67d4a2e..de7a669 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -69,9 +69,9 @@
import com.android.settingslib.Utils;
import com.android.systemui.biometrics.AuthController;
import com.android.systemui.dagger.SysUISingleton;
-import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.decor.CutoutDecorProviderFactory;
import com.android.systemui.decor.DebugRoundedCornerDelegate;
+import com.android.systemui.decor.DebugRoundedCornerModel;
import com.android.systemui.decor.DecorProvider;
import com.android.systemui.decor.DecorProviderFactory;
import com.android.systemui.decor.DecorProviderKt;
@@ -80,10 +80,12 @@
import com.android.systemui.decor.PrivacyDotDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerDecorProviderFactory;
import com.android.systemui.decor.RoundedCornerResDelegateImpl;
+import com.android.systemui.decor.ScreenDecorCommand;
import com.android.systemui.log.ScreenDecorationsLogger;
import com.android.systemui.qs.SettingObserver;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.ThreadFactory;
@@ -95,7 +97,6 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
@@ -130,7 +131,7 @@
@VisibleForTesting
protected boolean mIsRegistered;
private final Context mContext;
- private final Executor mMainExecutor;
+ private final CommandRegistry mCommandRegistry;
private final SecureSettings mSecureSettings;
@VisibleForTesting
DisplayTracker.Callback mDisplayListener;
@@ -313,8 +314,8 @@
@Inject
public ScreenDecorations(Context context,
- @Main Executor mainExecutor,
SecureSettings secureSettings,
+ CommandRegistry commandRegistry,
UserTracker userTracker,
DisplayTracker displayTracker,
PrivacyDotViewController dotViewController,
@@ -324,8 +325,8 @@
ScreenDecorationsLogger logger,
AuthController authController) {
mContext = context;
- mMainExecutor = mainExecutor;
mSecureSettings = secureSettings;
+ mCommandRegistry = commandRegistry;
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mDotViewController = dotViewController;
@@ -350,6 +351,45 @@
}
};
+ private final ScreenDecorCommand.Callback mScreenDecorCommandCallback = (cmd, pw) -> {
+ // If we are exiting debug mode, we can set it (false) and bail, otherwise we will
+ // ensure that debug mode is set
+ if (cmd.getDebug() != null && !cmd.getDebug()) {
+ setDebug(false);
+ return;
+ } else {
+ // setDebug is idempotent
+ setDebug(true);
+ }
+
+ if (cmd.getColor() != null) {
+ mDebugColor = cmd.getColor();
+ mExecutor.execute(() -> {
+ if (mScreenDecorHwcLayer != null) {
+ mScreenDecorHwcLayer.setDebugColor(cmd.getColor());
+ }
+ updateColorInversionDefault();
+ });
+ }
+
+ DebugRoundedCornerModel roundedTop = null;
+ DebugRoundedCornerModel roundedBottom = null;
+ if (cmd.getRoundedTop() != null) {
+ roundedTop = cmd.getRoundedTop().toRoundedCornerDebugModel();
+ }
+ if (cmd.getRoundedBottom() != null) {
+ roundedBottom = cmd.getRoundedBottom().toRoundedCornerDebugModel();
+ }
+ if (roundedTop != null || roundedBottom != null) {
+ mDebugRoundedCornerDelegate.applyNewDebugCorners(roundedTop, roundedBottom);
+ mExecutor.execute(() -> {
+ removeAllOverlays();
+ removeHwcOverlay();
+ setupDecorations();
+ });
+ }
+ };
+
@Override
public void start() {
if (DEBUG_DISABLE_SCREEN_DECORATIONS) {
@@ -361,6 +401,8 @@
mExecutor.execute(this::startOnScreenDecorationsThread);
mDotViewController.setUiExecutor(mExecutor);
mAuthController.addCallback(mAuthControllerCallback);
+ mCommandRegistry.registerCommand(ScreenDecorCommand.SCREEN_DECOR_CMD_NAME,
+ () -> new ScreenDecorCommand(mScreenDecorCommandCallback));
}
/**
@@ -1228,7 +1270,7 @@
bottomDrawable = mDebugRoundedCornerDelegate.getBottomRoundedDrawable();
}
- if (topDrawable == null || bottomDrawable == null) {
+ if (topDrawable == null && bottomDrawable == null) {
return;
}
mScreenDecorHwcLayer.updateRoundedCornerDrawable(topDrawable, bottomDrawable);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6f0f633..946ddba 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -37,7 +37,7 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
import com.android.systemui.statusbar.LiftReveal
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index dc9ba87..ebff0b0 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -85,6 +85,8 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter;
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -153,6 +155,7 @@
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
+ @NonNull private final Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@@ -272,7 +275,8 @@
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
mPrimaryBouncerInteractor, mAlternateBouncerInteractor, mUdfpsUtils,
- mUdfpsKeyguardAccessibilityDelegate)));
+ mUdfpsKeyguardAccessibilityDelegate,
+ mUdfpsKeyguardViewModels)));
}
@Override
@@ -591,6 +595,13 @@
// Pilfer if valid overlap, don't allow following events to reach keyguard
shouldPilfer = true;
+
+ // Touch is a valid UDFPS touch. Inform the falsing manager so that the touch
+ // isn't counted against the falsing algorithm as an accidental touch.
+ // We do this on the DOWN event instead of CANCEL/UP because the CANCEL/UP events
+ // get sent too late to this receiver (after the actual cancel/up motions occur),
+ // and therefore wouldn't end up being used as part of the falsing algo.
+ mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UP:
@@ -610,7 +621,6 @@
data.getTime(),
data.getGestureStart(),
mStatusBarStateController.isDozing());
- mFalsingManager.isFalseTouch(UDFPS_AUTHENTICATION);
break;
case UNCHANGED:
@@ -784,7 +794,7 @@
private boolean shouldTryToDismissKeyguard() {
return mOverlay != null
&& mOverlay.getAnimationViewController()
- instanceof UdfpsKeyguardViewControllerLegacy
+ instanceof UdfpsKeyguardViewControllerAdapter
&& mKeyguardStateController.canDismissLockScreen()
&& !mAttemptedToDismissKeyguard;
}
@@ -829,7 +839,8 @@
@NonNull InputManager inputManager,
@NonNull UdfpsUtils udfpsUtils,
@NonNull KeyguardFaceAuthInteractor keyguardFaceAuthInteractor,
- @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate) {
+ @NonNull UdfpsKeyguardAccessibilityDelegate udfpsKeyguardAccessibilityDelegate,
+ @NonNull Provider<UdfpsKeyguardViewModels> udfpsKeyguardViewModelsProvider) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -895,6 +906,7 @@
return Unit.INSTANCE;
});
mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
+ mUdfpsKeyguardViewModels = udfpsKeyguardViewModelsProvider;
final UdfpsOverlayController mUdfpsOverlayController = new UdfpsOverlayController();
mFingerprintManager.setUdfpsOverlayController(mUdfpsOverlayController);
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index e542147..d6ef94d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -46,15 +46,19 @@
import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import com.android.keyguard.KeyguardUpdateMonitor
-import com.android.settingslib.udfps.UdfpsUtils
import com.android.settingslib.udfps.UdfpsOverlayParams
+import com.android.settingslib.udfps.UdfpsUtils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.flags.Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -64,6 +68,8 @@
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import javax.inject.Provider
private const val TAG = "UdfpsControllerOverlay"
@@ -75,6 +81,7 @@
* request. This state can persist across configuration changes via the [show] and [hide]
* methods.
*/
+@ExperimentalCoroutinesApi
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
private val context: Context,
@@ -105,6 +112,7 @@
private val isDebuggable: Boolean = Build.IS_DEBUGGABLE,
private val udfpsUtils: UdfpsUtils,
private val udfpsKeyguardAccessibilityDelegate: UdfpsKeyguardAccessibilityDelegate,
+ private val udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>,
) {
/** The view, when [isShowing], or null. */
var overlayView: UdfpsView? = null
@@ -243,27 +251,40 @@
)
}
REASON_AUTH_KEYGUARD -> {
- UdfpsKeyguardViewControllerLegacy(
- view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
- updateSensorLocation(sensorBounds)
- },
- statusBarStateController,
- shadeExpansionStateManager,
- statusBarKeyguardViewManager,
- keyguardUpdateMonitor,
- dumpManager,
- transitionController,
- configurationController,
- keyguardStateController,
- unlockedScreenOffAnimationController,
- dialogManager,
- controller,
- activityLaunchAnimator,
- featureFlags,
- primaryBouncerInteractor,
- alternateBouncerInteractor,
- udfpsKeyguardAccessibilityDelegate,
- )
+ if (featureFlags.isEnabled(REFACTOR_UDFPS_KEYGUARD_VIEWS)) {
+ udfpsKeyguardViewModels.get().setSensorBounds(sensorBounds)
+ UdfpsKeyguardViewController(
+ view.addUdfpsView(R.layout.udfps_keyguard_view),
+ statusBarStateController,
+ shadeExpansionStateManager,
+ dialogManager,
+ dumpManager,
+ alternateBouncerInteractor,
+ udfpsKeyguardViewModels.get(),
+ )
+ } else {
+ UdfpsKeyguardViewControllerLegacy(
+ view.addUdfpsView(R.layout.udfps_keyguard_view_legacy) {
+ updateSensorLocation(sensorBounds)
+ },
+ statusBarStateController,
+ shadeExpansionStateManager,
+ statusBarKeyguardViewManager,
+ keyguardUpdateMonitor,
+ dumpManager,
+ transitionController,
+ configurationController,
+ keyguardStateController,
+ unlockedScreenOffAnimationController,
+ dialogManager,
+ controller,
+ activityLaunchAnimator,
+ featureFlags,
+ primaryBouncerInteractor,
+ alternateBouncerInteractor,
+ udfpsKeyguardAccessibilityDelegate,
+ )
+ }
}
REASON_AUTH_BP -> {
// note: empty controller, currently shows no visual affordance
@@ -415,7 +436,7 @@
}
private fun shouldRotate(animation: UdfpsAnimationViewController<*>?): Boolean {
- if (animation !is UdfpsKeyguardViewControllerLegacy) {
+ if (animation !is UdfpsKeyguardViewControllerAdapter) {
// always rotate view if we're not on the keyguard
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
new file mode 100644
index 0000000..8cc15da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.content.Context
+import android.util.AttributeSet
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** View corresponding with udfps_keyguard_view.xml */
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardView(
+ context: Context,
+ attrs: AttributeSet?,
+) :
+ UdfpsAnimationView(
+ context,
+ attrs,
+ ) {
+ private val fingerprintDrawablePlaceHolder = UdfpsFpDrawable(context)
+ private var visible = false
+
+ override fun calculateAlpha(): Int {
+ return if (mPauseAuth) {
+ 0
+ } else 255 // ViewModels handle animating alpha values
+ }
+
+ override fun getDrawable(): UdfpsDrawable {
+ return fingerprintDrawablePlaceHolder
+ }
+
+ fun useExpandedOverlay(useExpandedOverlay: Boolean) {
+ mUseExpandedOverlay = useExpandedOverlay
+ }
+
+ fun isVisible(): Boolean {
+ return visible
+ }
+
+ fun setVisible(isVisible: Boolean) {
+ visible = isVisible
+ isPauseAuth = !visible
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
index 9bafeec..15bd731 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerLegacy.kt
@@ -34,6 +34,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionListener
@@ -80,7 +81,8 @@
shadeExpansionStateManager,
systemUIDialogManager,
dumpManager,
- ) {
+ ),
+ UdfpsKeyguardViewControllerAdapter {
private val useExpandedOverlay: Boolean =
featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
private var showingUdfpsBouncer = false
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
index 39199d1..2102a1f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsLogger.kt
@@ -17,10 +17,10 @@
package com.android.systemui.biometrics
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.VERBOSE
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.log.dagger.UdfpsLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
new file mode 100644
index 0000000..2a9f3ea
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/controller/UdfpsKeyguardViewController.kt
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics.ui.controller
+
+import com.android.systemui.biometrics.UdfpsAnimationViewController
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.keyguard.ui.adapter.UdfpsKeyguardViewControllerAdapter
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+/** Class that coordinates non-HBM animations during keyguard authentication. */
+@ExperimentalCoroutinesApi
+open class UdfpsKeyguardViewController(
+ val view: UdfpsKeyguardView,
+ statusBarStateController: StatusBarStateController,
+ shadeExpansionStateManager: ShadeExpansionStateManager,
+ systemUIDialogManager: SystemUIDialogManager,
+ dumpManager: DumpManager,
+ private val alternateBouncerInteractor: AlternateBouncerInteractor,
+ udfpsKeyguardViewModels: UdfpsKeyguardViewModels,
+) :
+ UdfpsAnimationViewController<UdfpsKeyguardView>(
+ view,
+ statusBarStateController,
+ shadeExpansionStateManager,
+ systemUIDialogManager,
+ dumpManager,
+ ),
+ UdfpsKeyguardViewControllerAdapter {
+ override val tag: String
+ get() = TAG
+
+ init {
+ udfpsKeyguardViewModels.bindViews(view)
+ }
+
+ public override fun onViewAttached() {
+ super.onViewAttached()
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(true)
+ }
+
+ public override fun onViewDetached() {
+ super.onViewDetached()
+ alternateBouncerInteractor.setAlternateBouncerUIAvailable(false)
+ }
+
+ override fun shouldPauseAuth(): Boolean {
+ return !view.isVisible()
+ }
+
+ override fun listenForTouchesOutsideView(): Boolean {
+ return true
+ }
+
+ companion object {
+ private const val TAG = "UdfpsKeyguardViewController"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
index 96af42b..2a457eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BluetoothLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.BluetoothLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
index 5b3a982..068f329 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/logging/BroadcastDispatcherLogger.kt
@@ -21,10 +21,10 @@
import android.content.Intent
import android.content.IntentFilter
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogMessage
import com.android.systemui.log.dagger.BroadcastDispatcherLog
import javax.inject.Inject
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 d629e3e..8e41974 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt
@@ -154,7 +154,7 @@
private fun bindViews() {
setContentView(R.layout.controls_management)
- getLifecycle().addObserver(
+ lifecycle.addObserver(
ControlsAnimations.observerForAnimations(
requireViewById<ViewGroup>(R.id.controls_management_root),
window,
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 d3ffc95..d3aa449 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt
@@ -268,7 +268,7 @@
private fun bindViews() {
setContentView(R.layout.controls_management)
- getLifecycle().addObserver(
+ lifecycle.addObserver(
ControlsAnimations.observerForAnimations(
requireViewById<ViewGroup>(R.id.controls_management_root),
window,
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 fb19ac9..d5b8693 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsProviderSelectorActivity.kt
@@ -91,7 +91,7 @@
setContentView(R.layout.controls_management)
- getLifecycle().addObserver(
+ lifecycle.addObserver(
ControlsAnimations.observerForAnimations(
requireViewById<ViewGroup>(R.id.controls_management_root),
window,
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
index 4a22e4e..557dcf4 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsActivity.kt
@@ -72,7 +72,7 @@
setContentView(R.layout.controls_fullscreen)
- getLifecycle().addObserver(
+ lifecycle.addObserver(
ControlsAnimations.observerForAnimations(
requireViewById(R.id.control_detail_root),
window,
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 76002d3..40db63d 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
+import com.android.systemui.statusbar.phone.LockscreenWallpaper
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
@@ -301,4 +302,9 @@
@IntoMap
@ClassKey(KeyguardViewConfigurator::class)
abstract fun bindKeyguardViewConfigurator(impl: KeyguardViewConfigurator): CoreStartable
+
+ @Binds
+ @IntoMap
+ @ClassKey(LockscreenWallpaper::class)
+ abstract fun bindLockscreenWallpaper(impl: LockscreenWallpaper): CoreStartable
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
index 8f3c3d6..ead24ae 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java
@@ -54,6 +54,7 @@
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.FlagsModule;
import com.android.systemui.keyboard.KeyboardModule;
+import com.android.systemui.keyguard.ui.view.layout.LockscreenLayoutModule;
import com.android.systemui.log.dagger.LogModule;
import com.android.systemui.log.dagger.MonitorLog;
import com.android.systemui.log.table.TableLogBuffer;
@@ -178,6 +179,7 @@
GarbageMonitorModule.class,
KeyboardModule.class,
LetterboxModule.class,
+ LockscreenLayoutModule.class,
LogModule.class,
MediaProjectionModule.class,
MotionToolModule.class,
diff --git a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
index 4069bc7d..5571687 100644
--- a/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
+++ b/packages/SystemUI/src/com/android/systemui/decor/DebugRoundedCornerDelegate.kt
@@ -77,16 +77,30 @@
}
fun applyNewDebugCorners(
- topCorner: DebugRoundedCornerModel,
- bottomCorner: DebugRoundedCornerModel,
+ topCorner: DebugRoundedCornerModel?,
+ bottomCorner: DebugRoundedCornerModel?,
) {
- hasTop = true
- topRoundedDrawable = topCorner.toPathDrawable(paint)
- topRoundedSize = topCorner.size()
+ topCorner?.let {
+ hasTop = true
+ topRoundedDrawable = it.toPathDrawable(paint)
+ topRoundedSize = it.size()
+ }
+ ?: {
+ hasTop = false
+ topRoundedDrawable = null
+ topRoundedSize = Size(0, 0)
+ }
- hasBottom = true
- bottomRoundedDrawable = bottomCorner.toPathDrawable(paint)
- bottomRoundedSize = bottomCorner.size()
+ bottomCorner?.let {
+ hasBottom = true
+ bottomRoundedDrawable = it.toPathDrawable(paint)
+ bottomRoundedSize = it.size()
+ }
+ ?: {
+ hasBottom = false
+ bottomRoundedDrawable = null
+ bottomRoundedSize = Size(0, 0)
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt b/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt
new file mode 100644
index 0000000..fa1d898
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/decor/ScreenDecorCommand.kt
@@ -0,0 +1,171 @@
+/*
+ * 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.decor
+
+import android.graphics.Color
+import android.graphics.Path
+import android.util.PathParser
+import com.android.systemui.statusbar.commandline.ParseableCommand
+import com.android.systemui.statusbar.commandline.Type
+import com.android.systemui.statusbar.commandline.map
+import java.io.PrintWriter
+
+/** Debug screen-decor command to be handled by the SystemUI command line interface */
+class ScreenDecorCommand(
+ private val callback: Callback,
+) : ParseableCommand(SCREEN_DECOR_CMD_NAME) {
+ val debug: Boolean? by
+ param(
+ longName = "debug",
+ description =
+ "Enter or exits debug mode. Effectively makes the corners visible and allows " +
+ "for overriding the path data for the anti-aliasing corner paths and display " +
+ "cutout.",
+ valueParser = Type.Boolean,
+ )
+
+ val color: Int? by
+ param(
+ longName = "color",
+ shortName = "c",
+ description =
+ "Set a specific color for the debug assets. See Color#parseString() for " +
+ "accepted inputs.",
+ valueParser = Type.String.map { it.toColorIntOrNull() }
+ )
+
+ val roundedTop: RoundedCornerSubCommand? by subCommand(RoundedCornerSubCommand("rounded-top"))
+
+ val roundedBottom: RoundedCornerSubCommand? by
+ subCommand(RoundedCornerSubCommand("rounded-bottom"))
+
+ override fun execute(pw: PrintWriter) {
+ callback.onExecute(this, pw)
+ }
+
+ override fun toString(): String {
+ return "ScreenDecorCommand(" +
+ "debug=$debug, " +
+ "color=$color, " +
+ "roundedTop=$roundedTop, " +
+ "roundedBottom=$roundedBottom)"
+ }
+
+ /** For use in ScreenDecorations.java, define a Callback */
+ interface Callback {
+ fun onExecute(cmd: ScreenDecorCommand, pw: PrintWriter)
+ }
+
+ companion object {
+ const val SCREEN_DECOR_CMD_NAME = "screen-decor"
+ }
+}
+
+/**
+ * Defines a subcommand suitable for `rounded-top` and `rounded-bottom`. They both have the same
+ * API.
+ */
+class RoundedCornerSubCommand(name: String) : ParseableCommand(name) {
+ val height by
+ param(
+ longName = "height",
+ description = "The height of a corner, in pixels.",
+ valueParser = Type.Int,
+ )
+ .required()
+
+ val width by
+ param(
+ longName = "width",
+ description =
+ "The width of the corner, in pixels. Likely should be equal to the height.",
+ valueParser = Type.Int,
+ )
+ .required()
+
+ val pathData by
+ param(
+ longName = "path-data",
+ shortName = "d",
+ description =
+ "PathParser-compatible path string to be rendered as the corner drawable. " +
+ "This path should be a closed arc oriented as the top-left corner " +
+ "of the device",
+ valueParser = Type.String.map { it.toPathOrNull() }
+ )
+ .required()
+
+ val viewportHeight: Float? by
+ param(
+ longName = "viewport-height",
+ description =
+ "The height of the viewport for the given path string. " +
+ "If null, the corner height will be used.",
+ valueParser = Type.Float,
+ )
+
+ val scaleY: Float
+ get() = viewportHeight?.let { height.toFloat() / it } ?: 1.0f
+
+ val viewportWidth: Float? by
+ param(
+ longName = "viewport-width",
+ description =
+ "The width of the viewport for the given path string. " +
+ "If null, the corner width will be used.",
+ valueParser = Type.Float,
+ )
+
+ val scaleX: Float
+ get() = viewportWidth?.let { width.toFloat() / it } ?: 1.0f
+
+ override fun execute(pw: PrintWriter) {
+ // Not needed for a subcommand
+ }
+
+ override fun toString(): String {
+ return "RoundedCornerSubCommand(" +
+ "height=$height," +
+ " width=$width," +
+ " pathData='$pathData'," +
+ " viewportHeight=$viewportHeight," +
+ " viewportWidth=$viewportWidth)"
+ }
+
+ fun toRoundedCornerDebugModel(): DebugRoundedCornerModel =
+ DebugRoundedCornerModel(
+ path = pathData,
+ width = width,
+ height = height,
+ scaleX = scaleX,
+ scaleY = scaleY,
+ )
+}
+
+fun String.toPathOrNull(): Path? =
+ try {
+ PathParser.createPathFromPathData(this)
+ } catch (e: Exception) {
+ null
+ }
+
+fun String.toColorIntOrNull(): Int? =
+ try {
+ Color.parseColor(this)
+ } catch (e: Exception) {
+ null
+ }
diff --git a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
index c962e51..bcfeeb9e 100644
--- a/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/display/data/repository/DisplayMetricsRepository.kt
@@ -23,7 +23,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.DisplayMetricsRepoLog
import com.android.systemui.statusbar.policy.ConfigurationController
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
index 5369780..75b8e51 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeLogger.kt
@@ -20,9 +20,9 @@
import com.android.systemui.doze.DozeLog.Reason
import com.android.systemui.doze.DozeLog.reasonToString
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.DozeLog
import com.android.systemui.statusbar.policy.DevicePostureController
import com.google.errorprone.annotations.CompileTimeConstant
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
index fdb7651..0e22406 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.dreams
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.DreamLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayLifecycleOwner.kt b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayLifecycleOwner.kt
index 8325356..003d2c7 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayLifecycleOwner.kt
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayLifecycleOwner.kt
@@ -28,7 +28,8 @@
class DreamOverlayLifecycleOwner @Inject constructor() : LifecycleOwner {
val registry: LifecycleRegistry = LifecycleRegistry(this)
- override fun getLifecycle(): Lifecycle {
- return registry
- }
+ override val lifecycle: Lifecycle
+ get() {
+ return registry
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index db5f546..6abe951 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -91,6 +91,15 @@
val NOTIFICATION_SHELF_REFACTOR =
unreleasedFlag(271161129, "notification_shelf_refactor")
+ // TODO(b/288326013): Tracking Bug
+ @JvmField
+ val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
+ unreleasedFlag(
+ 288326013,
+ "notification_async_hybrid_view_inflation",
+ teamfood = false
+ )
+
@JvmField
val ANIMATED_NOTIFICATION_SHADE_INSETS =
releasedFlag(270682168, "animated_notification_shade_insets")
@@ -251,11 +260,6 @@
@JvmField
val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
- /** Migrate the lock icon view to the new keyguard root view. */
- // TODO(b/286552209): Tracking bug.
- @JvmField
- val MIGRATE_LOCK_ICON = unreleasedFlag(238, "migrate_lock_icon")
-
/** Whether to listen for fingerprint authentication over keyguard occluding activities. */
// TODO(b/283260512): Tracking bug.
@JvmField
@@ -270,6 +274,11 @@
@JvmField
val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock")
+ /** Migrate the lock icon view to the new keyguard root view. */
+ // TODO(b/286552209): Tracking bug.
+ @JvmField
+ val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon")
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -308,7 +317,7 @@
)
@JvmField
- val QS_PIPELINE_NEW_HOST = releasedFlag(504, "qs_pipeline_new_host")
+ val QS_PIPELINE_NEW_HOST = unreleasedFlag(504, "qs_pipeline_new_host", teamfood = true)
// TODO(b/278068252): Tracking Bug
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
index e8881a4..f59ad90 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewConfigurator.kt
@@ -26,6 +26,8 @@
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.ui.binder.KeyguardIndicationAreaBinder
import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManager
+import com.android.systemui.keyguard.ui.view.layout.KeyguardLayoutManagerCommandListener
import com.android.systemui.keyguard.ui.viewmodel.KeyguardIndicationAreaViewModel
import com.android.systemui.shade.NotificationShadeWindowView
import com.android.systemui.statusbar.KeyguardIndicationController
@@ -42,6 +44,8 @@
private val notificationShadeWindowView: NotificationShadeWindowView,
private val featureFlags: FeatureFlags,
private val indicationController: KeyguardIndicationController,
+ private val keyguardLayoutManager: KeyguardLayoutManager,
+ private val keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener,
) : CoreStartable {
private var indicationAreaHandle: DisposableHandle? = null
@@ -51,6 +55,8 @@
notificationShadeWindowView.requireViewById(R.id.notification_panel) as ViewGroup
bindIndicationArea(notificationPanel)
bindLockIconView(notificationPanel)
+ keyguardLayoutManager.layoutViews()
+ keyguardLayoutManagerCommandListener.start()
}
fun bindIndicationArea(legacyParent: ViewGroup) {
@@ -59,7 +65,7 @@
// At startup, 2 views with the ID `R.id.keyguard_indication_area` will be available.
// Disable one of them
if (featureFlags.isEnabled(Flags.MIGRATE_INDICATION_AREA)) {
- legacyParent.requireViewById<View>(R.id.keyguard_indication_area).let {
+ legacyParent.findViewById<View>(R.id.keyguard_indication_area)?.let {
legacyParent.removeView(it)
}
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index 155e023..468d760 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -140,6 +140,7 @@
import com.android.systemui.flags.Flags;
import com.android.systemui.flags.SystemPropertiesHelper;
import com.android.systemui.keyguard.dagger.KeyguardModule;
+import com.android.systemui.keyguard.shared.model.TransitionStep;
import com.android.systemui.keyguard.ui.viewmodel.DreamingToLockscreenTransitionViewModel;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.navigationbar.NavigationModeController;
@@ -545,6 +546,8 @@
private CentralSurfaces mCentralSurfaces;
+ private IRemoteAnimationFinishedCallback mUnoccludeFromDreamFinishedCallback;
+
private final DeviceConfig.OnPropertiesChangedListener mOnPropertiesChangedListener =
new DeviceConfig.OnPropertiesChangedListener() {
@Override
@@ -582,17 +585,9 @@
@Override
public void onUserSwitching(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitching %d", userId));
- // Note that the mLockPatternUtils user has already been updated from setCurrentUser.
- // We need to force a reset of the views, since lockNow (called by
- // ActivityManagerService) will not reconstruct the keyguard if it is already showing.
synchronized (KeyguardViewMediator.this) {
resetKeyguardDonePendingLocked();
- if (mLockPatternUtils.isLockScreenDisabled(userId)) {
- // If we are switching to a user that has keyguard disabled, dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- } else {
- resetStateLocked();
- }
+ dismiss(null /* callback */, null /* message */);
adjustStatusBarLocked();
}
}
@@ -600,16 +595,9 @@
@Override
public void onUserSwitchComplete(int userId) {
if (DEBUG) Log.d(TAG, String.format("onUserSwitchComplete %d", userId));
- if (userId != UserHandle.USER_SYSTEM) {
- UserInfo info = UserManager.get(mContext).getUserInfo(userId);
- // Don't try to dismiss if the user has Pin/Pattern/Password set
- if (info == null || mLockPatternUtils.isSecure(userId)) {
- return;
- } else if (info.isGuest() || info.isDemo()) {
- // If we just switched to a guest, try to dismiss keyguard.
- dismiss(null /* callback */, null /* message */);
- }
- }
+ // We are calling dismiss again and with a delay as there are race conditions
+ // in some scenarios caused by async layout listeners
+ mHandler.postDelayed(() -> dismiss(null /* callback */, null /* message */), 500);
}
@Override
@@ -649,6 +637,8 @@
switch (simState) {
case TelephonyManager.SIM_STATE_NOT_READY:
case TelephonyManager.SIM_STATE_ABSENT:
+ case TelephonyManager.SIM_STATE_UNKNOWN:
+ mPendingPinLock = false;
// only force lock screen in case of missing sim if user hasn't
// gone through setup wizard
synchronized (KeyguardViewMediator.this) {
@@ -713,9 +703,6 @@
}
}
break;
- case TelephonyManager.SIM_STATE_UNKNOWN:
- mPendingPinLock = false;
- break;
default:
if (DEBUG_SIM_STATES) Log.v(TAG, "Unspecific state: " + simState);
break;
@@ -1177,6 +1164,7 @@
getRemoteSurfaceAlphaApplier().accept(0.0f);
mDreamingToLockscreenTransitionViewModel.get()
.startTransition();
+ mUnoccludeFromDreamFinishedCallback = finishedCallback;
return;
}
@@ -1256,6 +1244,19 @@
};
}
+ private Consumer<TransitionStep> getFinishedCallbackConsumer() {
+ return (TransitionStep step) -> {
+ if (mUnoccludeFromDreamFinishedCallback == null) return;
+ try {
+ mUnoccludeFromDreamFinishedCallback.onAnimationFinished();
+ mUnoccludeFromDreamFinishedCallback = null;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Wasn't able to callback", e);
+ }
+ mInteractionJankMonitor.end(CUJ_LOCKSCREEN_OCCLUSION);
+ };
+ }
+
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -1521,6 +1522,9 @@
collectFlow(viewRootImpl.getView(),
mDreamingToLockscreenTransitionViewModel.get().getDreamOverlayAlpha(),
getRemoteSurfaceAlphaApplier(), mMainDispatcher);
+ collectFlow(viewRootImpl.getView(),
+ mDreamingToLockscreenTransitionViewModel.get().getTransitionEnded(),
+ getFinishedCallbackConsumer(), mMainDispatcher);
}
}
// Most services aren't available until the system reaches the ready state, so we
@@ -2383,58 +2387,72 @@
private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
+ String message = "";
switch (msg.what) {
case SHOW:
+ message = "SHOW";
handleShow((Bundle) msg.obj);
break;
case HIDE:
+ message = "HIDE";
handleHide();
break;
case RESET:
+ message = "RESET";
handleReset(msg.arg1 != 0);
break;
case VERIFY_UNLOCK:
+ message = "VERIFY_UNLOCK";
Trace.beginSection("KeyguardViewMediator#handleMessage VERIFY_UNLOCK");
handleVerifyUnlock();
Trace.endSection();
break;
case NOTIFY_STARTED_GOING_TO_SLEEP:
+ message = "NOTIFY_STARTED_GOING_TO_SLEEP";
handleNotifyStartedGoingToSleep();
break;
case NOTIFY_FINISHED_GOING_TO_SLEEP:
+ message = "NOTIFY_FINISHED_GOING_TO_SLEEP";
handleNotifyFinishedGoingToSleep();
break;
case NOTIFY_STARTED_WAKING_UP:
+ message = "NOTIFY_STARTED_WAKING_UP";
Trace.beginSection(
"KeyguardViewMediator#handleMessage NOTIFY_STARTED_WAKING_UP");
handleNotifyStartedWakingUp();
Trace.endSection();
break;
case KEYGUARD_DONE:
+ message = "KEYGUARD_DONE";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE");
handleKeyguardDone();
Trace.endSection();
break;
case KEYGUARD_DONE_DRAWING:
+ message = "KEYGUARD_DONE_DRAWING";
Trace.beginSection("KeyguardViewMediator#handleMessage KEYGUARD_DONE_DRAWING");
handleKeyguardDoneDrawing();
Trace.endSection();
break;
case SET_OCCLUDED:
+ message = "SET_OCCLUDED";
Trace.beginSection("KeyguardViewMediator#handleMessage SET_OCCLUDED");
handleSetOccluded(msg.arg1 != 0, msg.arg2 != 0);
Trace.endSection();
break;
case KEYGUARD_TIMEOUT:
+ message = "KEYGUARD_TIMEOUT";
synchronized (KeyguardViewMediator.this) {
doKeyguardLocked((Bundle) msg.obj);
}
break;
case DISMISS:
- final DismissMessage message = (DismissMessage) msg.obj;
- handleDismiss(message.getCallback(), message.getMessage());
+ message = "DISMISS";
+ final DismissMessage dismissMsg = (DismissMessage) msg.obj;
+ handleDismiss(dismissMsg.getCallback(), dismissMsg.getMessage());
break;
case START_KEYGUARD_EXIT_ANIM:
+ message = "START_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage START_KEYGUARD_EXIT_ANIM");
synchronized (KeyguardViewMediator.this) {
@@ -2452,21 +2470,25 @@
Trace.endSection();
break;
case CANCEL_KEYGUARD_EXIT_ANIM:
+ message = "CANCEL_KEYGUARD_EXIT_ANIM";
Trace.beginSection(
"KeyguardViewMediator#handleMessage CANCEL_KEYGUARD_EXIT_ANIM");
handleCancelKeyguardExitAnimation();
Trace.endSection();
break;
case KEYGUARD_DONE_PENDING_TIMEOUT:
+ message = "KEYGUARD_DONE_PENDING_TIMEOUT";
Trace.beginSection("KeyguardViewMediator#handleMessage"
+ " KEYGUARD_DONE_PENDING_TIMEOUT");
Log.w(TAG, "Timeout while waiting for activity drawn!");
Trace.endSection();
break;
case SYSTEM_READY:
+ message = "SYSTEM_READY";
handleSystemReady();
break;
}
+ Log.d(TAG, "KeyguardViewMediator queue processing message: " + message);
}
};
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
index b796334..ed1bf3e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromLockscreenTransitionInteractor.kt
@@ -167,9 +167,10 @@
from = KeyguardState.PRIMARY_BOUNCER,
to = KeyguardState.LOCKSCREEN,
animator =
- getDefaultAnimatorForTransitionsToState(KeyguardState.LOCKSCREEN).apply {
- duration = 0
- }
+ getDefaultAnimatorForTransitionsToState(
+ KeyguardState.LOCKSCREEN
+ )
+ .apply { duration = 0 }
)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
index a8147d0..ff0db34 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/FromOccludedTransitionInteractor.kt
@@ -156,7 +156,12 @@
override fun getDefaultAnimatorForTransitionsToState(toState: KeyguardState): ValueAnimator {
return ValueAnimator().apply {
- interpolator = Interpolators.LINEAR
+ interpolator =
+ when (toState) {
+ KeyguardState.ALTERNATE_BOUNCER -> Interpolators.FAST_OUT_SLOW_IN
+ else -> Interpolators.LINEAR
+ }
+
duration =
when (toState) {
KeyguardState.LOCKSCREEN -> TO_LOCKSCREEN_DURATION
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 7c5641f..4f7abd4 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -19,7 +19,7 @@
import com.android.keyguard.logging.KeyguardLogger
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.VERBOSE
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index df7c79f..45bf20d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -21,6 +21,7 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
@@ -51,11 +52,35 @@
/** (any)->GONE transition information */
val anyStateToGoneTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.GONE }
+ repository.transitions.filter { step -> step.to == GONE }
/** (any)->AOD transition information */
val anyStateToAodTransition: Flow<TransitionStep> =
- repository.transitions.filter { step -> step.to == KeyguardState.AOD }
+ repository.transitions.filter { step -> step.to == AOD }
+
+ /** DREAMING->(any) transition information. */
+ val fromDreamingTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.from == DREAMING }
+
+ /** (any)->Lockscreen transition information */
+ val anyStateToLockscreenTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == LOCKSCREEN }
+
+ /** (any)->Occluded transition information */
+ val anyStateToOccludedTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == OCCLUDED }
+
+ /** (any)->PrimaryBouncer transition information */
+ val anyStateToPrimaryBouncerTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == PRIMARY_BOUNCER }
+
+ /** (any)->Dreaming transition information */
+ val anyStateToDreamingTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == DREAMING }
+
+ /** (any)->AlternateBouncer transition information */
+ val anyStateToAlternateBouncerTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == ALTERNATE_BOUNCER }
/** AOD->LOCKSCREEN transition information. */
val aodToLockscreenTransition: Flow<TransitionStep> = repository.transition(AOD, LOCKSCREEN)
@@ -64,6 +89,9 @@
val dreamingToLockscreenTransition: Flow<TransitionStep> =
repository.transition(DREAMING, LOCKSCREEN)
+ /** GONE->AOD transition information. */
+ val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
+
/** GONE->DREAMING transition information. */
val goneToDreamingTransition: Flow<TransitionStep> = repository.transition(GONE, DREAMING)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
index ae6fc9e..0dda625 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/TransitionInteractor.kt
@@ -44,9 +44,9 @@
abstract fun start()
fun startTransitionTo(
- toState: KeyguardState,
- animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
- resetIfCancelled: Boolean = false
+ toState: KeyguardState,
+ animator: ValueAnimator? = getDefaultAnimatorForTransitionsToState(toState),
+ resetIfCancelled: Boolean = false
): UUID? {
if (
fromState != transitionInteractor.startedKeyguardState.value &&
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.kt
new file mode 100644
index 0000000..bba0e37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractor.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.keyguard.domain.interactor
+
+import android.animation.FloatEvaluator
+import android.animation.IntEvaluator
+import com.android.systemui.common.ui.data.repository.ConfigurationRepository
+import com.android.systemui.dagger.SysUISingleton
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+
+/** Encapsulates business logic for transitions between UDFPS states on the keyguard. */
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class UdfpsKeyguardInteractor
+@Inject
+constructor(
+ configRepo: ConfigurationRepository,
+ burnInInteractor: BurnInInteractor,
+ keyguardInteractor: KeyguardInteractor,
+) {
+ private val intEvaluator = IntEvaluator()
+ private val floatEvaluator = FloatEvaluator()
+
+ val dozeAmount = keyguardInteractor.dozeAmount
+ val scaleForResolution = configRepo.scaleForResolution
+
+ /** Burn-in offsets for the UDFPS view to mitigate burn-in on AOD. */
+ val burnInOffsets: Flow<BurnInOffsets> =
+ combine(
+ keyguardInteractor.dozeAmount,
+ burnInInteractor.udfpsBurnInXOffset,
+ burnInInteractor.udfpsBurnInYOffset,
+ burnInInteractor.udfpsBurnInProgress
+ ) { dozeAmount, fullyDozingBurnInX, fullyDozingBurnInY, fullyDozingBurnInProgress ->
+ BurnInOffsets(
+ intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInX),
+ intEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInY),
+ floatEvaluator.evaluate(dozeAmount, 0, fullyDozingBurnInProgress),
+ )
+ }
+}
+
+data class BurnInOffsets(
+ val burnInXOffset: Int, // current x burn in offset based on the aodTransitionAmount
+ val burnInYOffset: Int, // current y burn in offset based on the aodTransitionAmount
+ val burnInProgress: Float, // current progress based on the aodTransitionAmount
+)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
new file mode 100644
index 0000000..ebf1beb
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/adapter/UdfpsKeyguardViewControllerAdapter.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.systemui.keyguard.ui.adapter
+
+/**
+ * Temporary adapter class while
+ * [com.android.systemui.biometrics.ui.controller.UdfpsKeyguardViewController] is being refactored
+ * before [com.android.systemui.biometrics.UdfpsKeyguardViewControllerLegacy] is removed.
+ *
+ * TODO (b/278719514): Delete once udfps keyguard view is fully refactored.
+ */
+interface UdfpsKeyguardViewControllerAdapter
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
new file mode 100644
index 0000000..728dd39
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsAodFingerprintViewBinder.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsAodFingerprintViewBinder {
+
+ /**
+ * Drives UI for the UDFPS aod fingerprint view. See [UdfpsFingerprintViewBinder] and
+ * [UdfpsBackgroundViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: LottieAnimationView,
+ viewModel: UdfpsAodViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.progress = burnInOffsets.burnInProgress
+ view.translationX = burnInOffsets.burnInXOffset.toFloat()
+ view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ }
+ }
+
+ launch { viewModel.alpha.collect { alpha -> view.alpha = alpha } }
+
+ launch {
+ viewModel.padding.collect { padding ->
+ view.setPadding(padding, padding, padding, padding)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
new file mode 100644
index 0000000..26ef468
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsBackgroundViewBinder.kt
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.content.res.ColorStateList
+import android.widget.ImageView
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsBackgroundViewBinder {
+
+ /**
+ * Drives UI for the udfps background view. See [UdfpsAodFingerprintViewBinder] and
+ * [UdfpsFingerprintViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: ImageView,
+ viewModel: BackgroundViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.transition.collect {
+ view.alpha = it.alpha
+ view.scaleX = it.scale
+ view.scaleY = it.scale
+ view.imageTintList = ColorStateList.valueOf(it.color)
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
new file mode 100644
index 0000000..0ab8e52
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsFingerprintViewBinder.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.graphics.PorterDuff
+import android.graphics.PorterDuffColorFilter
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.airbnb.lottie.LottieAnimationView
+import com.airbnb.lottie.LottieProperty
+import com.airbnb.lottie.model.KeyPath
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsFingerprintViewBinder {
+ private var udfpsIconColor = 0
+
+ /**
+ * Drives UI for the UDFPS fingerprint view when it's NOT on aod. See
+ * [UdfpsAodFingerprintViewBinder] and [UdfpsBackgroundViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: LottieAnimationView,
+ viewModel: FingerprintViewModel,
+ ) {
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.STARTED) {
+ launch {
+ viewModel.transition.collect {
+ view.alpha = it.alpha
+ view.scaleX = it.scale
+ view.scaleY = it.scale
+ if (udfpsIconColor != (it.color)) {
+ udfpsIconColor = it.color
+ view.invalidate()
+ }
+ }
+ }
+
+ launch {
+ viewModel.burnInOffsets.collect { burnInOffsets ->
+ view.translationX = burnInOffsets.burnInXOffset.toFloat()
+ view.translationY = burnInOffsets.burnInYOffset.toFloat()
+ }
+ }
+
+ launch {
+ viewModel.dozeAmount.collect { dozeAmount ->
+ // Lottie progress represents: aod=0 to lockscreen=1
+ view.progress = 1f - dozeAmount
+ }
+ }
+
+ launch {
+ viewModel.padding.collect { padding ->
+ view.setPadding(padding, padding, padding, padding)
+ }
+ }
+ }
+ }
+
+ // Add a callback that updates the color to `udfpsIconColor` whenever invalidate is called
+ view.addValueCallback(KeyPath("**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(udfpsIconColor, PorterDuff.Mode.SRC_ATOP)
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
new file mode 100644
index 0000000..b568a9a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardInternalViewBinder.kt
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.biometrics.ui.binder
+
+import android.view.View
+import com.android.systemui.R
+import com.android.systemui.keyguard.ui.binder.UdfpsAodFingerprintViewBinder
+import com.android.systemui.keyguard.ui.binder.UdfpsBackgroundViewBinder
+import com.android.systemui.keyguard.ui.binder.UdfpsFingerprintViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+object UdfpsKeyguardInternalViewBinder {
+
+ @JvmStatic
+ fun bind(
+ view: View,
+ viewModel: UdfpsKeyguardInternalViewModel,
+ aodViewModel: UdfpsAodViewModel,
+ fingerprintViewModel: FingerprintViewModel,
+ backgroundViewModel: BackgroundViewModel,
+ ) {
+ view.accessibilityDelegate = viewModel.accessibilityDelegate
+
+ // bind child views
+ UdfpsAodFingerprintViewBinder.bind(view.findViewById(R.id.udfps_aod_fp), aodViewModel)
+ UdfpsFingerprintViewBinder.bind(
+ view.findViewById(R.id.udfps_lockscreen_fp),
+ fingerprintViewModel
+ )
+ UdfpsBackgroundViewBinder.bind(
+ view.findViewById(R.id.udfps_keyguard_fp_bg),
+ backgroundViewModel
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
new file mode 100644
index 0000000..667abae
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/UdfpsKeyguardViewBinder.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import android.graphics.RectF
+import android.view.View
+import android.widget.FrameLayout
+import androidx.asynclayoutinflater.view.AsyncLayoutInflater
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.R
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.biometrics.ui.binder.UdfpsKeyguardInternalViewBinder
+import com.android.systemui.keyguard.ui.viewmodel.BackgroundViewModel
+import com.android.systemui.keyguard.ui.viewmodel.FingerprintViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsAodViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardInternalViewModel
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.launch
+
+@ExperimentalCoroutinesApi
+object UdfpsKeyguardViewBinder {
+ /**
+ * Drives UI for the keyguard UDFPS view. Inflates child views on a background thread. For view
+ * binders for its child views, see [UdfpsFingerprintViewBinder], [UdfpsBackgroundViewBinder] &
+ * [UdfpsAodFingerprintViewBinder].
+ */
+ @JvmStatic
+ fun bind(
+ view: UdfpsKeyguardView,
+ viewModel: UdfpsKeyguardViewModel,
+ udfpsKeyguardInternalViewModel: UdfpsKeyguardInternalViewModel,
+ aodViewModel: UdfpsAodViewModel,
+ fingerprintViewModel: FingerprintViewModel,
+ backgroundViewModel: BackgroundViewModel,
+ ) {
+ view.useExpandedOverlay(viewModel.useExpandedOverlay())
+
+ val layoutInflaterFinishListener =
+ AsyncLayoutInflater.OnInflateFinishedListener { inflatedInternalView, _, parent ->
+ UdfpsKeyguardInternalViewBinder.bind(
+ inflatedInternalView,
+ udfpsKeyguardInternalViewModel,
+ aodViewModel,
+ fingerprintViewModel,
+ backgroundViewModel,
+ )
+ if (viewModel.useExpandedOverlay()) {
+ val lp = inflatedInternalView.layoutParams as FrameLayout.LayoutParams
+ lp.width = viewModel.sensorBounds.width()
+ lp.height = viewModel.sensorBounds.height()
+ val relativeToView =
+ getBoundsRelativeToView(
+ inflatedInternalView,
+ RectF(viewModel.sensorBounds),
+ )
+ lp.setMarginsRelative(
+ relativeToView.left.toInt(),
+ relativeToView.top.toInt(),
+ relativeToView.right.toInt(),
+ relativeToView.bottom.toInt(),
+ )
+ parent!!.addView(inflatedInternalView, lp)
+ } else {
+ parent!!.addView(inflatedInternalView)
+ }
+ }
+ val inflater = AsyncLayoutInflater(view.context)
+ inflater.inflate(R.layout.udfps_keyguard_view_internal, view, layoutInflaterFinishListener)
+
+ view.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ combine(aodViewModel.isVisible, fingerprintViewModel.visible) {
+ isAodVisible,
+ isFingerprintVisible ->
+ isAodVisible || isFingerprintVisible
+ }
+ .collect { view.setVisible(it) }
+ }
+ }
+ }
+ }
+
+ /**
+ * Converts coordinates of RectF relative to the screen to coordinates relative to this view.
+ *
+ * @param bounds RectF based off screen coordinates in current orientation
+ */
+ private fun getBoundsRelativeToView(view: View, bounds: RectF): RectF {
+ val pos: IntArray = view.locationOnScreen
+ return RectF(
+ bounds.left - pos[0],
+ bounds.top - pos[1],
+ bounds.right - pos[0],
+ bounds.bottom - pos[1]
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index a62f383..0077f2d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -19,10 +19,7 @@
import android.content.Context
import android.util.AttributeSet
-import android.view.Gravity
-import android.view.ViewGroup.LayoutParams.MATCH_PARENT
-import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
-import android.widget.FrameLayout
+import androidx.constraintlayout.widget.ConstraintLayout
import com.android.keyguard.LockIconView
import com.android.systemui.R
@@ -31,7 +28,7 @@
context: Context,
private val attrs: AttributeSet?,
) :
- FrameLayout(
+ ConstraintLayout(
context,
attrs,
) {
@@ -43,31 +40,11 @@
private fun addIndicationTextArea() {
val view = KeyguardIndicationArea(context, attrs)
- addView(
- view,
- FrameLayout.LayoutParams(
- MATCH_PARENT,
- WRAP_CONTENT,
- )
- .apply {
- gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL
- bottomMargin = R.dimen.keyguard_indication_margin_bottom.dp()
- }
- )
+ addView(view)
}
private fun addLockIconView() {
val view = LockIconView(context, attrs).apply { id = R.id.lock_icon_view }
- addView(
- view,
- LayoutParams(
- WRAP_CONTENT,
- WRAP_CONTENT,
- )
- )
- }
-
- private fun Int.dp(): Int {
- return context.resources.getDimensionPixelSize(this)
+ addView(view)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
new file mode 100644
index 0000000..baaeb60
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayout.kt
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.Context
+import android.graphics.Point
+import android.graphics.Rect
+import android.util.DisplayMetrics
+import android.view.View
+import android.view.ViewGroup.LayoutParams.MATCH_PARENT
+import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
+import android.view.WindowManager
+import androidx.annotation.VisibleForTesting
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.constraintlayout.widget.ConstraintSet.BOTTOM
+import androidx.constraintlayout.widget.ConstraintSet.END
+import androidx.constraintlayout.widget.ConstraintSet.PARENT_ID
+import androidx.constraintlayout.widget.ConstraintSet.START
+import androidx.constraintlayout.widget.ConstraintSet.TOP
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import javax.inject.Inject
+
+/**
+ * Positions elements of the lockscreen to the default position.
+ *
+ * This will be the most common use case for phones in portrait mode.
+ */
+@SysUISingleton
+class DefaultLockscreenLayout
+@Inject
+constructor(
+ private val authController: AuthController,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val windowManager: WindowManager,
+ private val context: Context,
+) : LockscreenLayout {
+ override val id: String = DEFAULT
+
+ override fun layoutIndicationArea(rootView: KeyguardRootView) {
+ val indicationArea = rootView.findViewById<View>(R.id.keyguard_indication_area) ?: return
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(indicationArea.id, MATCH_PARENT)
+ constrainHeight(indicationArea.id, WRAP_CONTENT)
+ connect(
+ indicationArea.id,
+ BOTTOM,
+ PARENT_ID,
+ BOTTOM,
+ R.dimen.keyguard_indication_margin_bottom.dp()
+ )
+ connect(indicationArea.id, START, PARENT_ID, START)
+ connect(indicationArea.id, END, PARENT_ID, END)
+ applyTo(rootView)
+ }
+ }
+
+ override fun layoutLockIcon(rootView: KeyguardRootView) {
+ val isUdfpsSupported = keyguardUpdateMonitor.isUdfpsSupported
+ val scaleFactor: Float = authController.scaleFactor
+ val mBottomPaddingPx = R.dimen.lock_icon_margin_bottom.dp()
+ val mDefaultPaddingPx = R.dimen.lock_icon_padding.dp()
+ val scaledPadding: Int = (mDefaultPaddingPx * scaleFactor).toInt()
+ val bounds = windowManager.currentWindowMetrics.bounds
+ val widthPixels = bounds.right.toFloat()
+ val heightPixels = bounds.bottom.toFloat()
+ val defaultDensity =
+ DisplayMetrics.DENSITY_DEVICE_STABLE.toFloat() /
+ DisplayMetrics.DENSITY_DEFAULT.toFloat()
+ val lockIconRadiusPx = (defaultDensity * 36).toInt()
+
+ if (isUdfpsSupported) {
+ authController.udfpsLocation?.let { udfpsLocation ->
+ centerLockIcon(udfpsLocation, authController.udfpsRadius, scaledPadding, rootView)
+ }
+ } else {
+ centerLockIcon(
+ Point(
+ (widthPixels / 2).toInt(),
+ (heightPixels - ((mBottomPaddingPx + lockIconRadiusPx) * scaleFactor)).toInt()
+ ),
+ lockIconRadiusPx * scaleFactor,
+ scaledPadding,
+ rootView
+ )
+ }
+ }
+
+ @VisibleForTesting
+ internal fun centerLockIcon(
+ center: Point,
+ radius: Float,
+ drawablePadding: Int,
+ rootView: KeyguardRootView,
+ ) {
+ val lockIconView = rootView.findViewById<View>(R.id.lock_icon_view) ?: return
+ val lockIcon = lockIconView.findViewById<View>(R.id.lock_icon) ?: return
+ lockIcon.setPadding(drawablePadding, drawablePadding, drawablePadding, drawablePadding)
+
+ val sensorRect =
+ Rect().apply {
+ set(
+ center.x - radius.toInt(),
+ center.y - radius.toInt(),
+ center.x + radius.toInt(),
+ center.y + radius.toInt(),
+ )
+ }
+
+ rootView.getConstraintSet().apply {
+ constrainWidth(lockIconView.id, sensorRect.right - sensorRect.left)
+ constrainHeight(lockIconView.id, sensorRect.bottom - sensorRect.top)
+ connect(lockIconView.id, TOP, PARENT_ID, TOP, sensorRect.top)
+ connect(lockIconView.id, START, PARENT_ID, START, sensorRect.left)
+ applyTo(rootView)
+ }
+ }
+
+ private fun Int.dp(): Int {
+ return context.resources.getDimensionPixelSize(this)
+ }
+
+ private fun ConstraintLayout.getConstraintSet(): ConstraintSet {
+ val cs = ConstraintSet()
+ cs.clone(this)
+ return cs
+ }
+
+ companion object {
+ const val DEFAULT = "default"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
new file mode 100644
index 0000000..9bc6302
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManager.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.content.res.Configuration
+import androidx.constraintlayout.widget.ConstraintSet
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import javax.inject.Inject
+
+/**
+ * Manages layout changes for the lockscreen.
+ *
+ * To add a layout, add an entry to the map with a unique id and call #transitionToLayout(string).
+ */
+@SysUISingleton
+class KeyguardLayoutManager
+@Inject
+constructor(
+ configurationController: ConfigurationController,
+ layouts: Set<@JvmSuppressWildcards LockscreenLayout>,
+ private val keyguardRootView: KeyguardRootView,
+) {
+ internal val layoutIdMap: Map<String, LockscreenLayout> = layouts.associateBy { it.id }
+ private var layout: LockscreenLayout? = layoutIdMap[DEFAULT]
+
+ init {
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ layoutViews()
+ }
+ }
+ )
+ }
+
+ /**
+ * Transitions to a layout.
+ *
+ * @param layoutId
+ * @return whether the transition has succeeded.
+ */
+ fun transitionToLayout(layoutId: String): Boolean {
+ layout = layoutIdMap[layoutId] ?: return false
+ layoutViews()
+ return true
+ }
+
+ fun layoutViews() {
+ layout?.layoutViews(keyguardRootView)
+ }
+
+ companion object {
+ const val TAG = "KeyguardLayoutManager"
+ }
+}
+
+interface LockscreenLayout {
+ val id: String
+
+ fun layoutViews(rootView: KeyguardRootView) {
+ // Clear constraints.
+ ConstraintSet()
+ .apply {
+ clone(rootView)
+ knownIds.forEach { getConstraint(it).layout.copyFrom(ConstraintSet.Layout()) }
+ }
+ .applyTo(rootView)
+ layoutIndicationArea(rootView)
+ layoutLockIcon(rootView)
+ }
+ fun layoutIndicationArea(rootView: KeyguardRootView)
+ fun layoutLockIcon(rootView: KeyguardRootView)
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
new file mode 100644
index 0000000..b351ea8
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListener.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import java.io.PrintWriter
+import javax.inject.Inject
+
+/** Uses $ adb shell cmd statusbar layout <LayoutId> */
+class KeyguardLayoutManagerCommandListener
+@Inject
+constructor(
+ private val commandRegistry: CommandRegistry,
+ private val keyguardLayoutManager: KeyguardLayoutManager
+) {
+ private val layoutCommand = KeyguardLayoutManagerCommand()
+
+ fun start() {
+ commandRegistry.registerCommand(COMMAND) { layoutCommand }
+ }
+
+ internal inner class KeyguardLayoutManagerCommand : Command {
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val arg = args.getOrNull(0)
+ if (arg == null || arg.lowercase() == "help") {
+ help(pw)
+ return
+ }
+
+ if (keyguardLayoutManager.transitionToLayout(arg)) {
+ pw.println("Transition succeeded!")
+ } else {
+ pw.println("Invalid argument! To see available layout ids, run:")
+ pw.println("$ adb shell cmd statusbar layout help")
+ }
+ }
+
+ override fun help(pw: PrintWriter) {
+ pw.println("Usage: $ adb shell cmd statusbar layout <layoutId>")
+ pw.println("Existing Layout Ids: ")
+ keyguardLayoutManager.layoutIdMap.forEach { entry -> pw.println("${entry.key}") }
+ }
+ }
+
+ companion object {
+ internal const val COMMAND = "layout"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.kt
new file mode 100644
index 0000000..00f93e3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/layout/LockscreenLayoutModule.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.ui.view.layout
+
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.IntoSet
+
+@Module
+abstract class LockscreenLayoutModule {
+ @Binds
+ @IntoSet
+ abstract fun bindDefaultLayout(
+ defaultLockscreenLayout: DefaultLockscreenLayout
+ ): LockscreenLayout
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
index 9ca4bd6..e24d326 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModel.kt
@@ -48,7 +48,7 @@
)
val transitionEnded =
- keyguardTransitionInteractor.dreamingToLockscreenTransition.filter { step ->
+ keyguardTransitionInteractor.fromDreamingTransition.filter { step ->
step.transitionState == TransitionState.FINISHED ||
step.transitionState == TransitionState.CANCELED
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
new file mode 100644
index 0000000..667c2f1
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModel.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import com.android.systemui.R
+import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+
+/** View-model for UDFPS AOD view. */
+@ExperimentalCoroutinesApi
+class UdfpsAodViewModel
+@Inject
+constructor(
+ val interactor: UdfpsKeyguardInteractor,
+ val context: Context,
+) {
+ val alpha: Flow<Float> = interactor.dozeAmount
+ val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+ val isVisible: Flow<Boolean> = alpha.map { it != 0f }
+
+ // Padding between the fingerprint icon and its bounding box in pixels.
+ val padding: Flow<Int> =
+ interactor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
new file mode 100644
index 0000000..d894a11
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardInternalViewModel.kt
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.biometrics.UdfpsKeyguardAccessibilityDelegate
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardInternalViewModel
+@Inject
+constructor(val accessibilityDelegate: UdfpsKeyguardAccessibilityDelegate)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
new file mode 100644
index 0000000..929f27f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModel.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Rect
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+class UdfpsKeyguardViewModel
+@Inject
+constructor(
+ private val featureFlags: FeatureFlags,
+) {
+ var sensorBounds: Rect = Rect()
+
+ fun useExpandedOverlay(): Boolean {
+ return featureFlags.isEnabled(Flags.UDFPS_NEW_TOUCH_DETECTION)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
new file mode 100644
index 0000000..098b481
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsKeyguardViewModels.kt
@@ -0,0 +1,36 @@
+package com.android.systemui.keyguard.ui.viewmodel
+
+import android.graphics.Rect
+import com.android.systemui.biometrics.UdfpsKeyguardView
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.ui.binder.UdfpsKeyguardViewBinder
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class UdfpsKeyguardViewModels
+@Inject
+constructor(
+ private val viewModel: UdfpsKeyguardViewModel,
+ private val internalViewModel: UdfpsKeyguardInternalViewModel,
+ private val aodViewModel: UdfpsAodViewModel,
+ private val lockscreenFingerprintViewModel: FingerprintViewModel,
+ private val lockscreenBackgroundViewModel: BackgroundViewModel,
+) {
+
+ fun setSensorBounds(sensorBounds: Rect) {
+ viewModel.sensorBounds = sensorBounds
+ }
+
+ fun bindViews(view: UdfpsKeyguardView) {
+ UdfpsKeyguardViewBinder.bind(
+ view,
+ viewModel,
+ internalViewModel,
+ aodViewModel,
+ lockscreenFingerprintViewModel,
+ lockscreenBackgroundViewModel
+ )
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
new file mode 100644
index 0000000..fd4b666
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModel.kt
@@ -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.systemui.keyguard.ui.viewmodel
+
+import android.content.Context
+import androidx.annotation.ColorInt
+import com.android.settingslib.Utils.getColorAttrDefaultColor
+import com.android.systemui.R
+import com.android.systemui.keyguard.domain.interactor.BurnInOffsets
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import javax.inject.Inject
+import kotlin.math.roundToInt
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.merge
+
+/** View-model for UDFPS lockscreen views. */
+@ExperimentalCoroutinesApi
+open class UdfpsLockscreenViewModel(
+ context: Context,
+ lockscreenColorResId: Int,
+ alternateBouncerColorResId: Int,
+ transitionInteractor: KeyguardTransitionInteractor,
+) {
+ private val toLockscreen: Flow<TransitionViewModel> =
+ transitionInteractor.anyStateToLockscreenTransition.map {
+ TransitionViewModel(
+ alpha =
+ if (it.from == KeyguardState.AOD) {
+ it.value // animate
+ } else {
+ 1f
+ },
+ scale = 1f,
+ color = getColorAttrDefaultColor(context, lockscreenColorResId),
+ )
+ }
+
+ private val toAlternateBouncer: Flow<TransitionViewModel> =
+ transitionInteractor.anyStateToAlternateBouncerTransition.map {
+ TransitionViewModel(
+ alpha = 1f,
+ scale =
+ if (visibleInKeyguardState(it.from)) {
+ 1f
+ } else {
+ it.value
+ },
+ color = getColorAttrDefaultColor(context, alternateBouncerColorResId),
+ )
+ }
+
+ private val fadeOut: Flow<TransitionViewModel> =
+ merge(
+ transitionInteractor.anyStateToGoneTransition,
+ transitionInteractor.anyStateToAodTransition,
+ transitionInteractor.anyStateToOccludedTransition,
+ transitionInteractor.anyStateToPrimaryBouncerTransition,
+ transitionInteractor.anyStateToDreamingTransition,
+ )
+ .map {
+ TransitionViewModel(
+ alpha =
+ if (visibleInKeyguardState(it.from)) {
+ 1f - it.value
+ } else {
+ 0f
+ },
+ scale = 1f,
+ color =
+ if (it.from == KeyguardState.ALTERNATE_BOUNCER) {
+ getColorAttrDefaultColor(context, alternateBouncerColorResId)
+ } else {
+ getColorAttrDefaultColor(context, lockscreenColorResId)
+ },
+ )
+ }
+
+ private fun visibleInKeyguardState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF,
+ KeyguardState.DOZING,
+ KeyguardState.DREAMING,
+ KeyguardState.AOD,
+ KeyguardState.PRIMARY_BOUNCER,
+ KeyguardState.GONE,
+ KeyguardState.OCCLUDED -> false
+ KeyguardState.LOCKSCREEN,
+ KeyguardState.ALTERNATE_BOUNCER -> true
+ }
+ }
+
+ val transition: Flow<TransitionViewModel> =
+ merge(
+ toAlternateBouncer,
+ toLockscreen,
+ fadeOut,
+ )
+ val visible: Flow<Boolean> = transition.map { it.alpha != 0f }
+}
+
+@ExperimentalCoroutinesApi
+class FingerprintViewModel
+@Inject
+constructor(
+ val context: Context,
+ transitionInteractor: KeyguardTransitionInteractor,
+ interactor: UdfpsKeyguardInteractor,
+) :
+ UdfpsLockscreenViewModel(
+ context,
+ android.R.attr.textColorPrimary,
+ com.android.internal.R.attr.materialColorOnPrimaryFixed,
+ transitionInteractor,
+ ) {
+ val dozeAmount: Flow<Float> = interactor.dozeAmount
+ val burnInOffsets: Flow<BurnInOffsets> = interactor.burnInOffsets
+
+ // Padding between the fingerprint icon and its bounding box in pixels.
+ val padding: Flow<Int> =
+ interactor.scaleForResolution.map { scale ->
+ (context.resources.getDimensionPixelSize(R.dimen.lock_icon_padding) * scale)
+ .roundToInt()
+ }
+}
+
+@ExperimentalCoroutinesApi
+class BackgroundViewModel
+@Inject
+constructor(
+ val context: Context,
+ transitionInteractor: KeyguardTransitionInteractor,
+) :
+ UdfpsLockscreenViewModel(
+ context,
+ com.android.internal.R.attr.colorSurface,
+ com.android.internal.R.attr.materialColorPrimaryFixed,
+ transitionInteractor,
+ )
+
+data class TransitionViewModel(
+ val alpha: Float,
+ val scale: Float,
+ @ColorInt val color: Int,
+)
diff --git a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
index 34a6740..e064839 100644
--- a/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
+++ b/packages/SystemUI/src/com/android/systemui/lifecycle/RepeatWhenAttached.kt
@@ -167,9 +167,10 @@
registry.currentState = Lifecycle.State.DESTROYED
}
- override fun getLifecycle(): Lifecycle {
- return registry
- }
+ override val lifecycle: Lifecycle
+ get() {
+ return registry
+ }
private fun updateState() {
registry.currentState =
diff --git a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
index 3226865..d4b799f 100644
--- a/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/BouncerLogger.kt
@@ -18,6 +18,7 @@
import com.android.systemui.bouncer.shared.model.BouncerMessageModel
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.BouncerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
index fefa1b2..68cdfb6 100644
--- a/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/FaceAuthenticationLogger.kt
@@ -6,7 +6,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.FaceAuthLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
index 27301e9..150de26 100644
--- a/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/ScreenDecorationsLogger.kt
@@ -21,9 +21,9 @@
import android.graphics.RectF
import androidx.core.graphics.toRectF
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.ScreenDecorationsLog
import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
index 8d622ae..67a985e 100644
--- a/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/table/TableLogBuffer.kt
@@ -21,8 +21,8 @@
import com.android.systemui.Dumpable
import com.android.systemui.common.buffer.RingBuffer
import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.plugins.log.TableLogBufferBase
import com.android.systemui.util.time.SystemClock
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
index e2e269d..534241e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutLogger.kt
@@ -19,7 +19,7 @@
import android.media.session.PlaybackState
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaTimeoutListenerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
index 9e53d77..888b9c7 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/resume/ResumeMediaBrowserLogger.kt
@@ -19,7 +19,7 @@
import android.content.ComponentName
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaBrowserLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
index 30ee147..2883210 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/KeyguardMediaController.kt
@@ -128,6 +128,15 @@
var visibilityChangedListener: ((Boolean) -> Unit)? = null
+ /**
+ * Whether the doze wake up animation is delayed and we are currently waiting for it to start.
+ */
+ var isDozeWakeUpAnimationWaiting: Boolean = false
+ set(value) {
+ field = value
+ refreshMediaPosition()
+ }
+
/** single pane media container placed at the top of the notifications list */
var singlePaneContainer: MediaContainerView? = null
private set
@@ -221,7 +230,13 @@
// by the clock. This is not the case for single-line clock though.
// For single shade, we don't need to do it, because media is a child of NSSL, which already
// gets hidden on AOD.
- return !statusBarStateController.isDozing
+ // Media also has to be hidden when waking up from dozing, and the doze wake up animation is
+ // delayed and waiting to be started.
+ // This is to stay in sync with the delaying of the horizontal alignment of the rest of the
+ // keyguard container, that is also delayed until the "wait" is over.
+ // If we show media during this waiting period, the shade will still be centered, and using
+ // the entire width of the screen, and making media show fully stretched.
+ return !statusBarStateController.isDozing && !isDozeWakeUpAnimationWaiting
}
private fun showMediaPlayer() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
index 0ed2434..3dc0000 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselControllerLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaCarouselControllerLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
index c781b76..8f1595d 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaViewLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.MediaViewLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
index 318cd99..26a7d04 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputAdapter.java
@@ -20,6 +20,7 @@
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_NONE;
import static com.android.settingslib.media.MediaDevice.SelectionBehavior.SELECTION_BEHAVIOR_TRANSFER;
+import android.annotation.DrawableRes;
import android.content.Context;
import android.content.res.ColorStateList;
import android.graphics.drawable.AnimatedVectorDrawable;
@@ -181,27 +182,23 @@
mController.getSelectedMediaDevice(), device)));
boolean isHost = device.isHostForOngoingSession()
&& isActiveWithOngoingSession;
- if (isHost) {
+ if (isActiveWithOngoingSession) {
mCurrentActivePosition = position;
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
mSubTitleText.setText(device.getSubtextString());
updateTwoLineLayoutContentAlpha(DEVICE_CONNECTED_ALPHA);
- updateEndClickAreaAsSessionEditing(device);
+ updateEndClickAreaAsSessionEditing(device,
+ isHost ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
setTwoLineLayout(device, null /* title */, true /* bFocused */,
true /* showSeekBar */, false /* showProgressBar */,
true /* showSubtitle */, false /* showStatus */,
true /* showEndTouchArea */, false /* isFakeActive */);
initSeekbar(device, isCurrentSeekbarInvisible);
} else {
- if (isActiveWithOngoingSession) {
- //Selected device which has ongoing session, disable seekbar since we
- //only allow volume control on Host
+ if (currentlyConnected) {
mCurrentActivePosition = position;
- }
- boolean showSeekbar =
- (!device.hasOngoingSession() && currentlyConnected);
- if (showSeekbar) {
updateTitleIcon(R.drawable.media_output_icon_volume,
mController.getColorItemContent());
initSeekbar(device, isCurrentSeekbarInvisible);
@@ -222,10 +219,10 @@
updateClickActionBasedOnSelectionBehavior(device)
? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
setTwoLineLayout(device, currentlyConnected /* bFocused */,
- showSeekbar /* showSeekBar */,
+ currentlyConnected /* showSeekBar */,
false /* showProgressBar */, true /* showSubtitle */,
deviceStatusIcon != null /* showStatus */,
- isActiveWithOngoingSession /* isFakeActive */);
+ false /* isFakeActive */);
}
} else if (device.getState() == MediaDeviceState.STATE_CONNECTING_FAILED) {
setUpDeviceIcon(device);
@@ -267,25 +264,16 @@
setSingleLineLayout(getItemTitle(device));
} else if (device.hasOngoingSession()) {
mCurrentActivePosition = position;
- if (device.isHostForOngoingSession()) {
- updateTitleIcon(R.drawable.media_output_icon_volume,
- mController.getColorItemContent());
- updateEndClickAreaAsSessionEditing(device);
- mEndClickIcon.setVisibility(View.VISIBLE);
- setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
- false /* showProgressBar */, false /* showCheckBox */,
- true /* showEndTouchArea */);
- initSeekbar(device, isCurrentSeekbarInvisible);
- } else {
- updateDeviceStatusIcon(mContext.getDrawable(
- R.drawable.ic_sound_bars_anim));
- mStatusIcon.setVisibility(View.VISIBLE);
- updateSingleLineLayoutContentAlpha(
- updateClickActionBasedOnSelectionBehavior(device)
- ? DEVICE_CONNECTED_ALPHA : DEVICE_DISCONNECTED_ALPHA);
- setSingleLineLayout(getItemTitle(device));
- initFakeActiveDevice();
- }
+ updateTitleIcon(R.drawable.media_output_icon_volume,
+ mController.getColorItemContent());
+ updateEndClickAreaAsSessionEditing(device, device.isHostForOngoingSession()
+ ? R.drawable.media_output_status_edit_session
+ : R.drawable.ic_sound_bars_anim);
+ mEndClickIcon.setVisibility(View.VISIBLE);
+ setSingleLineLayout(getItemTitle(device), true /* showSeekBar */,
+ false /* showProgressBar */, false /* showCheckBox */,
+ true /* showEndTouchArea */);
+ initSeekbar(device, isCurrentSeekbarInvisible);
} else if (mController.isCurrentConnectedDeviceRemote()
&& !mController.getSelectableMediaDevice().isEmpty()) {
//If device is connected and there's other selectable devices, layout as
@@ -362,7 +350,7 @@
mStatusIcon.setAlpha(alphaValue);
}
- private void updateEndClickAreaAsSessionEditing(MediaDevice device) {
+ private void updateEndClickAreaAsSessionEditing(MediaDevice device, @DrawableRes int id) {
mEndClickIcon.setOnClickListener(null);
mEndTouchArea.setOnClickListener(null);
updateEndClickAreaColor(mController.getColorSeekbarProgress());
@@ -371,6 +359,11 @@
mEndClickIcon.setOnClickListener(
v -> mController.tryToLaunchInAppRoutingIntent(device.getId(), v));
mEndTouchArea.setOnClickListener(v -> mEndClickIcon.performClick());
+ Drawable drawable = mContext.getDrawable(id);
+ mEndClickIcon.setImageDrawable(drawable);
+ if (drawable instanceof AnimatedVectorDrawable) {
+ ((AnimatedVectorDrawable) drawable).start();
+ }
}
public void updateEndClickAreaColor(int color) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
index bbcf259..4171682 100644
--- a/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/muteawait/MediaMuteAwaitLogger.kt
@@ -3,7 +3,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.MediaMuteAwaitLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [MediaMuteAwaitConnectionManager]. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
index 66399d5..46c0132 100644
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesLogger.kt
@@ -3,7 +3,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.NearbyMediaDevicesLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [NearbyMediaDevicesManager]. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
index eeda1027..3c2226f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/common/MediaTttLoggerUtils.kt
@@ -17,7 +17,7 @@
package com.android.systemui.media.taptotransfer.common
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** A helper for logging media tap-to-transfer events. */
object MediaTttLoggerUtils {
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
index 206e5e3..503afd3 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderLogger.kt
@@ -20,7 +20,7 @@
import com.android.internal.logging.InstanceId
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.media.taptotransfer.common.MediaTttLoggerUtils
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
index 8d80990..07846b5 100644
--- a/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
+++ b/packages/SystemUI/src/com/android/systemui/model/SysUiState.java
@@ -114,7 +114,7 @@
pw.print(" mSysUiStateFlags="); pw.println(mFlags);
pw.println(" " + QuickStepContract.getSystemUiStateString(mFlags));
pw.print(" backGestureDisabled=");
- pw.println(QuickStepContract.isBackGestureDisabled(mFlags));
+ pw.println(QuickStepContract.isBackGestureDisabled(mFlags, false /* forTrackpad */));
pw.print(" assistantGestureDisabled=");
pw.println(QuickStepContract.isAssistantGestureDisabled(mFlags));
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 99c591f..8225c47 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -462,7 +462,7 @@
* @return Whether the IME is shown on top of the screen given the {@code vis} flag of
* {@link InputMethodService} and the keyguard states.
*/
- public boolean isImeShown(@InputMethodService.ImeWindowVisibility int vis) {
+ public boolean isImeShown(int vis) {
View shadeWindowView = mNotificationShadeWindowController.getWindowRootView();
boolean isKeyguardShowing = mKeyguardStateController.isShowing();
boolean imeVisibleOnShade = shadeWindowView != null && shadeWindowView.isAttachedToWindow()
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 5bae1cb..682335e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -66,7 +66,6 @@
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Region;
-import android.inputmethodservice.InputMethodService;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -1048,9 +1047,8 @@
// ----- CommandQueue Callbacks -----
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
if (displayId != mDisplayId) {
return;
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index cecf043..3b32313e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -338,9 +338,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition, boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
+ boolean showImeSwitcher) {
boolean imeShown = mNavBarHelper.isImeShown(vis);
if (!imeShown) {
// Count imperceptible changes as visible so we transition taskbar out quickly.
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 7b86d0a..a8af67a 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -1023,7 +1023,8 @@
boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
boolean isBackAllowedCommon = !mDisabledForQuickstep && mIsBackGestureAllowed
&& !mGestureBlockingActivityRunning
- && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
+ && !QuickStepContract.isBackGestureDisabled(mSysUiFlags,
+ mIsTrackpadThreeFingerSwipe)
&& !isTrackpadScroll(mIsTrackpadGestureFeaturesEnabled, ev);
if (mIsTrackpadThreeFingerSwipe) {
// Trackpad back gestures don't have zones, so we don't need to check if the down
@@ -1056,8 +1057,9 @@
+ " disp=%s, wl=%d, il=%d, wr=%d, ir=%d, excl=%s]",
curTime, curTimeStr, mAllowGesture, mIsTrackpadThreeFingerSwipe,
mIsOnLeftEdge, mDeferSetIsOnLeftEdge, mIsBackGestureAllowed,
- QuickStepContract.isBackGestureDisabled(mSysUiFlags), mDisabledForQuickstep,
- mGestureBlockingActivityRunning, mIsInPip, mDisplaySize,
+ QuickStepContract.isBackGestureDisabled(mSysUiFlags,
+ mIsTrackpadThreeFingerSwipe),
+ mDisabledForQuickstep, mGestureBlockingActivityRunning, mIsInPip, mDisplaySize,
mEdgeWidthLeft, mLeftInset, mEdgeWidthRight, mRightInset, mExcludeRegion));
} else if (mAllowGesture || mLogGesture) {
if (!mThresholdCrossed) {
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
index e56106d..f934346 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/logging/PrivacyLogger.kt
@@ -20,8 +20,8 @@
import android.permission.PermissionGroupUsage
import com.android.systemui.log.dagger.PrivacyLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogMessage
import com.android.systemui.privacy.PrivacyDialog
import com.android.systemui.privacy.PrivacyItem
import java.util.Locale
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
index ac6aabb..6563e42 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragmentDisableFlagsLogger.kt
@@ -2,7 +2,7 @@
import com.android.systemui.log.dagger.QSFragmentDisableLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
index 6265b3c..3432628 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileRequestDialog.kt
@@ -18,6 +18,7 @@
import android.content.Context
import android.graphics.drawable.Icon
+import android.view.ContextThemeWrapper
import android.view.LayoutInflater
import android.view.ViewGroup
import android.widget.TextView
@@ -66,7 +67,8 @@
}
private fun createTileView(tileData: TileData): QSTileView {
- val tile = QSTileViewImpl(context, QSIconViewImpl(context), true)
+ val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ val tile = QSTileViewImpl(themedContext, QSIconViewImpl(themedContext), true)
val state = QSTile.BooleanState().apply {
label = tileData.label
handlesLongClick = false
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
index 9c9ad33..3c53d77 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/binder/FooterActionsViewBinder.kt
@@ -244,8 +244,8 @@
val backgroundResource =
when (model.backgroundColor) {
- R.attr.offStateColor -> R.drawable.qs_footer_action_circle
- com.android.internal.R.attr.colorAccent -> R.drawable.qs_footer_action_circle_color
+ R.attr.shadeInactive -> R.drawable.qs_footer_action_circle
+ R.attr.shadeActive -> R.drawable.qs_footer_action_circle_color
else -> error("Unsupported icon background resource ${model.backgroundColor}")
}
buttonView.setBackgroundResource(backgroundResource)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
index b3596a2..32146b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/footer/ui/viewmodel/FooterActionsViewModel.kt
@@ -145,8 +145,12 @@
R.drawable.ic_settings,
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
),
- iconTint = null,
- backgroundColor = R.attr.offStateColor,
+ iconTint =
+ Utils.getColorAttrDefaultColor(
+ context,
+ R.attr.onShadeInactiveVariant,
+ ),
+ backgroundColor = R.attr.shadeInactive,
this::onSettingsButtonClicked,
)
@@ -162,9 +166,9 @@
iconTint =
Utils.getColorAttrDefaultColor(
context,
- com.android.internal.R.attr.textColorOnAccent,
+ R.attr.onShadeActive,
),
- backgroundColor = com.android.internal.R.attr.colorAccent,
+ backgroundColor = R.attr.shadeActive,
this::onPowerButtonClicked,
)
} else {
@@ -264,7 +268,7 @@
),
),
iconTint = null,
- backgroundColor = R.attr.offStateColor,
+ backgroundColor = R.attr.shadeInactive,
onClick = this::onUserSwitcherClicked,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
index c00a81c..39745c8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/logging/QSLogger.kt
@@ -24,9 +24,9 @@
import com.android.systemui.log.ConstantStringsLogger
import com.android.systemui.log.ConstantStringsLoggerImpl
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.log.dagger.QSConfigLog
import com.android.systemui.log.dagger.QSLog
import com.android.systemui.plugins.qs.QSTile
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
index 498f403..6e7e099 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepository.kt
@@ -51,12 +51,26 @@
@Inject
constructor(
@Application private val applicationContext: Context,
- private val packageManager: PackageManager,
@Background private val backgroundDispatcher: CoroutineDispatcher,
) : InstalledTilesComponentRepository {
- override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> =
- conflatedCallbackFlow {
+ override fun getInstalledTilesComponents(userId: Int): Flow<Set<ComponentName>> {
+ /*
+ * In order to query [PackageManager] for different users, this implementation will call
+ * [Context.createContextAsUser] and retrieve the [PackageManager] from that context.
+ */
+ val packageManager =
+ if (applicationContext.userId == userId) {
+ applicationContext.packageManager
+ } else {
+ applicationContext
+ .createContextAsUser(
+ UserHandle.of(userId),
+ /* flags */ 0,
+ )
+ .packageManager
+ }
+ return conflatedCallbackFlow {
val receiver =
object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
@@ -74,12 +88,13 @@
awaitClose { applicationContext.unregisterReceiver(receiver) }
}
.onStart { emit(Unit) }
- .map { reloadComponents(userId) }
+ .map { reloadComponents(userId, packageManager) }
.distinctUntilChanged()
.flowOn(backgroundDispatcher)
+ }
@WorkerThread
- private fun reloadComponents(userId: Int): Set<ComponentName> {
+ private fun reloadComponents(userId: Int, packageManager: PackageManager): Set<ComponentName> {
return packageManager
.queryIntentServicesAsUser(INTENT, FLAGS, userId)
.mapNotNull { it.serviceInfo }
diff --git a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
index d400faa..573cb715 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/pipeline/shared/logging/QSPipelineLogger.kt
@@ -18,7 +18,7 @@
import android.annotation.UserIdInt
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.qs.pipeline.dagger.QSAutoAddLog
import com.android.systemui.qs.pipeline.dagger.QSTileListLog
import com.android.systemui.qs.pipeline.shared.TileSpec
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index e541681..7e45491 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -248,13 +248,11 @@
*/
private static int getIconColorForState(Context context, QSTile.State state) {
if (state.disabledByPolicy || state.state == Tile.STATE_UNAVAILABLE) {
- return Utils.getColorAttrDefaultColor(
- context, com.android.internal.R.attr.textColorTertiary);
+ return Utils.getColorAttrDefaultColor(context, R.attr.outline);
} else if (state.state == Tile.STATE_INACTIVE) {
- return Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary);
+ return Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant);
} else if (state.state == Tile.STATE_ACTIVE) {
- return Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.textColorOnAccent);
+ return Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive);
} else {
Log.e("QSIconView", "Invalid state " + state);
return 0;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
index 2a9e7d0..1ca2a96 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java
@@ -330,7 +330,9 @@
final int eventId = mClickEventId++;
mQSLogger.logTileLongClick(mTileSpec, mStatusBarStateController.getState(), mState.state,
eventId);
- mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
+ if (!mFalsingManager.isFalseLongTap(FalsingManager.LOW_PENALTY)) {
+ mHandler.obtainMessage(H.LONG_CLICK, eventId, 0, view).sendToTarget();
+ }
}
public LogMaker populate(LogMaker logMaker) {
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 b806683..d81e4c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -29,6 +29,7 @@
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
+import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@@ -92,24 +93,21 @@
updateHeight()
}
- private val colorActive = Utils.getColorAttrDefaultColor(context,
- com.android.internal.R.attr.colorAccentPrimary)
- private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.offStateColor)
- private val colorUnavailable = Utils.applyAlpha(UNAVAILABLE_ALPHA, colorInactive)
+ private val colorActive = Utils.getColorAttrDefaultColor(context, R.attr.shadeActive)
+ private val colorInactive = Utils.getColorAttrDefaultColor(context, R.attr.shadeInactive)
+ private val colorUnavailable = Utils.getColorAttrDefaultColor(context, R.attr.shadeDisabled)
- private val colorLabelActive =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorOnAccent)
- private val colorLabelInactive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorPrimary)
+ private val colorLabelActive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeActive)
+ private val colorLabelInactive = Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactive)
private val colorLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
+ Utils.getColorAttrDefaultColor(context, R.attr.outline)
private val colorSecondaryLabelActive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondaryInverse)
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeActiveVariant)
private val colorSecondaryLabelInactive =
- Utils.getColorAttrDefaultColor(context, android.R.attr.textColorSecondary)
+ Utils.getColorAttrDefaultColor(context, R.attr.onShadeInactiveVariant)
private val colorSecondaryLabelUnavailable =
- Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.textColorTertiary)
+ Utils.getColorAttrDefaultColor(context, R.attr.outline)
private lateinit var label: TextView
protected lateinit var secondaryLabel: TextView
@@ -151,6 +149,11 @@
private val locInScreen = IntArray(2)
init {
+ val typedValue = TypedValue()
+ if (!getContext().theme.resolveAttribute(R.attr.isQsTheme, typedValue, true)) {
+ throw IllegalStateException("QSViewImpl must be inflated with a theme that contains " +
+ "Theme.SystemUI.QuickSettings")
+ }
setId(generateViewId())
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL or Gravity.START
diff --git a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
index 9b5898f..2ad5429 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/ui/view/SceneWindowRootView.kt
@@ -52,9 +52,8 @@
setOnBackInvokedDispatcher(viewRootImpl.onBackInvokedDispatcher)
}
- override fun getLifecycle(): Lifecycle {
- return this@repeatWhenAttached.lifecycle
- }
+ override val lifecycle: Lifecycle =
+ this@repeatWhenAttached.lifecycle
}
)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 2ef9e07..d97db3b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -117,6 +117,7 @@
import com.android.systemui.bouncer.shared.constants.KeyguardBouncerConstants;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.DisplayId;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeLog;
@@ -208,7 +209,6 @@
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
import com.android.systemui.statusbar.phone.TapAgainViewController;
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardQsUserSwitchController;
@@ -238,7 +238,7 @@
import kotlinx.coroutines.CoroutineDispatcher;
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public final class NotificationPanelViewController implements ShadeSurface, Dumpable {
public static final String TAG = NotificationPanelView.class.getSimpleName();
@@ -1175,6 +1175,7 @@
mKeyguardStatusViewComponentFactory.build(keyguardStatusView);
mKeyguardStatusViewController = statusViewComponent.getKeyguardStatusViewController();
mKeyguardStatusViewController.init();
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
updateClockAppearance();
if (mKeyguardUserSwitcherController != null) {
@@ -1227,6 +1228,7 @@
private void onSplitShadeEnabledChanged() {
mShadeLog.logSplitShadeChanged(mSplitShadeEnabled);
+ mKeyguardStatusViewController.setSplitShadeEnabled(mSplitShadeEnabled);
// Reset any left over overscroll state. It is a rare corner case but can happen.
mQsController.setOverScrollAmount(0);
mScrimController.setNotificationsOverScrollAmount(0);
@@ -1407,11 +1409,13 @@
mKeyguardBottomArea = keyguardBottomArea;
}
- void setOpenCloseListener(OpenCloseListener openCloseListener) {
+ @Override
+ public void setOpenCloseListener(OpenCloseListener openCloseListener) {
mOpenCloseListener = openCloseListener;
}
- void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
+ @Override
+ public void setTrackingStartedListener(TrackingStartedListener trackingStartedListener) {
mTrackingStartedListener = trackingStartedListener;
}
@@ -1623,6 +1627,7 @@
mWillPlayDelayedDozeAmountAnimation = willPlay;
mWakeUpCoordinator.logDelayingClockWakeUpAnimation(willPlay);
+ mKeyguardMediaController.setDozeWakeUpAnimationWaiting(willPlay);
// Once changing this value, see if we should move the clock.
positionClockAndNotifications();
@@ -3378,11 +3383,13 @@
ViewGroupFadeHelper.reset(mView);
}
- void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void addOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().addOnGlobalLayoutListener(listener);
}
- void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
+ @Override
+ public void removeOnGlobalLayoutListener(ViewTreeObserver.OnGlobalLayoutListener listener) {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
@@ -3565,6 +3572,7 @@
}
private void endMotionEvent(MotionEvent event, float x, float y, boolean forceCancel) {
+ mShadeLog.logEndMotionEvent("endMotionEvent called", forceCancel, false);
mTrackingPointer = -1;
mAmbientState.setSwipingUp(false);
if ((mTracking && mTouchSlopExceeded) || Math.abs(x - mInitialExpandX) > mTouchSlop
@@ -3586,15 +3594,19 @@
} else if (event.getActionMasked() == MotionEvent.ACTION_CANCEL || forceCancel) {
if (onKeyguard) {
expand = true;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel while on keyguard",
+ forceCancel, expand);
} else if (mCentralSurfaces.isBouncerShowingOverDream()) {
expand = false;
} else {
// If we get a cancel, put the shade back to the state it was in when the
// gesture started
expand = !mPanelClosedOnDown;
+ mShadeLog.logEndMotionEvent("endMotionEvent: cancel", forceCancel, expand);
}
} else {
expand = flingExpands(vel, vectorVel, x, y);
+ mShadeLog.logEndMotionEvent("endMotionEvent: flingExpands", forceCancel, expand);
}
mDozeLog.traceFling(
@@ -3847,8 +3859,8 @@
return !isFullyCollapsed() && !mTracking && !mClosing;
}
- /** Collapses the shade instantly without animation. */
- void instantCollapse() {
+ @Override
+ public void instantCollapse() {
abortAnimations();
setExpandedFraction(0f);
if (mExpanding) {
@@ -4021,8 +4033,8 @@
mFixedDuration = NO_FIXED_DURATION;
}
- /** */
- boolean postToView(Runnable action) {
+ @Override
+ public boolean postToView(Runnable action) {
return mView.post(action);
}
@@ -4731,6 +4743,8 @@
mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("intercept down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
@@ -4948,6 +4962,8 @@
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
+ mShadeLog.logPanelClosedOnDown("handle down touch", mPanelClosedOnDown,
+ mExpandedFraction);
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
@@ -5113,18 +5129,5 @@
return super.performAccessibilityAction(host, action, args);
}
}
-
- /** Listens for when touch tracking begins. */
- interface TrackingStartedListener {
- void onTrackingStarted();
- }
-
- /** Listens for when shade begins opening of finishes closing. */
- interface OpenCloseListener {
- /** Called when the shade finishes closing. */
- void onClosingFinished();
- /** Called when the shade starts opening. */
- void onOpenStarted();
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
index 1361c9f..025c4611 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/QuickSettingsController.java
@@ -68,6 +68,7 @@
import com.android.systemui.R;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentHostManager;
@@ -98,7 +99,6 @@
import com.android.systemui.statusbar.phone.ScrimController;
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
import com.android.systemui.statusbar.phone.StatusBarTouchableRegionManager;
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent;
import com.android.systemui.statusbar.policy.CastController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.LargeScreenUtils;
@@ -113,7 +113,7 @@
/** Handles QuickSettings touch handling, expansion and animation state
* TODO (b/264460656) make this dumpable
*/
-@CentralSurfacesComponent.CentralSurfacesScope
+@SysUISingleton
public class QuickSettingsController implements Dumpable {
public static final String TAG = "QuickSettingsController";
@@ -1220,14 +1220,15 @@
if (mIsFullWidth) {
clipStatusView = qsVisible;
float screenCornerRadius =
- !mSplitShadeEnabled || mRecordingController.isRecording()
- || mCastController.hasConnectedCastDevice()
+ mRecordingController.isRecording() || mCastController.hasConnectedCastDevice()
? 0 : mScreenCornerRadius;
radius = (int) MathUtils.lerp(screenCornerRadius, mScrimCornerRadius,
Math.min(top / (float) mScrimCornerRadius, 1f));
- float bottomRadius = mExpanded ? screenCornerRadius :
- calculateBottomCornerRadius(screenCornerRadius);
+ float bottomRadius = mSplitShadeEnabled ? screenCornerRadius : 0;
+ if (!mExpanded) {
+ bottomRadius = calculateBottomCornerRadius(bottomRadius);
+ }
mScrimController.setNotificationBottomRadius(bottomRadius);
}
if (isQsFragmentCreated()) {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
index 9ed0e9a..317d885 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeController.java
@@ -165,8 +165,7 @@
NotificationShadeWindowViewController notificationShadeWindowViewController);
/** */
- void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController);
+ void setShadeViewController(ShadeViewController shadeViewController);
/** Listens for shade visibility changes. */
interface ShadeVisibilityListener {
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index c9338b3..b92afac 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -70,7 +70,8 @@
private boolean mExpandedVisible;
- private NotificationPanelViewController mNotificationPanelViewController;
+ // TODO(b/237661616): Rename this variable to mShadeViewController.
+ private ShadeViewController mNotificationPanelViewController;
private NotificationPresenter mPresenter;
private NotificationShadeWindowViewController mNotificationShadeWindowViewController;
private ShadeVisibilityListener mShadeVisibilityListener;
@@ -426,12 +427,11 @@
}
@Override
- public void setNotificationPanelViewController(
- NotificationPanelViewController notificationPanelViewController) {
- mNotificationPanelViewController = notificationPanelViewController;
+ public void setShadeViewController(ShadeViewController shadeViewController) {
+ mNotificationPanelViewController = shadeViewController;
mNotificationPanelViewController.setTrackingStartedListener(this::runPostCollapseRunnables);
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {
ShadeControllerImpl.this.onClosingFinished();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index 3af75ce..8789a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -195,7 +195,9 @@
set(value) {
if (visible && field != value) {
field = value
+ iconContainer.setQsExpansionTransitioning(value > 0f && value < 1.0f)
updatePosition()
+ updateIgnoredSlots()
}
}
@@ -216,6 +218,8 @@
view.onApplyWindowInsets(insets)
}
+ private var singleCarrier = false
+
private val demoModeReceiver =
object : DemoMode {
override fun demoCommands() = listOf(DemoMode.COMMAND_CLOCK)
@@ -479,17 +483,20 @@
private fun updateListeners() {
mShadeCarrierGroupController.setListening(visible)
if (visible) {
- updateSingleCarrier(mShadeCarrierGroupController.isSingleCarrier)
+ singleCarrier = mShadeCarrierGroupController.isSingleCarrier
+ updateIgnoredSlots()
mShadeCarrierGroupController.setOnSingleCarrierChangedListener {
- updateSingleCarrier(it)
+ singleCarrier = it
+ updateIgnoredSlots()
}
} else {
mShadeCarrierGroupController.setOnSingleCarrierChangedListener(null)
}
}
- private fun updateSingleCarrier(singleCarrier: Boolean) {
- if (singleCarrier) {
+ private fun updateIgnoredSlots() {
+ // switching from QQS to QS state halfway through the transition
+ if (singleCarrier || qsExpandedFraction < 0.5) {
iconContainer.removeIgnoredSlots(carrierIconSlots)
} else {
iconContainer.addIgnoredSlots(carrierIconSlots)
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 2da8d5f4..1c30bdd 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -19,7 +19,7 @@
import android.view.MotionEvent
import com.android.systemui.log.dagger.ShadeLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.shade.ShadeViewController.Companion.FLING_COLLAPSE
import com.android.systemui.shade.ShadeViewController.Companion.FLING_EXPAND
import com.android.systemui.shade.ShadeViewController.Companion.FLING_HIDE
@@ -90,7 +90,7 @@
double1 = event.y.toDouble()
},
{
- "$str1\neventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
+ "$str1: eventTime=$long1,downTime=$long2,y=$double1,action=$int1,class=$int2"
}
)
}
@@ -280,6 +280,42 @@
)
}
+ fun logEndMotionEvent(
+ msg: String,
+ forceCancel: Boolean,
+ expand: Boolean,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = forceCancel
+ bool2 = expand
+ },
+ { "$str1; force=$bool1; expand=$bool2" }
+ )
+ }
+
+ fun logPanelClosedOnDown(
+ msg: String,
+ panelClosedOnDown: Boolean,
+ expandFraction: Float,
+ )
+ {
+ buffer.log(
+ TAG,
+ LogLevel.VERBOSE,
+ {
+ str1 = msg
+ bool1 = panelClosedOnDown
+ double1 = expandFraction.toDouble()
+ },
+ { "$str1; mPanelClosedOnDown=$bool1; mExpandedFraction=$double1" }
+ )
+ }
+
fun flingQs(flingType: Int, isClick: Boolean) {
buffer.log(
TAG,
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
index a2b9351..8ae9e5e 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeModule.kt
@@ -49,6 +49,7 @@
import com.android.systemui.statusbar.notification.row.dagger.NotificationShelfComponent
import com.android.systemui.statusbar.notification.shelf.ui.viewbinder.NotificationShelfViewBinderWrapperControllerImpl
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout
+import com.android.systemui.statusbar.phone.KeyguardBottomAreaView
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.phone.TapAgainView
import com.android.systemui.statusbar.policy.BatteryController
@@ -71,6 +72,12 @@
@ClassKey(AuthRippleController::class)
abstract fun bindAuthRippleController(controller: AuthRippleController): CoreStartable
+ @Binds
+ @SysUISingleton
+ abstract fun bindsShadeViewController(
+ notificationPanelViewController: NotificationPanelViewController
+ ): ShadeViewController
+
companion object {
const val SHADE_HEADER = "large_screen_shade_header"
@@ -165,6 +172,20 @@
return notificationShadeWindowView.findViewById(R.id.notification_panel)
}
+ /**
+ * Constructs a new, unattached [KeyguardBottomAreaView].
+ *
+ * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
+ */
+ @Provides
+ fun providesKeyguardBottomAreaView(
+ npv: NotificationPanelView,
+ layoutInflater: LayoutInflater,
+ ): KeyguardBottomAreaView {
+ return layoutInflater.inflate(R.layout.keyguard_bottom_area, npv, false)
+ as KeyguardBottomAreaView
+ }
+
@Provides
@SysUISingleton
fun providesLightRevealScrim(
@@ -194,9 +215,15 @@
@Provides
@SysUISingleton
fun providesLockIconView(
- notificationShadeWindowView: NotificationShadeWindowView,
+ keyguardRootView: KeyguardRootView,
+ notificationPanelView: NotificationPanelView,
+ featureFlags: FeatureFlags
): LockIconView {
- return notificationShadeWindowView.findViewById(R.id.lock_icon_view)
+ if (featureFlags.isEnabled(Flags.MIGRATE_LOCK_ICON)) {
+ return keyguardRootView.findViewById(R.id.lock_icon_view)
+ } else {
+ return notificationPanelView.findViewById(R.id.lock_icon_view)
+ }
}
// TODO(b/277762009): Only allow this view's controller to inject the view. See above.
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
index 3d9fcf9..9aa5eb0 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeViewController.kt
@@ -17,6 +17,7 @@
import android.view.MotionEvent
import android.view.ViewGroup
+import android.view.ViewTreeObserver
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.statusbar.RemoteInputController
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
@@ -77,6 +78,9 @@
/** Collapses the shade with an animation duration in milliseconds. */
fun collapseWithDuration(animationDuration: Int)
+ /** Collapses the shade instantly without animation. */
+ fun instantCollapse()
+
/**
* Animate QS collapse by flinging it. If QS is expanded, it will collapse into QQS and stop. If
* in split shade, it will collapse the whole shade.
@@ -100,6 +104,9 @@
/** Returns whether the shade's top level view is enabled. */
val isViewEnabled: Boolean
+ /** Sets a listener to be notified when the shade starts opening or finishes closing. */
+ fun setOpenCloseListener(openCloseListener: OpenCloseListener)
+
/** Returns whether status bar icons should be hidden when the shade is expanded. */
fun shouldHideStatusBarIconsWhenExpanded(): Boolean
@@ -109,6 +116,9 @@
*/
fun blockExpansionForCurrentTouch()
+ /** Sets a listener to be notified when touch tracking begins. */
+ fun setTrackingStartedListener(trackingStartedListener: TrackingStartedListener)
+
/**
* Disables the shade header.
*
@@ -178,6 +188,15 @@
/** Ensures that the touchable region is updated. */
fun updateTouchableRegion()
+ /** Adds a global layout listener. */
+ fun addOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Removes a global layout listener. */
+ fun removeOnGlobalLayoutListener(listener: ViewTreeObserver.OnGlobalLayoutListener)
+
+ /** Posts the given runnable to the view. */
+ fun postToView(action: Runnable): Boolean
+
// ******* Begin Keyguard Section *********
/** Animate to expanded shade after a delay in ms. Used for lockscreen to shade transition. */
fun transitionToExpandedShade(delay: Long)
@@ -337,3 +356,17 @@
/** Return the fraction of the shade that's expanded, when in lockscreen. */
val lockscreenShadeDragProgress: Float
}
+
+/** Listens for when touch tracking begins. */
+interface TrackingStartedListener {
+ fun onTrackingStarted()
+}
+
+/** Listens for when shade begins opening or finishes closing. */
+interface OpenCloseListener {
+ /** Called when the shade finishes closing. */
+ fun onClosingFinished()
+
+ /** Called when the shade starts opening. */
+ fun onOpenStarted()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
index d06634b..51a27cf 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeWindowLogger.kt
@@ -21,9 +21,9 @@
import com.android.systemui.log.ConstantStringsLogger
import com.android.systemui.log.ConstantStringsLoggerImpl
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogMessage
import javax.inject.Inject
private const val TAG = "systemui.shadewindow"
diff --git a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
index eceda84..6fde84a 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/domain/interactor/ShadeInteractor.kt
@@ -17,38 +17,52 @@
package com.android.systemui.shade.domain.interactor
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.statusbar.disableflags.data.repository.DisableFlagsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
import com.android.systemui.statusbar.policy.DeviceProvisionedController
import com.android.systemui.user.domain.interactor.UserInteractor
import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.map
+import kotlinx.coroutines.flow.stateIn
/** Business logic for shade interactions. */
@SysUISingleton
class ShadeInteractor
@Inject
constructor(
+ @Application scope: CoroutineScope,
disableFlagsRepository: DisableFlagsRepository,
keyguardRepository: KeyguardRepository,
userSetupRepository: UserSetupRepository,
deviceProvisionedController: DeviceProvisionedController,
userInteractor: UserInteractor,
) {
+ /** Emits true if the shade is currently allowed and false otherwise. */
+ val isShadeEnabled: StateFlow<Boolean> =
+ disableFlagsRepository.disableFlags
+ .map { it.isShadeEnabled() }
+ .stateIn(scope, SharingStarted.Eagerly, initialValue = false)
+
/** Emits true if the shade can be expanded from QQS to QS and false otherwise. */
val isExpandToQsEnabled: Flow<Boolean> =
combine(
disableFlagsRepository.disableFlags,
+ isShadeEnabled,
keyguardRepository.isDozing,
userSetupRepository.isUserSetupFlow,
- ) { disableFlags, isDozing, isUserSetup ->
+ ) { disableFlags, isShadeEnabled, isDozing, isUserSetup ->
deviceProvisionedController.isDeviceProvisioned &&
// Disallow QS during setup if it's a simple user switcher. (The user intends to
// use the lock screen user switcher, QS is not needed.)
(isUserSetup || !userInteractor.isSimpleUserSwitcher) &&
- disableFlags.isShadeEnabled() &&
+ isShadeEnabled &&
disableFlags.isQuickSettingsEnabled() &&
!isDozing
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
index e008ec0..d3c19b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/ActionClickLogger.kt
@@ -19,7 +19,7 @@
import android.app.PendingIntent
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 6c2c0cf..a532195 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -19,6 +19,7 @@
import static android.app.StatusBarManager.DISABLE2_NONE;
import static android.app.StatusBarManager.DISABLE_NONE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.INVALID_DISPLAY;
import android.annotation.Nullable;
@@ -36,7 +37,7 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
+import android.inputmethodservice.InputMethodService.BackDispositionMode;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.os.Binder;
@@ -225,10 +226,8 @@
* @param backDisposition Disposition mode of back button. It should be one of below flags:
* @param showImeSwitcher {@code true} to show IME switch button.
*/
- default void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher) { }
+ default void setImeWindowStatus(int displayId, IBinder token, int vis,
+ @BackDispositionMode int backDisposition, boolean showImeSwitcher) { }
default void showRecentApps(boolean triggeredFromAltTab) { }
default void hideRecentApps(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) { }
default void toggleTaskbar() { }
@@ -679,9 +678,7 @@
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
synchronized (mLock) {
mHandler.removeMessages(MSG_SHOW_IME_BUTTON);
@@ -1095,9 +1092,7 @@
}
}
- private void handleShowImeButton(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ private void handleShowImeButton(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
if (displayId == INVALID_DISPLAY) return;
@@ -1117,7 +1112,7 @@
private void sendImeInvisibleStatusForPrevNavBar() {
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).setImeWindowStatus(mLastUpdatedImeDisplayId,
- null /* token */, 0 /* vis */, BACK_DISPOSITION_DEFAULT,
+ null /* token */, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT,
false /* showImeSwitcher */);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
index 3918144..ec66e99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyboardShortcutListSearch.java
@@ -557,19 +557,7 @@
new ShortcutKeyGroupMultiMappingInfo(
context.getString(R.string.group_system_access_google_assistant),
Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON))),
- /* Lock screen: Meta + L */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_lock_screen),
- Arrays.asList(
- Pair.create(KeyEvent.KEYCODE_L, KeyEvent.META_META_ON))),
- /* Pull up Notes app for quick memo: Meta + Ctrl + N */
- new ShortcutKeyGroupMultiMappingInfo(
- context.getString(R.string.group_system_quick_memo),
- Arrays.asList(
- Pair.create(
- KeyEvent.KEYCODE_N,
- KeyEvent.META_META_ON | KeyEvent.META_CTRL_ON)))
+ Pair.create(KeyEvent.KEYCODE_A, KeyEvent.META_META_ON)))
);
for (ShortcutKeyGroupMultiMappingInfo info : infoList) {
systemGroup.addItem(info.getShortcutMultiMappingInfo());
@@ -611,21 +599,12 @@
new ArrayList<>());
// System multitasking shortcuts:
- // Enter Split screen with current app to RHS: Meta + Ctrl + Right arrow
- // Enter Split screen with current app to LHS: Meta + Ctrl + Left arrow
// Switch from Split screen to full screen: Meta + Ctrl + Up arrow
- // During Split screen: replace an app from one to another: Meta + Ctrl + Down arrow
String[] shortcutLabels = {
- context.getString(R.string.system_multitasking_rhs),
- context.getString(R.string.system_multitasking_lhs),
context.getString(R.string.system_multitasking_full_screen),
- context.getString(R.string.system_multitasking_replace)
};
int[] keyCodes = {
- KeyEvent.KEYCODE_DPAD_RIGHT,
- KeyEvent.KEYCODE_DPAD_LEFT,
KeyEvent.KEYCODE_DPAD_UP,
- KeyEvent.KEYCODE_DPAD_DOWN
};
for (int i = 0; i < shortcutLabels.length; i++) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
index 9692482..42ebaa3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
@@ -41,7 +41,7 @@
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_TRUST;
import static com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController.INDICATION_TYPE_USER_LOCKED;
import static com.android.systemui.keyguard.ScreenLifecycle.SCREEN_ON;
-import static com.android.systemui.log.LogLevel.ERROR;
+import static com.android.systemui.log.core.LogLevel.ERROR;
import static com.android.systemui.plugins.FalsingManager.LOW_PENALTY;
import android.app.AlarmManager;
@@ -97,7 +97,7 @@
import com.android.systemui.keyguard.KeyguardIndicationRotateTextViewController;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.util.IndicationHelper;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index e2d2ac0..4710574 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -32,6 +32,7 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.ShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
@@ -76,6 +77,7 @@
dumpManager: DumpManager,
qsTransitionControllerFactory: LockscreenShadeQsTransitionController.Factory,
private val shadeRepository: ShadeRepository,
+ private val shadeInteractor: ShadeInteractor,
private val powerInteractor: PowerInteractor,
) : Dumpable {
private var pulseHeight: Float = 0f
@@ -558,7 +560,7 @@
animationHandler: ((Long) -> Unit)? = null,
cancelAction: Runnable? = null
) {
- if (centralSurfaces.isShadeDisabled) {
+ if (!shadeInteractor.isShadeEnabled.value) {
cancelAction?.run()
logger.logShadeDisabledOnGoToLockedShade()
return
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
new file mode 100644
index 0000000..de369c3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/CommandParser.kt
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+/**
+ * [CommandParser] defines the collection of tokens which can be parsed from an incoming command
+ * list, and parses them into their respective containers. Supported tokens are of the following
+ * forms:
+ * ```
+ * Flag: boolean value, false by default. always optional.
+ * Param: named parameter, taking N args all of a given type. Currently only single arg parameters
+ * are supported.
+ * SubCommand: named command created by adding a command to a parent. Supports all fields above, but
+ * not other subcommands.
+ * ```
+ *
+ * Tokens are added via the factory methods for each token type. They can be made `required` by
+ * calling the [require] method for the appropriate type, as follows:
+ * ```
+ * val requiredParam = parser.require(parser.param(...))
+ * ```
+ *
+ * The reason for having an explicit require is so that generic type arguments can be handled
+ * properly. See [SingleArgParam] and [SingleArgParamOptional] for the difference between an
+ * optional parameter and a required one.
+ *
+ * Typical usage of a required parameter, however, will occur within the context of a
+ * [ParseableCommand], which defines a convenience `require()` method:
+ * ```
+ * class MyCommand : ParseableCommand {
+ * val requiredParam = param(...).require()
+ * }
+ * ```
+ *
+ * This parser defines two modes of parsing, both of which validate for required parameters.
+ * 1. [parse] is a top-level parsing method. This parser will walk the given arg list and populate
+ * all of the delegate classes based on their type. It will handle SubCommands, and after parsing
+ * will check for any required-but-missing SubCommands or Params.
+ *
+ * **This method requires that every received token is represented in its grammar.**
+ * 2. [parseAsSubCommand] is a second-level parsing method suitable for any [SubCommand]. This
+ * method will handle _only_ flags and params. It will return parsing control to its parent
+ * parser on the first unknown token rather than throwing.
+ */
+class CommandParser {
+ private val _flags = mutableListOf<Flag>()
+ val flags: List<Flag> = _flags
+ private val _params = mutableListOf<Param>()
+ val params: List<Param> = _params
+ private val _subCommands = mutableListOf<SubCommand>()
+ val subCommands: List<SubCommand> = _subCommands
+
+ private val tokenSet = mutableSetOf<String>()
+
+ /**
+ * Parse the arg list into the fields defined in the containing class.
+ *
+ * @return true if all required fields are present after parsing
+ * @throws ArgParseError on any failure to process args
+ */
+ fun parse(args: List<String>): Boolean {
+ if (args.isEmpty()) {
+ return false
+ }
+
+ val iterator = args.listIterator()
+ var tokenHandled: Boolean
+ while (iterator.hasNext()) {
+ val token = iterator.next()
+ tokenHandled = false
+
+ flags
+ .find { it.matches(token) }
+ ?.let {
+ it.inner = true
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ params
+ .find { it.matches(token) }
+ ?.let {
+ it.parseArgsFromIter(iterator)
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ subCommands
+ .find { it.matches(token) }
+ ?.let {
+ it.parseSubCommandArgs(iterator)
+ tokenHandled = true
+ }
+
+ if (!tokenHandled) {
+ throw ArgParseError("Unknown token: $token")
+ }
+ }
+
+ return validateRequiredParams()
+ }
+
+ /**
+ * Parse a subset of the commands that came in from the top-level [parse] method, for the
+ * subcommand that this parser represents. Note that subcommands may not contain other
+ * subcommands. But they may contain flags and params.
+ *
+ * @return true if all required fields are present after parsing
+ * @throws ArgParseError on any failure to process args
+ */
+ fun parseAsSubCommand(iter: ListIterator<String>): Boolean {
+ // arg[-1] is our subcommand name, so the rest of the args are either for this
+ // subcommand, OR for the top-level command to handle. Therefore, we bail on the first
+ // failure, but still check our own required params
+
+ // The mere presence of a subcommand (similar to a flag) is a valid subcommand
+ if (flags.isEmpty() && params.isEmpty()) {
+ return validateRequiredParams()
+ }
+
+ var tokenHandled: Boolean
+ while (iter.hasNext()) {
+ val token = iter.next()
+ tokenHandled = false
+
+ flags
+ .find { it.matches(token) }
+ ?.let {
+ it.inner = true
+ tokenHandled = true
+ }
+
+ if (tokenHandled) continue
+
+ params
+ .find { it.matches(token) }
+ ?.let {
+ it.parseArgsFromIter(iter)
+ tokenHandled = true
+ }
+
+ if (!tokenHandled) {
+ // Move the cursor position backwards since we've arrived at a token
+ // that we don't own
+ iter.previous()
+ break
+ }
+ }
+
+ return validateRequiredParams()
+ }
+
+ /**
+ * If [parse] or [parseAsSubCommand] does not produce a valid result, generate a list of errors
+ * based on missing elements
+ */
+ fun generateValidationErrorMessages(): List<String> {
+ val missingElements = mutableListOf<String>()
+
+ if (unhandledParams.isNotEmpty()) {
+ val names = unhandledParams.map { it.longName }
+ missingElements.add("No values passed for required params: $names")
+ }
+
+ if (unhandledSubCmds.isNotEmpty()) {
+ missingElements.addAll(unhandledSubCmds.map { it.longName })
+ val names = unhandledSubCmds.map { it.shortName }
+ missingElements.add("No values passed for required sub-commands: $names")
+ }
+
+ return missingElements
+ }
+
+ /** Check for any missing, required params, or any invalid subcommands */
+ private fun validateRequiredParams(): Boolean =
+ unhandledParams.isEmpty() && unhandledSubCmds.isEmpty() && unvalidatedSubCmds.isEmpty()
+
+ // If any required param (aka non-optional) hasn't handled a field, then return false
+ private val unhandledParams: List<Param>
+ get() = params.filter { (it is SingleArgParam<*>) && !it.handled }
+
+ private val unhandledSubCmds: List<SubCommand>
+ get() = subCommands.filter { (it is RequiredSubCommand<*> && !it.handled) }
+
+ private val unvalidatedSubCmds: List<SubCommand>
+ get() = subCommands.filter { !it.validationStatus }
+
+ private fun checkCliNames(short: String?, long: String): String? {
+ if (short != null && tokenSet.contains(short)) {
+ return short
+ }
+
+ if (tokenSet.contains(long)) {
+ return long
+ }
+
+ return null
+ }
+
+ private fun subCommandContainsSubCommands(cmd: ParseableCommand): Boolean =
+ cmd.parser.subCommands.isNotEmpty()
+
+ private fun registerNames(short: String?, long: String) {
+ if (short != null) {
+ tokenSet.add(short)
+ }
+ tokenSet.add(long)
+ }
+
+ /**
+ * Turns a [SingleArgParamOptional]<T> into a [SingleArgParam] by converting the [T?] into [T]
+ *
+ * @return a [SingleArgParam] property delegate
+ */
+ fun <T : Any> require(old: SingleArgParamOptional<T>): SingleArgParam<T> {
+ val newParam =
+ SingleArgParam(
+ longName = old.longName,
+ shortName = old.shortName,
+ description = old.description,
+ valueParser = old.valueParser,
+ )
+
+ replaceWithRequired(old, newParam)
+ return newParam
+ }
+
+ private fun <T : Any> replaceWithRequired(
+ old: SingleArgParamOptional<T>,
+ new: SingleArgParam<T>,
+ ) {
+ _params.remove(old)
+ _params.add(new)
+ }
+
+ /**
+ * Turns an [OptionalSubCommand] into a [RequiredSubCommand] by converting the [T?] in to [T]
+ *
+ * @return a [RequiredSubCommand] property delegate
+ */
+ fun <T : ParseableCommand> require(optional: OptionalSubCommand<T>): RequiredSubCommand<T> {
+ val newCmd = RequiredSubCommand(optional.cmd)
+ replaceWithRequired(optional, newCmd)
+ return newCmd
+ }
+
+ private fun <T : ParseableCommand> replaceWithRequired(
+ old: OptionalSubCommand<T>,
+ new: RequiredSubCommand<T>,
+ ) {
+ _subCommands.remove(old)
+ _subCommands.add(new)
+ }
+
+ internal fun flag(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ ): Flag {
+ checkCliNames(shortName, longName)?.let {
+ throw IllegalArgumentException("Detected reused flag name ($it)")
+ }
+ registerNames(shortName, longName)
+
+ val flag = Flag(shortName, longName, description)
+ _flags.add(flag)
+ return flag
+ }
+
+ internal fun <T : Any> param(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ valueParser: ValueParser<T>,
+ ): SingleArgParamOptional<T> {
+ checkCliNames(shortName, longName)?.let {
+ throw IllegalArgumentException("Detected reused param name ($it)")
+ }
+ registerNames(shortName, longName)
+
+ val param =
+ SingleArgParamOptional(
+ shortName = shortName,
+ longName = longName,
+ description = description,
+ valueParser = valueParser,
+ )
+ _params.add(param)
+ return param
+ }
+
+ internal fun <T : ParseableCommand> subCommand(
+ command: T,
+ ): OptionalSubCommand<T> {
+ checkCliNames(null, command.name)?.let {
+ throw IllegalArgumentException("Cannot re-use name for subcommand ($it)")
+ }
+
+ if (subCommandContainsSubCommands(command)) {
+ throw IllegalArgumentException(
+ "SubCommands may not contain other SubCommands. $command"
+ )
+ }
+
+ registerNames(null, command.name)
+
+ val subCmd = OptionalSubCommand(command)
+ _subCommands.add(subCmd)
+ return subCmd
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt
new file mode 100644
index 0000000..6ed5eed
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/Parameters.kt
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Definitions for all parameter types usable by [ParseableCommand]. Parameters are command line
+ * tokens that accept a fixed number of arguments and convert them to a parsed type.
+ *
+ * Example:
+ * ```
+ * my_command --single-arg-param arg
+ * ```
+ *
+ * In the example, `my_command` is the name of the command, `--single-arg-param` is the parameter,
+ * and `arg` is the value parsed by that parameter into its eventual type.
+ *
+ * Note on generics: The intended usage for parameters is to be able to return the parsed type from
+ * the given command as a `val` via property delegation. For example, let's say we have a command
+ * that has one optional and one required parameter:
+ * ```
+ * class MyCommand : ParseableCommand {
+ * val requiredParam: Int by parser.param(...).required()
+ * val optionalParam: Int? by parser.param(...)
+ * }
+ * ```
+ *
+ * In order to make the simple `param` method return the correct type, we need to do two things:
+ * 1. Break out the generic type into 2 pieces (TParsed and T)
+ * 2. Create two different underlying Parameter subclasses to handle the property delegation. One
+ * handles `T?` and the other handles `T`. Note that in both cases, `TParsed` is always non-null
+ * since the value parsed from the argument will throw an exception if missing or if it cannot be
+ * parsed.
+ */
+
+/** A param type knows the number of arguments it expects */
+sealed interface Param : Describable {
+ val numArgs: Int
+
+ /**
+ * Consume [numArgs] items from the iterator and relay the result into its corresponding
+ * delegated type.
+ */
+ fun parseArgsFromIter(iterator: Iterator<String>)
+}
+
+/**
+ * Base class for required and optional SingleArgParam classes. For convenience, UnaryParam is
+ * defined as a [MultipleArgParam] where numArgs = 1. The benefit is that we can define the parsing
+ * in a single place, and yet on the client side we can unwrap the underlying list of params
+ * automatically.
+ */
+abstract class UnaryParamBase<out T, out TParsed : T>(val wrapped: MultipleArgParam<T, TParsed>) :
+ Param, ReadOnlyProperty<Any?, T> {
+ var handled = false
+
+ override fun describe(pw: IndentingPrintWriter) {
+ if (shortName != null) {
+ pw.print("$shortName, ")
+ }
+ pw.print(longName)
+ pw.println(" ${typeDescription()}")
+ if (description != null) {
+ pw.indented { pw.println(description) }
+ }
+ }
+
+ /**
+ * Try to describe the arg type. We can know if it's one of the base types what kind of input it
+ * takes. Otherwise just print "<arg>" and let the clients describe in the help text
+ */
+ private fun typeDescription() =
+ when (wrapped.valueParser) {
+ Type.Int -> "<int>"
+ Type.Float -> "<float>"
+ Type.String -> "<string>"
+ Type.Boolean -> "<boolean>"
+ else -> "<arg>"
+ }
+}
+
+/** Required single-arg parameter, delegating a non-null type to the client. */
+class SingleArgParam<out T : Any>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val description: String? = null,
+ val valueParser: ValueParser<T>,
+) :
+ UnaryParamBase<T, T>(
+ MultipleArgParam(
+ longName,
+ shortName,
+ 1,
+ description,
+ valueParser,
+ )
+ ) {
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T =
+ if (handled) {
+ wrapped.getValue(thisRef, property)[0]
+ } else {
+ throw IllegalStateException("Attempt to read property before parse() has executed")
+ }
+
+ override val numArgs: Int = 1
+
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ wrapped.parseArgsFromIter(iterator)
+ handled = true
+ }
+}
+
+/** Optional single-argument parameter, delegating a nullable type to the client. */
+class SingleArgParamOptional<out T : Any>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val description: String? = null,
+ val valueParser: ValueParser<T>,
+) :
+ UnaryParamBase<T?, T>(
+ MultipleArgParam(
+ longName,
+ shortName,
+ 1,
+ description,
+ valueParser,
+ )
+ ) {
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
+ wrapped.getValue(thisRef, property).getOrNull(0)
+
+ override val numArgs: Int = 1
+
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ wrapped.parseArgsFromIter(iterator)
+ handled = true
+ }
+}
+
+/**
+ * Parses a list of args into the underlying [T] data type. The resultant value is an ordered list
+ * of type [TParsed].
+ *
+ * [T] and [TParsed] are split out here in the case where the entire param is optional. I.e., a
+ * MultipleArgParam<T?, T> indicates a command line argument that can be omitted. In that case, the
+ * inner list is List<T>?, NOT List<T?>. If the argument is provided, then the type is always going
+ * to be parsed into T rather than T?.
+ */
+class MultipleArgParam<out T, out TParsed : T>(
+ override val longName: String,
+ override val shortName: String? = null,
+ override val numArgs: Int = 1,
+ override val description: String? = null,
+ val valueParser: ValueParser<TParsed>,
+) : ReadOnlyProperty<Any?, List<TParsed>>, Param {
+ private val inner: MutableList<TParsed> = mutableListOf()
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): List<TParsed> = inner
+
+ /**
+ * Consumes [numArgs] values of the iterator and parses them into [TParsed].
+ *
+ * @throws ArgParseError on the first failure
+ */
+ override fun parseArgsFromIter(iterator: Iterator<String>) {
+ if (!iterator.hasNext()) {
+ throw ArgParseError("no argument provided for $shortName")
+ }
+ for (i in 0 until numArgs) {
+ valueParser
+ .parseValue(iterator.next())
+ .fold(onSuccess = { inner.add(it) }, onFailure = { throw it })
+ }
+ }
+}
+
+data class ArgParseError(override val message: String) : Exception(message)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt
new file mode 100644
index 0000000..ecd3fa6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ParseableCommand.kt
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import java.io.PrintWriter
+import java.lang.IllegalArgumentException
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * An implementation of [Command] that includes a [CommandParser] which can set all delegated
+ * properties.
+ *
+ * As the number of registrants to [CommandRegistry] grows, we should have a default mechanism for
+ * parsing common command line arguments. We are not expecting to build an arbitrarily-functional
+ * CLI, nor a GNU arg parse compliant interface here, we simply want to be able to empower clients
+ * to create simple CLI grammars such as:
+ * ```
+ * $ my_command [-f|--flag]
+ * $ my_command [-a|--arg] <params...>
+ * $ my_command [subcommand1] [subcommand2]
+ * $ my_command <positional_arg ...> # not-yet implemented
+ * ```
+ *
+ * Note that the flags `-h` and `--help` are reserved for the base class. It seems prudent to just
+ * avoid them in your implementation.
+ *
+ * Usage:
+ *
+ * The intended usage tries to be clever enough to enable good ergonomics, while not too clever as
+ * to be unmaintainable. Using the default parser is done using property delegates, and looks like:
+ * ```
+ * class MyCommand(
+ * onExecute: (cmd: MyCommand, pw: PrintWriter) -> ()
+ * ) : ParseableCommand(name) {
+ * val flag1 by flag(
+ * shortName = "-f",
+ * longName = "--flag",
+ * required = false,
+ * )
+ * val param1: String by param(
+ * shortName = "-a",
+ * longName = "--args",
+ * valueParser = Type.String
+ * ).required()
+ * val param2: Int by param(..., valueParser = Type.Int)
+ * val subCommand by subCommand(...)
+ *
+ * override fun execute(pw: PrintWriter) {
+ * onExecute(this, pw)
+ * }
+ *
+ * companion object {
+ * const val name = "my_command"
+ * }
+ * }
+ *
+ * fun main() {
+ * fun printArgs(cmd: MyCommand, pw: PrintWriter) {
+ * pw.println("${cmd.flag1}")
+ * pw.println("${cmd.param1}")
+ * pw.println("${cmd.param2}")
+ * pw.println("${cmd.subCommand}")
+ * }
+ *
+ * commandRegistry.registerCommand(MyCommand.companion.name) {
+ * MyCommand() { (cmd, pw) ->
+ * printArgs(cmd, pw)
+ * }
+ * }
+ * }
+ *
+ * ```
+ */
+abstract class ParseableCommand(val name: String, val description: String? = null) : Command {
+ val parser: CommandParser = CommandParser()
+
+ val help by flag(longName = "help", shortName = "h", description = "Print help and return")
+
+ /**
+ * After [execute(pw, args)] is called, this class goes through a parsing stage and sets all
+ * delegated properties. It is safe to read any delegated properties here.
+ *
+ * This method is never called for [SubCommand]s, since they are associated with a top-level
+ * command that handles [execute]
+ */
+ abstract fun execute(pw: PrintWriter)
+
+ /**
+ * Given a command string list, [execute] parses the incoming command and validates the input.
+ * If this command or any of its subcommands is passed `-h` or `--help`, then execute will only
+ * print the relevant help message and exit.
+ *
+ * If any error is thrown during parsing, we will catch and log the error. This process should
+ * _never_ take down its process. Override [onParseFailed] to handle an [ArgParseError].
+ *
+ * Important: none of the delegated fields can be read before this stage.
+ */
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ val success: Boolean
+ try {
+ success = parser.parse(args)
+ } catch (e: ArgParseError) {
+ pw.println(e.message)
+ onParseFailed(e)
+ return
+ } catch (e: Exception) {
+ pw.println("Unknown exception encountered during parse")
+ pw.println(e)
+ return
+ }
+
+ // Now we've parsed the incoming command without error. There are two things to check:
+ // 1. If any help is requested, print the help message and return
+ // 2. Otherwise, make sure required params have been passed in, and execute
+
+ val helpSubCmds = subCmdsRequestingHelp()
+
+ // Top-level help encapsulates subcommands. Otherwise, if _any_ subcommand requests
+ // help then defer to them. Else, just execute
+ if (help) {
+ help(pw)
+ } else if (helpSubCmds.isNotEmpty()) {
+ helpSubCmds.forEach { it.help(pw) }
+ } else {
+ if (!success) {
+ parser.generateValidationErrorMessages().forEach { pw.println(it) }
+ } else {
+ execute(pw)
+ }
+ }
+ }
+
+ /**
+ * Returns a list of all commands that asked for help. If non-empty, parsing will stop to print
+ * help. It is not guaranteed that delegates are fulfilled if help is requested
+ */
+ private fun subCmdsRequestingHelp(): List<ParseableCommand> =
+ parser.subCommands.filter { it.cmd.help }.map { it.cmd }
+
+ /** Override to do something when parsing fails */
+ open fun onParseFailed(error: ArgParseError) {}
+
+ /** Override to print a usage clause. E.g. `usage: my-cmd <arg1> <arg2>` */
+ open fun usage(pw: IndentingPrintWriter) {}
+
+ /**
+ * Print out the list of tokens, their received types if any, and their description in a
+ * formatted string.
+ *
+ * Example:
+ * ```
+ * my-command:
+ * MyCmd.description
+ *
+ * [optional] usage block
+ *
+ * Flags:
+ * -f
+ * description
+ * --flag2
+ * description
+ *
+ * Parameters:
+ * Required:
+ * -p1 [Param.Type]
+ * description
+ * --param2 [Param.Type]
+ * description
+ * Optional:
+ * same as above
+ *
+ * SubCommands:
+ * Required:
+ * ...
+ * Optional:
+ * ...
+ * ```
+ */
+ override fun help(pw: PrintWriter) {
+ val ipw = IndentingPrintWriter(pw)
+ ipw.printBoxed(name)
+ ipw.println()
+
+ // Allow for a simple `usage` block for clients
+ ipw.indented { usage(ipw) }
+
+ if (description != null) {
+ ipw.indented { ipw.println(description) }
+ ipw.println()
+ }
+
+ val flags = parser.flags
+ if (flags.isNotEmpty()) {
+ ipw.println("FLAGS:")
+ ipw.indented {
+ flags.forEach {
+ it.describe(ipw)
+ ipw.println()
+ }
+ }
+ }
+
+ val (required, optional) = parser.params.partition { it is SingleArgParam<*> }
+ if (required.isNotEmpty()) {
+ ipw.println("REQUIRED PARAMS:")
+ required.describe(ipw)
+ }
+ if (optional.isNotEmpty()) {
+ ipw.println("OPTIONAL PARAMS:")
+ optional.describe(ipw)
+ }
+
+ val (reqSub, optSub) = parser.subCommands.partition { it is RequiredSubCommand<*> }
+ if (reqSub.isNotEmpty()) {
+ ipw.println("REQUIRED SUBCOMMANDS:")
+ reqSub.describe(ipw)
+ }
+ if (optSub.isNotEmpty()) {
+ ipw.println("OPTIONAL SUBCOMMANDS:")
+ optSub.describe(ipw)
+ }
+ }
+
+ fun flag(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ ): Flag {
+ if (!checkShortName(shortName)) {
+ throw IllegalArgumentException(
+ "Flag short name must be one character long, or null. Got ($shortName)"
+ )
+ }
+
+ if (!checkLongName(longName)) {
+ throw IllegalArgumentException("Flags must not start with '-'. Got $($longName)")
+ }
+
+ val short = shortName?.let { "-$shortName" }
+ val long = "--$longName"
+
+ return parser.flag(long, short, description)
+ }
+
+ fun <T : Any> param(
+ longName: String,
+ shortName: String? = null,
+ description: String = "",
+ valueParser: ValueParser<T>,
+ ): SingleArgParamOptional<T> {
+ if (!checkShortName(shortName)) {
+ throw IllegalArgumentException(
+ "Parameter short name must be one character long, or null. Got ($shortName)"
+ )
+ }
+
+ if (!checkLongName(longName)) {
+ throw IllegalArgumentException("Parameters must not start with '-'. Got $($longName)")
+ }
+
+ val short = shortName?.let { "-$shortName" }
+ val long = "--$longName"
+
+ return parser.param(long, short, description, valueParser)
+ }
+
+ fun <T : ParseableCommand> subCommand(
+ command: T,
+ ) = parser.subCommand(command)
+
+ /** For use in conjunction with [param], makes the parameter required */
+ fun <T : Any> SingleArgParamOptional<T>.required(): SingleArgParam<T> = parser.require(this)
+
+ /** For use in conjunction with [subCommand], makes the given [SubCommand] required */
+ fun <T : ParseableCommand> OptionalSubCommand<T>.required(): RequiredSubCommand<T> =
+ parser.require(this)
+
+ private fun checkShortName(short: String?): Boolean {
+ return short == null || short.length == 1
+ }
+
+ private fun checkLongName(long: String): Boolean {
+ return !long.startsWith("-")
+ }
+
+ companion object {
+ fun Iterable<Describable>.describe(pw: IndentingPrintWriter) {
+ pw.indented {
+ forEach {
+ it.describe(pw)
+ pw.println()
+ }
+ }
+ }
+ }
+}
+
+/**
+ * A flag is a boolean value passed over the command line. It can have a short form or long form.
+ * The value is [Boolean.true] if the flag is found, else false
+ */
+data class Flag(
+ override val shortName: String? = null,
+ override val longName: String,
+ override val description: String? = null,
+) : ReadOnlyProperty<Any?, Boolean>, Describable {
+ var inner: Boolean = false
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>) = inner
+}
+
+/**
+ * Named CLI token. Can have a short or long name. Note: consider renaming to "primary" and
+ * "secondary" names since we don't actually care what the strings are
+ *
+ * Flags and params will have [shortName]s that are always prefixed with a single dash, while
+ * [longName]s are prefixed by a double dash. E.g., `my_command -f --flag`.
+ *
+ * Subcommands do not do any prefixing, and register their name as the [longName]
+ *
+ * Can be matched against an incoming token
+ */
+interface CliNamed {
+ val shortName: String?
+ val longName: String
+
+ fun matches(token: String) = shortName == token || longName == token
+}
+
+interface Describable : CliNamed {
+ val description: String?
+
+ fun describe(pw: IndentingPrintWriter) {
+ if (shortName != null) {
+ pw.print("$shortName, ")
+ }
+ pw.print(longName)
+ pw.println()
+ if (description != null) {
+ pw.indented { pw.println(description) }
+ }
+ }
+}
+
+/**
+ * Print [s] inside of a unicode character box, like so:
+ * ```
+ * ╔═══════════╗
+ * ║ my-string ║
+ * ╚═══════════╝
+ * ```
+ */
+fun PrintWriter.printDoubleBoxed(s: String) {
+ val length = s.length
+ println("╔${"═".repeat(length + 2)}╗")
+ println("║ $s ║")
+ println("╚${"═".repeat(length + 2)}╝")
+}
+
+/**
+ * Print [s] inside of a unicode character box, like so:
+ * ```
+ * ┌───────────┐
+ * │ my-string │
+ * └───────────┘
+ * ```
+ */
+fun PrintWriter.printBoxed(s: String) {
+ val length = s.length
+ println("┌${"─".repeat(length + 2)}┐")
+ println("│ $s │")
+ println("└${"─".repeat(length + 2)}┘")
+}
+
+fun IndentingPrintWriter.indented(block: () -> Unit) {
+ increaseIndent()
+ block()
+ decreaseIndent()
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt
new file mode 100644
index 0000000..41bac86
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/SubCommand.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import android.util.IndentingPrintWriter
+import kotlin.properties.ReadOnlyProperty
+import kotlin.reflect.KProperty
+
+/**
+ * Sub commands wrap [ParseableCommand]s and are attached to a parent [ParseableCommand]. As such
+ * they have their own parser which will parse the args as a subcommand. I.e., the subcommand's
+ * parser will consume the iterator created by the parent, reversing the index when it reaches an
+ * unknown token.
+ *
+ * In order to keep subcommands relatively simple and not have to do complicated validation, sub
+ * commands will return control to the parent parser as soon as they discover a token that they do
+ * not own. They will throw an [ArgParseError] if parsing fails or if they don't receive arguments
+ * for a required parameter.
+ */
+sealed interface SubCommand : Describable {
+ val cmd: ParseableCommand
+
+ /** Checks if all of the required elements were passed in to [parseSubCommandArgs] */
+ var validationStatus: Boolean
+
+ /**
+ * To keep parsing simple, [parseSubCommandArgs] requires a [ListIterator] so that it can rewind
+ * the iterator when it yields control upwards
+ */
+ fun parseSubCommandArgs(iterator: ListIterator<String>)
+}
+
+/**
+ * Note that the delegated type from the subcommand is `T: ParseableCommand?`. SubCommands are
+ * created via adding a fully-formed [ParseableCommand] to parent command.
+ *
+ * At this point in time, I don't recommend nesting subcommands.
+ */
+class OptionalSubCommand<T : ParseableCommand>(
+ override val cmd: T,
+) : SubCommand, ReadOnlyProperty<Any?, ParseableCommand?> {
+ override val shortName: String? = null
+ override val longName: String = cmd.name
+ override val description: String? = cmd.description
+ override var validationStatus = true
+
+ private var isPresent = false
+
+ /** Consume tokens from the iterator and pass them to the wrapped command */
+ override fun parseSubCommandArgs(iterator: ListIterator<String>) {
+ validationStatus = cmd.parser.parseAsSubCommand(iterator)
+ isPresent = true
+ }
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): T? =
+ if (isPresent) {
+ cmd
+ } else {
+ null
+ }
+
+ override fun describe(pw: IndentingPrintWriter) {
+ cmd.help(pw)
+ }
+}
+
+/**
+ * Non-optional subcommand impl. Top-level parser is expected to throw [ArgParseError] if this token
+ * is not present in the incoming command
+ */
+class RequiredSubCommand<T : ParseableCommand>(
+ override val cmd: T,
+) : SubCommand, ReadOnlyProperty<Any?, ParseableCommand> {
+ override val shortName: String? = null
+ override val longName: String = cmd.name
+ override val description: String? = cmd.description
+ override var validationStatus = true
+
+ /** Unhandled, required subcommands are an error */
+ var handled = false
+
+ override fun parseSubCommandArgs(iterator: ListIterator<String>) {
+ validationStatus = cmd.parser.parseAsSubCommand(iterator)
+ handled = true
+ }
+
+ override fun getValue(thisRef: Any?, property: KProperty<*>): ParseableCommand = cmd
+
+ override fun describe(pw: IndentingPrintWriter) {
+ cmd.help(pw)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
new file mode 100644
index 0000000..01083d9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/commandline/ValueParser.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import kotlin.contracts.ExperimentalContracts
+import kotlin.contracts.InvocationKind
+import kotlin.contracts.contract
+
+/**
+ * Utilities for parsing the [String] command line arguments. Arguments are related to the
+ * [Parameter] type, which declares the number of, and resulting type of, the arguments that it
+ * takes when parsing. For Example:
+ * ```
+ * my-command --param <str> --param2 <int>
+ * ```
+ *
+ * Defines 2 parameters, the first of which takes a string, and the second requires an int. Because
+ * fundamentally _everything_ is a string, we have to define a convenient way to get from the
+ * incoming `StringArg` to the resulting `T`-arg, where `T` is the type required by the client.
+ *
+ * Parsing is therefore a relatively straightforward operation: (String) -> T. However, since
+ * parsing can always fail, the type is actually (String) -> Result<T>. We will always want to fail
+ * on the first error and propagate it to the caller (typically this results in printing the `help`
+ * message of the command`).
+ *
+ * The identity parsing is trivial:
+ * ```
+ * (s: String) -> String = { s -> s }
+ * ```
+ *
+ * Basic mappings are actually even provided by Kotlin's stdlib:
+ * ```
+ * (s: String) -> Boolean = { s -> s.toBooleanOrNull() }
+ * (s: String) -> Int = { s -> s.toIntOrNull() }
+ * ...
+ * ```
+ *
+ * In order to properly encode errors, we will ascribe an error type to any `null` values, such that
+ * parsing looks like this:
+ * ```
+ * val mapping: (String) -> T? = {...} // for some T
+ * val parser: (String) -> Result<T> = { s ->
+ * mapping(s)?.let {
+ * Result.success(it)
+ * } ?: Result.failure(/* some failure type */)
+ * }
+ * ```
+ *
+ * Composition
+ *
+ * The ability to compose value parsing enables us to provide a couple of reasonable default parsers
+ * and allow clients to seamlessly build upon that using map functions. Consider the case where we
+ * want to validate that a value is an [Int] between 0 and 100. We start with the generic [Int]
+ * parser, and a validator, of the type (Int) -> Result<Int>:
+ * ```
+ * val intParser = { s ->
+ * s.toStringOrNull().?let {...} ?: ...
+ * }
+ *
+ * val validator = { i ->
+ * if (i > 100 || i < 0) {
+ * Result.failure(...)
+ * } else {
+ * Result.success(i)
+ * }
+ * ```
+ *
+ * In order to combine these functions, we need to define a new [flatMap] function that can get us
+ * from a `Result<T>` to a `Result<R>`, and short-circuit on any error. We want to see this:
+ * ```
+ * val validatingParser = { s ->
+ * intParser.invoke(s).flatMap { i ->
+ * validator(i)
+ * }
+ * }
+ * ```
+ *
+ * The flatMap is relatively simply defined, we can mimic the existing definition for [Result.map],
+ * though the implementation is uglier because of the `internal` definition for `value`
+ *
+ * ```
+ * inline fun <R, T> Result<T>.flatMap(transform: (value: T) -> Result<R>): Result<R> {
+ * return when {
+ * isSuccess -> transform(getOrThrow())
+ * else -> Result.failure(exceptionOrNull()!!)
+ * }
+ * }
+ * ```
+ */
+
+/**
+ * Given a [transform] that returns a [Result], apply the transform to this result, unwrapping the
+ * return value so that
+ *
+ * These [contract] and [callsInPlace] methods are copied from the [Result.map] definition
+ */
+@OptIn(ExperimentalContracts::class)
+inline fun <R, T> Result<T>.flatMap(transform: (value: T) -> Result<R>): Result<R> {
+ contract { callsInPlace(transform, InvocationKind.AT_MOST_ONCE) }
+
+ return when {
+ // Should never throw, we just don't have access to [this.value]
+ isSuccess -> transform(getOrThrow())
+ // Exception should never be null here
+ else -> Result.failure(exceptionOrNull()!!)
+ }
+}
+
+/**
+ * ValueParser turns a [String] into a Result<A> by applying a transform. See the default
+ * implementations below for starting points. The intention here is to provide the base mappings and
+ * allow clients to attach their own transforms. They are expected to succeed or return null on
+ * failure. The failure is propagated to the command parser as a Result and will fail on any
+ * [Result.failure]
+ */
+fun interface ValueParser<out A> {
+ fun parseValue(value: String): Result<A>
+}
+
+/** Map a [ValueParser] of type A to one of type B, by applying the given [transform] */
+inline fun <A, B> ValueParser<A>.map(crossinline transform: (A) -> B?): ValueParser<B> {
+ return ValueParser<B> { value ->
+ this.parseValue(value).flatMap { a ->
+ transform(a)?.let { b -> Result.success(b) }
+ ?: Result.failure(ArgParseError("Failed to transform value $value"))
+ }
+ }
+}
+
+/**
+ * Base type parsers are provided by the lib, and can be simply composed upon by [ValueParser.map]
+ * functions on the parser
+ */
+
+/** String parsing always succeeds if the value exists */
+private val parseString: ValueParser<String> = ValueParser { value -> Result.success(value) }
+
+private val parseBoolean: ValueParser<Boolean> = ValueParser { value ->
+ value.toBooleanStrictOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as a boolean"))
+}
+
+private val parseInt: ValueParser<Int> = ValueParser { value ->
+ value.toIntOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as an int"))
+}
+
+private val parseFloat: ValueParser<Float> = ValueParser { value ->
+ value.toFloatOrNull()?.let { Result.success(it) }
+ ?: Result.failure(ArgParseError("Failed to parse $value as a float"))
+}
+
+/** Default parsers that can be use as-is, or [map]ped to another type */
+object Type {
+ val Boolean = parseBoolean
+ val Int = parseInt
+ val Float = parseFloat
+ val String = parseString
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
index 2465c21..73f181b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/NetworkControllerImpl.java
@@ -74,7 +74,7 @@
import com.android.systemui.demomode.DemoModeController;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.log.LogBuffer;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.log.dagger.StatusBarNetworkControllerLog;
import com.android.systemui.qs.tiles.dialog.InternetDialogFactory;
import com.android.systemui.settings.UserTracker;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
index 075b41b..035fa04 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/dagger/CentralSurfacesDependenciesModule.java
@@ -41,6 +41,8 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.power.domain.interactor.PowerInteractor;
import com.android.systemui.settings.DisplayTracker;
+import com.android.systemui.shade.NotificationPanelViewController;
+import com.android.systemui.shade.ShadeSurface;
import com.android.systemui.shade.carrier.ShadeCarrierGroupController;
import com.android.systemui.statusbar.ActionClickLogger;
import com.android.systemui.statusbar.CommandQueue;
@@ -273,6 +275,21 @@
return ongoingCallController;
}
+ /**
+ * {@link NotificationPanelViewController} implements two interfaces:
+ * - {@link com.android.systemui.shade.ShadeViewController}, which can be used by any class
+ * needing access to the shade.
+ * - {@link ShadeSurface}, which should *only* be used by {@link CentralSurfacesImpl}.
+ *
+ * Since {@link ShadeSurface} should only be accessible by {@link CentralSurfacesImpl}, it's
+ * *only* bound in this CentralSurfaces dependencies module.
+ * The {@link com.android.systemui.shade.ShadeViewController} interface is bound in
+ * {@link com.android.systemui.shade.ShadeModule} so others can access it.
+ */
+ @Binds
+ @SysUISingleton
+ ShadeSurface provideShadeSurface(NotificationPanelViewController impl);
+
/** */
@Binds
ShadeCarrierGroupController.SlotIndexResolver provideSlotIndexResolver(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
index ac05248..2bb4765 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/disableflags/data/model/DisableFlagsModel.kt
@@ -20,7 +20,7 @@
import android.app.StatusBarManager.DISABLE_NONE
import android.app.StatusBarManager.DISABLE_NOTIFICATION_ALERTS
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
index 3d6d489..84796f9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/StatusBarEventsModule.kt
@@ -22,6 +22,8 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.LogBufferFactory
import com.android.systemui.statusbar.window.StatusBarWindowController
import com.android.systemui.util.concurrency.DelayableExecutor
import com.android.systemui.util.time.SystemClock
@@ -36,6 +38,13 @@
@Provides
@SysUISingleton
+ @SystemStatusAnimationSchedulerLog
+ fun provideSystemStatusAnimationSchedulerLogBuffer(factory: LogBufferFactory): LogBuffer {
+ return factory.create("SystemStatusAnimationSchedulerLog", 60)
+ }
+
+ @Provides
+ @SysUISingleton
fun provideSystemStatusAnimationScheduler(
featureFlags: FeatureFlags,
coordinator: SystemEventCoordinator,
@@ -44,7 +53,8 @@
dumpManager: DumpManager,
systemClock: SystemClock,
@Application coroutineScope: CoroutineScope,
- @Main executor: DelayableExecutor
+ @Main executor: DelayableExecutor,
+ logger: SystemStatusAnimationSchedulerLogger
): SystemStatusAnimationScheduler {
return if (featureFlags.isEnabled(Flags.PLUG_IN_STATUS_BAR_CHIP)) {
SystemStatusAnimationSchedulerImpl(
@@ -53,7 +63,8 @@
statusBarWindowController,
dumpManager,
systemClock,
- coroutineScope
+ coroutineScope,
+ logger
)
} else {
SystemStatusAnimationSchedulerLegacyImpl(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
index 56ea703..6fc715a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImpl.kt
@@ -18,7 +18,6 @@
import android.os.Process
import android.provider.DeviceConfig
-import android.util.Log
import androidx.core.animation.Animator
import androidx.core.animation.AnimatorListenerAdapter
import androidx.core.animation.AnimatorSet
@@ -69,7 +68,8 @@
private val statusBarWindowController: StatusBarWindowController,
dumpManager: DumpManager,
private val systemClock: SystemClock,
- @Application private val coroutineScope: CoroutineScope
+ @Application private val coroutineScope: CoroutineScope,
+ private val logger: SystemStatusAnimationSchedulerLogger?
) : SystemStatusAnimationScheduler {
companion object {
@@ -121,6 +121,10 @@
}
}
}
+
+ coroutineScope.launch {
+ animationState.collect { logger?.logAnimationStateUpdate(it) }
+ }
}
@SystemAnimationState override fun getAnimationState(): Int = animationState.value
@@ -140,32 +144,17 @@
) {
// a event can only be scheduled if no other event is in progress or it has a higher
// priority. If a persistent dot is currently displayed, don't schedule the event.
- if (DEBUG) {
- Log.d(TAG, "scheduling event $event")
- }
-
+ logger?.logScheduleEvent(event)
scheduleEvent(event)
} else if (currentlyDisplayedEvent?.shouldUpdateFromEvent(event) == true) {
- if (DEBUG) {
- Log.d(
- TAG,
- "updating current event from: $event. animationState=${animationState.value}"
- )
- }
+ logger?.logUpdateEvent(event, animationState.value)
currentlyDisplayedEvent?.updateFromEvent(event)
if (event.forceVisible) hasPersistentDot = true
} else if (scheduledEvent.value?.shouldUpdateFromEvent(event) == true) {
- if (DEBUG) {
- Log.d(
- TAG,
- "updating scheduled event from: $event. animationState=${animationState.value}"
- )
- }
+ logger?.logUpdateEvent(event, animationState.value)
scheduledEvent.value?.updateFromEvent(event)
} else {
- if (DEBUG) {
- Log.d(TAG, "ignoring event $event")
- }
+ logger?.logIgnoreEvent(event)
}
}
@@ -356,6 +345,7 @@
}
private fun notifyTransitionToPersistentDot(): Animator? {
+ logger?.logTransitionToPersistentDotCallbackInvoked()
val anims: List<Animator> =
listeners.mapNotNull {
it.onSystemStatusAnimationTransitionToPersistentDot(
@@ -373,6 +363,7 @@
private fun notifyHidePersistentDot(): Animator? {
Assert.isMainThread()
+ logger?.logHidePersistentDotCallbackInvoked()
val anims: List<Animator> = listeners.mapNotNull { it.onHidePersistentDot() }
if (animationState.value == SHOWING_PERSISTENT_DOT) {
@@ -424,5 +415,4 @@
}
}
-private const val DEBUG = false
private const val TAG = "SystemStatusAnimationSchedulerImpl"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt
new file mode 100644
index 0000000..4ac94a6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLog.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.events
+
+import javax.inject.Qualifier
+
+/** Logs for the SystemStatusAnimationScheduler. */
+@Qualifier
+@MustBeDocumented
+@kotlin.annotation.Retention(AnnotationRetention.RUNTIME)
+annotation class SystemStatusAnimationSchedulerLog
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt
new file mode 100644
index 0000000..22b0b69
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerLogger.kt
@@ -0,0 +1,92 @@
+package com.android.systemui.statusbar.events
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.log.LogBuffer
+import com.android.systemui.log.core.LogLevel
+import javax.inject.Inject
+
+/** Logs for the SystemStatusAnimationScheduler. */
+@SysUISingleton
+class SystemStatusAnimationSchedulerLogger
+@Inject
+constructor(
+ @SystemStatusAnimationSchedulerLog private val logBuffer: LogBuffer,
+) {
+
+ fun logScheduleEvent(event: StatusEvent) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ },
+ { "Scheduling event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }
+ )
+ }
+
+ fun logUpdateEvent(event: StatusEvent, @SystemAnimationState animationState: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ int2 = animationState
+ },
+ {
+ "Updating current event from: $str1(forceVisible=$bool1, priority=$int1, " +
+ "showAnimation=$bool2), animationState=${animationState.name()}"
+ }
+ )
+ }
+
+ fun logIgnoreEvent(event: StatusEvent) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ {
+ str1 = event.javaClass.simpleName
+ int1 = event.priority
+ bool1 = event.forceVisible
+ bool2 = event.showAnimation
+ },
+ { "Ignore event: $str1(forceVisible=$bool1, priority=$int1, showAnimation=$bool2)" }
+ )
+ }
+
+ fun logHidePersistentDotCallbackInvoked() {
+ logBuffer.log(TAG, LogLevel.DEBUG, "Hide persistent dot callback invoked")
+ }
+
+ fun logTransitionToPersistentDotCallbackInvoked() {
+ logBuffer.log(TAG, LogLevel.DEBUG, "Transition to persistent dot callback invoked")
+ }
+
+ fun logAnimationStateUpdate(@SystemAnimationState animationState: Int) {
+ logBuffer.log(
+ TAG,
+ LogLevel.DEBUG,
+ { int1 = animationState },
+ { "AnimationState update: ${int1.name()}" }
+ )
+ animationState.name()
+ }
+
+ private fun @receiver:SystemAnimationState Int.name() =
+ when (this) {
+ IDLE -> "IDLE"
+ ANIMATION_QUEUED -> "ANIMATION_QUEUED"
+ ANIMATING_IN -> "ANIMATING_IN"
+ RUNNING_CHIP_ANIM -> "RUNNING_CHIP_ANIM"
+ ANIMATING_OUT -> "ANIMATING_OUT"
+ SHOWING_PERSISTENT_DOT -> "SHOWING_PERSISTENT_DOT"
+ else -> "UNKNOWN_ANIMATION_STATE"
+ }
+}
+
+private const val TAG = "SystemStatusAnimationSchedulerLog"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
index a67c26c..96725fc 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/gesture/SwipeUpGestureLogger.kt
@@ -19,7 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.SwipeUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
/** Log messages for [SwipeUpGestureHandler]. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
index 8778463..11b1053 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/lockscreen/LockscreenSmartspaceController.kt
@@ -124,6 +124,7 @@
private var showSensitiveContentForCurrentUser = false
private var showSensitiveContentForManagedUser = false
private var managedUserHandle: UserHandle? = null
+ private var mSplitShadeEnabled = false
// TODO(b/202758428): refactor so that we can test color updates via region samping, similar to
// how we test color updates when theme changes (See testThemeChangeUpdatesTextColor).
@@ -131,6 +132,7 @@
// TODO: Move logic into SmartspaceView
var stateChangeListener = object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
+ (v as SmartspaceView).setSplitShadeEnabled(mSplitShadeEnabled)
smartspaceViews.add(v as SmartspaceView)
connectSession()
@@ -221,6 +223,11 @@
execution.assertIsMainThread()
smartspaceViews.forEach { it.setDozeAmount(eased) }
}
+
+ override fun onDozingChanged(isDozing: Boolean) {
+ execution.assertIsMainThread()
+ smartspaceViews.forEach { it.setDozing(isDozing) }
+ }
}
private val deviceProvisionedListener =
@@ -426,6 +433,11 @@
reloadSmartspace()
}
+ fun setSplitShadeEnabled(enabled: Boolean) {
+ mSplitShadeEnabled = enabled
+ smartspaceViews.forEach { it.setSplitShadeEnabled(enabled) }
+ }
+
/**
* Requests the smartspace session for an update.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
index a3a72d9..cea2b59 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationClickerLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
index f7679ed..502e1d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLogger.kt
@@ -14,7 +14,7 @@
package com.android.systemui.statusbar.notification
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.NotificationLockscreenLog
import com.android.systemui.statusbar.StatusBarState
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
index 487a5f8..7809eaa 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/RemoteInputControllerLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.DEBUG
import com.android.systemui.log.dagger.NotificationRemoteInputLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
index 39d0833..0ab348d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coalescer/GroupCoalescerLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
class GroupCoalescerLogger @Inject constructor(
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
index 79c63e6..bd1141e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/GutsCoordinatorLogger.kt
@@ -2,7 +2,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.row.NotificationGuts
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
index e17ce5c..496fb83 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinatorLogger.kt
@@ -4,7 +4,7 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "HeadsUpCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
index 1f8ec34..4c33524 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.collection.coordinator
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.UnseenNotificationLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
index 6271d38..bf65043 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/PreparationCoordinatorLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
index 1f4861a..0d9681f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ShadeEventCoordinatorLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "ShadeEventCoordinator"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
index f13ff68..a8409d0c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/ShadeListBuilderLogger.kt
@@ -18,9 +18,9 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.GroupEntry
import com.android.systemui.statusbar.notification.collection.ListEntry
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
index 73227ab..014ac54 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/notifcollection/NotifCollectionLogger.kt
@@ -22,11 +22,11 @@
import android.service.notification.StatusBarNotification
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
-import com.android.systemui.log.LogLevel.WTF
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.WTF
import com.android.systemui.statusbar.notification.collection.NotifCollection
import com.android.systemui.statusbar.notification.collection.NotifCollection.CancellationReason
import com.android.systemui.statusbar.notification.collection.NotifCollection.FutureDismissal
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
index 07fd349..e61f9bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/NodeSpecBuilderLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.listbuilder.NotifSection
import com.android.systemui.util.Compile
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
index a880b71..082f308 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/ShadeViewDifferLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import java.lang.RuntimeException
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
index 0b31265..c6d2861 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/HeadsUpViewBinderLogger.kt
@@ -2,7 +2,7 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
index 5bac2a9..4a823a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/interruption/NotificationInterruptLogger.kt
@@ -20,9 +20,9 @@
import com.android.systemui.log.dagger.NotificationInterruptLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import com.android.systemui.util.Compile
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
index fe03b2a..0e1f66f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/logging/NotificationRoundnessLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.logging
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
index 8af488e..27510d4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ActivatableNotificationView.java
@@ -359,9 +359,9 @@
}
@Override
- public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
- Runnable onFinishedRunnable, AnimatorListenerAdapter animationListener) {
+ public long performRemoveAnimation(long duration, long delay, float translationDirection,
+ boolean isHeadsUpAnimation, Runnable onFinishedRunnable,
+ AnimatorListenerAdapter animationListener) {
enableAppearDrawing(true);
mIsHeadsUpAnimation = isHeadsUpAnimation;
if (mDrawingAppearAnimation) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
index 30747db..b34c281 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRow.java
@@ -2975,7 +2975,6 @@
long delay,
float translationDirection,
boolean isHeadsUpAnimation,
- float endLocation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
if (mMenuRow != null && mMenuRow.isMenuVisible()) {
@@ -2986,7 +2985,7 @@
public void onAnimationEnd(Animator animation) {
ExpandableNotificationRow.super.performRemoveAnimation(
duration, delay, translationDirection, isHeadsUpAnimation,
- endLocation, onFinishedRunnable, animationListener);
+ onFinishedRunnable, animationListener);
}
});
anim.start();
@@ -2994,7 +2993,7 @@
}
}
return super.performRemoveAnimation(duration, delay, translationDirection,
- isHeadsUpAnimation, endLocation, onFinishedRunnable, animationListener);
+ isHeadsUpAnimation, onFinishedRunnable, animationListener);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index f0e15c2..f986244 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -367,7 +367,6 @@
* such that the child appears to be going away to the top. 1
* Should mean the opposite.
* @param isHeadsUpAnimation Is this a headsUp animation.
- * @param endLocation The location where the horizonal heads up disappear animation should end.
* @param onFinishedRunnable A runnable which should be run when the animation is finished.
* @param animationListener An animation listener to add to the animation.
*
@@ -375,7 +374,7 @@
* animation starts.
*/
public abstract long performRemoveAnimation(long duration,
- long delay, float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ long delay, float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
index 45be0b1..3856700 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotifBindPipelineLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
index 89338f9..4f5a04f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/NotificationRowLogger.kt
@@ -19,7 +19,7 @@
import android.view.ViewGroup
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
index 684a276..02627fd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/RowContentBindStageLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.NotificationLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
index b24cec1..0c686be 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/StackScrollerDecorView.java
@@ -235,7 +235,7 @@
@Override
public long performRemoveAnimation(long duration, long delay,
- float translationDirection, boolean isHeadsUpAnimation, float endLocation,
+ float translationDirection, boolean isHeadsUpAnimation,
Runnable onFinishedRunnable,
AnimatorListenerAdapter animationListener) {
// TODO: Use duration
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
index b8f28b5..04308b4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/MediaContainerView.kt
@@ -69,11 +69,14 @@
canvas.clipPath(clipPath)
}
-
- override fun performRemoveAnimation(duration: Long, delay: Long, translationDirection: Float,
- isHeadsUpAnimation: Boolean, endLocation: Float,
- onFinishedRunnable: Runnable?,
- animationListener: AnimatorListenerAdapter?): Long {
+ override fun performRemoveAnimation(
+ duration: Long,
+ delay: Long,
+ translationDirection: Float,
+ isHeadsUpAnimation: Boolean,
+ onFinishedRunnable: Runnable?,
+ animationListener: AnimatorListenerAdapter?
+ ): Long {
return 0
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
index 6be1ef8..4986b63 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerLogger.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.notification.stack
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
index f953187..2da5582 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsLogger.kt
@@ -19,7 +19,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.dagger.NotificationSectionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
private const val TAG = "NotifSections"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 7c66bbe5..ef7375a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -424,7 +424,8 @@
}
};
- private final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
+ @VisibleForTesting
+ final NotificationSwipeHelper.NotificationCallback mNotificationCallback =
new NotificationSwipeHelper.NotificationCallback() {
@Override
@@ -483,10 +484,11 @@
*/
public void handleChildViewDismissed(View view) {
+ // The View needs to clean up the Swipe states, e.g. roundness.
+ mView.onSwipeEnd();
if (mView.getClearAllInProgress()) {
return;
}
- mView.onSwipeEnd();
if (view instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) view;
if (row.isHeadsUp()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
index 9c1bd17..2c38b8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -1,11 +1,11 @@
package com.android.systemui.statusbar.notification.stack
import android.view.ViewGroup
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
index d73919b..2742a23 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateAnimator.java
@@ -27,8 +27,6 @@
import com.android.systemui.R;
import com.android.systemui.shared.clocks.AnimatableClockView;
import com.android.systemui.statusbar.NotificationShelf;
-import com.android.systemui.statusbar.StatusBarIconView;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.ExpandableView;
import com.android.systemui.statusbar.notification.row.StackScrollerDecorView;
@@ -427,7 +425,7 @@
}
changingView.performRemoveAnimation(ANIMATION_DURATION_APPEAR_DISAPPEAR,
0 /* delay */, translationDirection, false /* isHeadsUpAppear */,
- 0, postAnimation, null);
+ postAnimation, null);
} else if (event.animationType ==
NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_REMOVE_SWIPED_OUT) {
if (mHostLayout.isFullySwipedOut(changingView)) {
@@ -474,28 +472,12 @@
mTmpState.initFrom(changingView);
endRunnable = changingView::removeFromTransientContainer;
}
- float targetLocation = 0;
boolean needsAnimation = true;
if (changingView instanceof ExpandableNotificationRow) {
ExpandableNotificationRow row = (ExpandableNotificationRow) changingView;
if (row.isDismissed()) {
needsAnimation = false;
}
-
- NotificationEntry entry = row.getEntry();
- StatusBarIconView icon = entry.getIcons().getStatusBarIcon();
- final StatusBarIconView centeredIcon = entry.getIcons().getCenteredIcon();
- if (centeredIcon != null && centeredIcon.getParent() != null) {
- icon = centeredIcon;
- }
- if (icon.getParent() != null) {
- icon.getLocationOnScreen(mTmpLocation);
- float iconPosition = mTmpLocation[0] - icon.getTranslationX()
- + ViewState.getFinalTranslationX(icon)
- + icon.getWidth() * 0.25f;
- mHostLayout.getLocationOnScreen(mTmpLocation);
- targetLocation = iconPosition - mTmpLocation[0];
- }
}
if (needsAnimation) {
@@ -515,7 +497,7 @@
}
long removeAnimationDelay = changingView.performRemoveAnimation(
ANIMATION_DURATION_HEADS_UP_DISAPPEAR,
- 0, 0.0f, true /* isHeadsUpAppear */, targetLocation,
+ 0, 0.0f, true /* isHeadsUpAppear */,
postAnimation, getGlobalAnimationFinishedListener());
mAnimationProperties.delay += removeAnimationDelay;
} else if (endRunnable != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
index c7f80f3..0b2c486 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackStateLogger.kt
@@ -1,8 +1,8 @@
package com.android.systemui.statusbar.notification.stack
-import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.dagger.NotificationRenderLog
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
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 4ba09e1..478baf2f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -203,8 +203,6 @@
int getStatusBarHeight();
- boolean isShadeDisabled();
-
boolean isLaunchingActivityOverLockscreen();
void onKeyguardViewManagerStatesUpdated();
@@ -380,14 +378,6 @@
void resendMessage(Object msg);
- int getDisabled1();
-
- void setDisabled1(int disabled);
-
- int getDisabled2();
-
- void setDisabled2(int disabled);
-
void setLastCameraLaunchSource(int source);
void setLaunchCameraOnFinishedGoingToSleep(boolean launch);
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 332be2a..6431ef9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacks.java
@@ -111,6 +111,9 @@
private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+ private int mDisabled1;
+ private int mDisabled2;
+
@Inject
CentralSurfacesCommandQueueCallbacks(
CentralSurfaces centralSurfaces,
@@ -256,22 +259,14 @@
return;
}
- int state2BeforeAdjustment = state2;
- state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
- Log.d(CentralSurfaces.TAG,
- mDisableFlagsLogger.getDisableFlagsString(
- /* new= */ new DisableFlagsLogger.DisableState(
- state1, state2BeforeAdjustment),
- /* newStateAfterLocalModification= */ new DisableFlagsLogger.DisableState(
- state1, state2)));
-
- final int old1 = mCentralSurfaces.getDisabled1();
+ final int old1 = mDisabled1;
final int diff1 = state1 ^ old1;
- mCentralSurfaces.setDisabled1(state1);
+ mDisabled1 = state1;
- final int old2 = mCentralSurfaces.getDisabled2();
+ state2 = mRemoteInputQuickSettingsDisabler.adjustDisableFlags(state2);
+ final int old2 = mDisabled2;
final int diff2 = state2 ^ old2;
- mCentralSurfaces.setDisabled2(state2);
+ mDisabled2 = state2;
if ((diff1 & StatusBarManager.DISABLE_EXPAND) != 0) {
if ((state1 & StatusBarManager.DISABLE_EXPAND) != 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 88ccae6..0d3dfae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -182,7 +182,6 @@
import com.android.systemui.settings.UserTracker;
import com.android.systemui.settings.brightness.BrightnessSliderController;
import com.android.systemui.shade.CameraLauncher;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
import com.android.systemui.shade.QuickSettingsController;
@@ -363,26 +362,6 @@
}
@Override
- public int getDisabled1() {
- return mDisabled1;
- }
-
- @Override
- public void setDisabled1(int disabled) {
- mDisabled1 = disabled;
- }
-
- @Override
- public int getDisabled2() {
- return mDisabled2;
- }
-
- @Override
- public void setDisabled2(int disabled) {
- mDisabled2 = disabled;
- }
-
- @Override
public void setLastCameraLaunchSource(int source) {
mLastCameraLaunchSource = source;
}
@@ -497,14 +476,12 @@
private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
- @VisibleForTesting
- ShadeSurface mShadeSurface;
+ private final ShadeSurface mShadeSurface;
private final ShadeLogger mShadeLogger;
// settings
private QSPanelController mQSPanelController;
- @VisibleForTesting
- QuickSettingsController mQsController;
+ private final QuickSettingsController mQsController;
KeyguardIndicationController mKeyguardIndicationController;
@@ -530,11 +507,6 @@
private CentralSurfacesComponent mCentralSurfacesComponent;
- // Flags for disabling the status bar
- // Two variables because the first one evidently ran out of room for new flags.
- private int mDisabled1 = 0;
- private int mDisabled2 = 0;
-
/**
* This keeps track of whether we have (or haven't) registered the predictive back callback.
* Since we can have visible -> visible transitions, we need to avoid
@@ -729,9 +701,11 @@
MetricsLogger metricsLogger,
ShadeLogger shadeLogger,
@UiBackground Executor uiBgExecutor,
+ ShadeSurface shadeSurface,
NotificationMediaManager notificationMediaManager,
NotificationLockscreenUserManager lockScreenUserManager,
NotificationRemoteInputManager remoteInputManager,
+ QuickSettingsController quickSettingsController,
UserSwitcherController userSwitcherController,
BatteryController batteryController,
SysuiColorExtractor colorExtractor,
@@ -830,9 +804,11 @@
mMetricsLogger = metricsLogger;
mShadeLogger = shadeLogger;
mUiBgExecutor = uiBgExecutor;
+ mShadeSurface = shadeSurface;
mMediaManager = notificationMediaManager;
mLockscreenUserManager = lockScreenUserManager;
mRemoteInputManager = remoteInputManager;
+ mQsController = quickSettingsController;
mUserSwitcherController = userSwitcherController;
mBatteryController = batteryController;
mColorExtractor = colorExtractor;
@@ -1636,13 +1612,9 @@
// (Right now, there's a circular dependency.)
mNotificationShadeWindowController.setWindowRootView(windowRootView);
mNotificationShadeWindowViewController.setupExpandedStatusBar();
- NotificationPanelViewController npvc =
- mCentralSurfacesComponent.getNotificationPanelViewController();
- mShadeSurface = npvc;
- mShadeController.setNotificationPanelViewController(npvc);
+ mShadeController.setShadeViewController(mShadeSurface);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
- mQsController = mCentralSurfacesComponent.getQuickSettingsController();
mBackActionInteractor.setup(mQsController, mShadeSurface);
mPresenter = mCentralSurfacesComponent.getNotificationPresenter();
mNotificationActivityStarter = mCentralSurfacesComponent.getNotificationActivityStarter();
@@ -1724,11 +1696,6 @@
return mStatusBarWindowController.getStatusBarHeight();
}
- @Override
- public boolean isShadeDisabled() {
- return (mDisabled2 & StatusBarManager.DISABLE2_NOTIFICATION_SHADE) != 0;
- }
-
private void updateReportRejectedTouchVisibility() {
if (mReportRejectedTouch == null) {
return;
@@ -1843,7 +1810,7 @@
@Override
public void onStatusBarTrackpadEvent(MotionEvent event) {
- mCentralSurfacesComponent.getNotificationPanelViewController().handleExternalTouch(event);
+ mShadeSurface.handleExternalTouch(event);
}
private void onExpandedInvisible() {
@@ -2210,10 +2177,6 @@
*/
@Override
public void setLockscreenUser(int newUserId) {
- if (mLockscreenWallpaper != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
- mLockscreenWallpaper.setCurrentUser(newUserId);
- }
- mScrimController.setCurrentUser(newUserId);
if (mWallpaperSupported) {
mWallpaperChangedReceiver.onReceive(mContext, null);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 4d716c2..680f19a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -45,7 +45,7 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterViewController;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.log.LogLevel;
+import com.android.systemui.log.core.LogLevel;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeViewStateProvider;
import com.android.systemui.statusbar.CommandQueue;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
index 5c357d7..686efb7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LSShadeTransitionLogger.kt
@@ -21,7 +21,7 @@
import com.android.internal.logging.nano.MetricsProto.MetricsEvent
import com.android.systemui.log.dagger.LSShadeTransitionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index c07b5e0..b2c39f7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -40,12 +40,17 @@
import com.android.internal.util.IndentingPrintWriter;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.CoreStartable;
import com.android.systemui.Dumpable;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.NotificationMediaManager;
+import com.android.systemui.user.data.model.SelectedUserModel;
+import com.android.systemui.user.data.model.SelectionStatus;
+import com.android.systemui.user.data.repository.UserRepository;
+import com.android.systemui.util.kotlin.JavaAdapter;
import libcore.io.IoUtils;
@@ -59,7 +64,7 @@
*/
@SysUISingleton
public class LockscreenWallpaper extends IWallpaperManagerCallback.Stub implements Runnable,
- Dumpable {
+ Dumpable, CoreStartable {
private static final String TAG = "LockscreenWallpaper";
@@ -72,6 +77,8 @@
private final WallpaperManager mWallpaperManager;
private final KeyguardUpdateMonitor mUpdateMonitor;
private final Handler mH;
+ private final JavaAdapter mJavaAdapter;
+ private final UserRepository mUserRepository;
private boolean mCached;
private Bitmap mCache;
@@ -88,6 +95,8 @@
DumpManager dumpManager,
NotificationMediaManager mediaManager,
@Main Handler mainHandler,
+ JavaAdapter javaAdapter,
+ UserRepository userRepository,
UserTracker userTracker) {
dumpManager.registerDumpable(getClass().getSimpleName(), this);
mWallpaperManager = wallpaperManager;
@@ -95,6 +104,8 @@
mUpdateMonitor = keyguardUpdateMonitor;
mMediaManager = mediaManager;
mH = mainHandler;
+ mJavaAdapter = javaAdapter;
+ mUserRepository = userRepository;
if (iWallpaperManager != null && !mWallpaperManager.isLockscreenLiveWallpaperEnabled()) {
// Service is disabled on some devices like Automotive
@@ -106,6 +117,14 @@
}
}
+ @Override
+ public void start() {
+ if (!isLockscreenLiveWallpaperEnabled()) {
+ mJavaAdapter.alwaysCollectFlow(
+ mUserRepository.getSelectedUser(), this::setSelectedUser);
+ }
+ }
+
public Bitmap getBitmap() {
assertLockscreenLiveWallpaperNotEnabled();
@@ -169,9 +188,15 @@
}
}
- public void setCurrentUser(int user) {
+ private void setSelectedUser(SelectedUserModel selectedUserModel) {
assertLockscreenLiveWallpaperNotEnabled();
+ if (selectedUserModel.getSelectionStatus().equals(SelectionStatus.SELECTION_IN_PROGRESS)) {
+ // Wait until the selection has finished before updating.
+ return;
+ }
+
+ int user = selectedUserModel.getUserInfo().id;
if (user != mCurrentUserId) {
if (mSelectedUser == null || user != mSelectedUser.getIdentifier()) {
mCached = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 47c4023..c16e13c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -1476,10 +1476,6 @@
}
}
- public void setCurrentUser(int currentUser) {
- // Don't care in the base class.
- }
-
private void updateThemeColors() {
if (mScrimBehind == null) return;
int background = Utils.getColorAttr(mScrimBehind.getContext(),
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
index 12f023b..d07378e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarNotificationActivityStarterLogger.kt
@@ -19,10 +19,10 @@
import android.app.PendingIntent
import com.android.systemui.log.dagger.NotifInteractionLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogLevel.ERROR
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.WARNING
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogLevel.ERROR
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.WARNING
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index c332280..604b1f5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -64,6 +64,7 @@
private boolean mNeedsUnderflow;
// Individual StatusBarIconViews draw their etc dots centered in this width
private int mIconDotFrameWidth;
+ private boolean mQsExpansionTransitioning;
private boolean mShouldRestrictIcons = true;
// Used to count which states want to be visible during layout
private ArrayList<StatusIconState> mLayoutStates = new ArrayList<>();
@@ -87,6 +88,10 @@
super.onFinishInflate();
}
+ public void setQsExpansionTransitioning(boolean expansionTransitioning) {
+ mQsExpansionTransitioning = expansionTransitioning;
+ }
+
public void setShouldRestrictIcons(boolean should) {
mShouldRestrictIcons = should;
}
@@ -386,6 +391,7 @@
StatusIconState vs = getViewStateFromChild(child);
if (vs != null) {
vs.applyToView(child);
+ vs.qsExpansionTransitioning = mQsExpansionTransitioning;
}
}
}
@@ -420,6 +426,7 @@
/// StatusBarIconView.STATE_*
public int visibleState = STATE_ICON;
public boolean justAdded = true;
+ public boolean qsExpansionTransitioning = false;
// How far we are from the end of the view actually is the most relevant for animation
float distanceToViewEnd = -1;
@@ -462,12 +469,13 @@
}
icon.setVisibleState(visibleState, animateVisibility);
- if (animationProperties != null) {
+ if (animationProperties != null && !qsExpansionTransitioning) {
animateTo(view, animationProperties);
} else {
super.applyToView(view);
}
+ qsExpansionTransitioning = false;
justAdded = false;
distanceToViewEnd = currentDistanceToEnd;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
index c618be8..4ae460a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesComponent.java
@@ -21,10 +21,8 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import com.android.systemui.scene.ui.view.WindowRootView;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.NotificationShadeWindowView;
import com.android.systemui.shade.NotificationShadeWindowViewController;
-import com.android.systemui.shade.QuickSettingsController;
import com.android.systemui.shade.ShadeHeaderController;
import com.android.systemui.statusbar.NotificationPresenter;
import com.android.systemui.statusbar.notification.NotificationActivityStarter;
@@ -89,14 +87,6 @@
NotificationShadeWindowViewController getNotificationShadeWindowViewController();
/**
- * Creates a NotificationPanelViewController.
- */
- NotificationPanelViewController getNotificationPanelViewController();
-
- /** Creates a QuickSettingsController. */
- QuickSettingsController getQuickSettingsController();
-
- /**
* Creates a StatusBarHeadsUpChangeListener.
*/
StatusBarHeadsUpChangeListener getStatusBarHeadsUpChangeListener();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
index 6b0746f..ebdde78 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/StatusBarViewModule.java
@@ -16,28 +16,20 @@
package com.android.systemui.statusbar.phone.dagger;
-import android.view.LayoutInflater;
-
import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.NotificationPanelView;
-import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.shade.ShadeExpansionStateManager;
import com.android.systemui.shade.ShadeViewController;
import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.OperatorNameViewController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
-import com.android.systemui.statusbar.phone.KeyguardBottomAreaView;
import com.android.systemui.statusbar.phone.NotificationIconAreaController;
-import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
import com.android.systemui.statusbar.phone.StatusBarHideIconsForBouncerManager;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.phone.StatusBarLocationPublisher;
-import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragmentLogger;
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent;
@@ -49,10 +41,8 @@
import com.android.systemui.util.CarrierConfigTracker;
import com.android.systemui.util.settings.SecureSettings;
-import dagger.Binds;
import dagger.Module;
import dagger.Provides;
-import dagger.multibindings.IntoSet;
import java.util.concurrent.Executor;
@@ -70,17 +60,6 @@
public static final String STATUS_BAR_FRAGMENT = "status_bar_fragment";
- /** */
- @Binds
- @CentralSurfacesComponent.CentralSurfacesScope
- abstract ShadeViewController bindsShadeViewController(
- NotificationPanelViewController notificationPanelViewController);
-
- @Binds
- @IntoSet
- abstract StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
- SystemBarAttributesListener systemBarAttributesListener);
-
/**
* Creates a new {@link CollapsedStatusBarFragment}.
*
@@ -145,17 +124,4 @@
statusBarWindowStateController,
keyguardUpdateMonitor);
}
-
- /**
- * Constructs a new, unattached {@link KeyguardBottomAreaView}.
- *
- * Note that this is explicitly _not_ a singleton, as we want to be able to reinflate it
- */
- @Provides
- public static KeyguardBottomAreaView providesKeyguardBottomAreaView(
- NotificationPanelView npv, LayoutInflater layoutInflater) {
- return (KeyguardBottomAreaView) layoutInflater.inflate(R
- .layout.keyguard_bottom_area, npv, false);
- }
-
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
index f4ab408..7cdb9c0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/CollapsedStatusBarFragmentLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.log.dagger.CollapsedSbFragmentLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.disableflags.DisableFlagsLogger
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index 730ecde..97a1bd1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -26,20 +26,23 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.StatusBarBoundsProvider;
+import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
+import dagger.Binds;
+import dagger.Module;
+import dagger.Provides;
+import dagger.multibindings.IntoSet;
+import dagger.multibindings.Multibinds;
+
import java.util.Optional;
import java.util.Set;
import javax.inject.Named;
-import dagger.Module;
-import dagger.Provides;
-import dagger.multibindings.Multibinds;
-
/** Dagger module for {@link StatusBarFragmentComponent}. */
@Module
public interface StatusBarFragmentModule {
@@ -151,4 +154,10 @@
/** */
@Multibinds
Set<StatusBarBoundsProvider.BoundsChangeListener> boundsChangeListeners();
+
+ /** */
+ @Binds
+ @IntoSet
+ StatusBarBoundsProvider.BoundsChangeListener sysBarAttrsListenerAsBoundsListener(
+ SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
index b3a1c40..051e88f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/MobileInputLogger.kt
@@ -23,7 +23,7 @@
import com.android.settingslib.mobile.MobileMappings
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.MobileInputLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
index 74352d29..54948a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryImpl.kt
@@ -250,15 +250,8 @@
.distinctUntilChanged()
.onEach { logger.logDefaultMobileIconGroup(it) }
- override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository {
- if (!isValidSubId(subId)) {
- throw IllegalArgumentException(
- "subscriptionId $subId is not in the list of valid subscriptions"
- )
- }
-
- return getOrCreateRepoForSubId(subId)
- }
+ override fun getRepoForSubId(subId: Int): FullMobileConnectionRepository =
+ getOrCreateRepoForSubId(subId)
private fun getOrCreateRepoForSubId(subId: Int) =
subIdRepositoryCache[subId]
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
index 7e0c145..cea6654 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/MobileViewLogger.kt
@@ -21,7 +21,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.MobileViewLog
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.LocationBasedMobileViewModel
import java.io.PrintWriter
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
index 507549b..f4c5723 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/VerboseMobileViewLogger.kt
@@ -20,7 +20,7 @@
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.VerboseMobileViewLog
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger.Companion.getIdForLogging
import com.android.systemui.statusbar.pipeline.mobile.ui.model.SignalIconModel
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
index cac0ae3..8a4d14e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityInputLogger.kt
@@ -20,7 +20,7 @@
import android.net.NetworkCapabilities
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.SharedConnectivityInputLog
import com.android.systemui.statusbar.pipeline.shared.data.model.DefaultConnectionModel
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
index 328d901..4b9de85 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/LoggerHelper.kt
@@ -19,7 +19,7 @@
import android.net.Network
import android.net.NetworkCapabilities
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** Helper object for logs that are shared between wifi and mobile. */
object LoggerHelper {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
index 058eda4..9a504c9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/shared/data/model/DefaultConnectionModel.kt
@@ -17,7 +17,7 @@
package com.android.systemui.statusbar.pipeline.shared.data.model
import android.net.NetworkCapabilities
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogMessage
/**
* A model for all of the current default connections(s).
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
index a4fbc2c..a57be66 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/demo/DemoWifiRepository.kt
@@ -96,7 +96,7 @@
networkId = DEMO_NET_ID,
isValidated = validated ?: true,
level = level ?: 0,
- ssid = ssid,
+ ssid = ssid ?: DEMO_NET_SSID,
// These fields below aren't supported in demo mode, since they aren't needed to satisfy
// the interface.
@@ -115,5 +115,6 @@
companion object {
private const val DEMO_NET_ID = 1234
+ private const val DEMO_NET_SSID = "Demo SSID"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
index 4a9ceac..f244376 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/shared/WifiInputLogger.kt
@@ -20,7 +20,7 @@
import android.net.NetworkCapabilities
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.pipeline.dagger.WifiInputLog
import com.android.systemui.statusbar.pipeline.shared.LoggerHelper
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
index 6ba2a81..096ad1f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/DeviceStateRotationLockSettingControllerLogger.kt
@@ -22,7 +22,7 @@
import android.provider.Settings.Secure.DEVICE_STATE_ROTATION_LOCK_UNLOCKED
import com.android.internal.R
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.log.dagger.DeviceStateAutoRotationLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
index 06ed1fd..175473f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManagerLogger.kt
@@ -18,8 +18,8 @@
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel.INFO
-import com.android.systemui.log.LogLevel.VERBOSE
+import com.android.systemui.log.core.LogLevel.INFO
+import com.android.systemui.log.core.LogLevel.VERBOSE
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
index 21d0338..cac5e32 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/SmartReplyStateInflater.kt
@@ -22,6 +22,13 @@
import android.app.RemoteInput
import android.content.Context
import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.ImageDecoder
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.graphics.drawable.Icon
import android.os.Build
import android.os.Bundle
import android.os.SystemClock
@@ -48,7 +55,13 @@
import com.android.systemui.statusbar.policy.SmartReplyView.SmartActions
import com.android.systemui.statusbar.policy.SmartReplyView.SmartButtonType
import com.android.systemui.statusbar.policy.SmartReplyView.SmartReplies
+import java.util.concurrent.FutureTask
+import java.util.concurrent.SynchronousQueue
+import java.util.concurrent.ThreadPoolExecutor
+import java.util.concurrent.TimeUnit
import javax.inject.Inject
+import kotlin.system.measureTimeMillis
+
/** Returns whether we should show the smart reply view and its smart suggestions. */
fun shouldShowSmartReplyView(
@@ -281,6 +294,51 @@
): Button
}
+private const val ICON_TASK_TIMEOUT_MS = 500L
+private val iconTaskThreadPool = ThreadPoolExecutor(0, 25, 1, TimeUnit.MINUTES, SynchronousQueue())
+
+private fun loadIconDrawableWithTimeout(
+ icon: Icon,
+ packageContext: Context,
+ targetSize: Int,
+): Drawable? {
+ if (icon.type != Icon.TYPE_URI && icon.type != Icon.TYPE_URI_ADAPTIVE_BITMAP) {
+ return icon.loadDrawable(packageContext)
+ }
+ val bitmapTask = FutureTask {
+ val bitmap: Bitmap?
+ val durationMillis = measureTimeMillis {
+ val source = ImageDecoder.createSource(packageContext.contentResolver, icon.uri)
+ bitmap = ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
+ decoder.setTargetSize(targetSize, targetSize)
+ decoder.allocator = ImageDecoder.ALLOCATOR_DEFAULT
+ }
+ }
+ if (durationMillis > ICON_TASK_TIMEOUT_MS) {
+ Log.w(TAG, "Loading $icon took ${durationMillis / 1000f} sec")
+ }
+ checkNotNull(bitmap) { "ImageDecoder.decodeBitmap() returned null" }
+ }
+ val bitmap = runCatching {
+ iconTaskThreadPool.execute(bitmapTask)
+ bitmapTask.get(ICON_TASK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
+ }.getOrElse { ex ->
+ Log.e(TAG, "Failed to load $icon: $ex")
+ bitmapTask.cancel(true)
+ return null
+ }
+ // TODO(b/288561520): rewrite Icon so that we don't need to duplicate this logic
+ val bitmapDrawable = BitmapDrawable(packageContext.resources, bitmap)
+ val result = if (icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP)
+ AdaptiveIconDrawable(null, bitmapDrawable) else bitmapDrawable
+ if (icon.hasTint()) {
+ result.mutate()
+ result.setTintList(icon.tintList)
+ result.setTintBlendMode(icon.tintBlendMode)
+ }
+ return result
+}
+
/* internal */ class SmartActionInflaterImpl @Inject constructor(
private val constants: SmartReplyConstants,
private val activityStarter: ActivityStarter,
@@ -304,12 +362,12 @@
// We received the Icon from the application - so use the Context of the application to
// reference icon resources.
- val iconDrawable = action.getIcon().loadDrawable(packageContext)
- .apply {
- val newIconSize: Int = context.resources.getDimensionPixelSize(
- R.dimen.smart_action_button_icon_size)
- setBounds(0, 0, newIconSize, newIconSize)
- }
+ val newIconSize = context.resources
+ .getDimensionPixelSize(R.dimen.smart_action_button_icon_size)
+ val iconDrawable =
+ loadIconDrawableWithTimeout(action.getIcon(), packageContext, newIconSize)
+ ?: GradientDrawable()
+ iconDrawable.setBounds(0, 0, newIconSize, newIconSize)
// Add the action icon to the Smart Action button.
setCompoundDrawablesRelative(iconDrawable, null, null, null)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
index b135d0d..1c3a8850 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/ZenModeControllerImpl.java
@@ -28,6 +28,7 @@
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerExecutor;
+import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Global;
@@ -122,7 +123,12 @@
userTracker.getUserId()) {
@Override
protected void handleValueChanged(int value, boolean observedChange) {
- updateZenModeConfig();
+ try {
+ Trace.beginSection("updateZenModeConfig");
+ updateZenModeConfig();
+ } finally {
+ Trace.endSection();
+ }
}
};
mNoMan = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
index bcf3b0c..4a9921e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java
@@ -240,8 +240,10 @@
Insets.of(0, safeTouchRegionHeight, 0, 0));
}
lp.providedInsets = new InsetsFrameProvider[] {
- new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars()),
- new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement()),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, statusBars())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
+ new InsetsFrameProvider(mInsetsSourceOwner, 0, tappableElement())
+ .setInsetsSize(Insets.of(0, height, 0, 0)),
gestureInsetsProvider
};
return lp;
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
index 066ac04..a9d2029 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/TemporaryViewLogger.kt
@@ -18,7 +18,7 @@
import android.view.View
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
/** A logger for temporary view changes -- see [TemporaryViewDisplayController]. */
open class TemporaryViewLogger<T : TemporaryViewInfo>(
diff --git a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
index d55751b..6706873 100644
--- a/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/temporarydisplay/chipbar/ChipbarLogger.kt
@@ -18,7 +18,7 @@
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.temporarydisplay.TemporaryViewLogger
import com.android.systemui.temporarydisplay.dagger.ChipbarLog
import javax.inject.Inject
diff --git a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
index dfe748a..c109eb4 100644
--- a/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/toast/ToastLogger.kt
@@ -18,9 +18,9 @@
import com.android.systemui.log.dagger.ToastLog
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
-import com.android.systemui.log.LogLevel.DEBUG
-import com.android.systemui.log.LogMessage
+import com.android.systemui.log.core.LogLevel
+import com.android.systemui.log.core.LogLevel.DEBUG
+import com.android.systemui.log.core.LogMessage
import javax.inject.Inject
private const val TAG = "ToastLog"
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
new file mode 100644
index 0000000..cefd466
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/user/data/model/SelectedUserModel.kt
@@ -0,0 +1,35 @@
+/*
+ * 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.user.data.model
+
+import android.content.pm.UserInfo
+
+/** A model for the currently selected user. */
+data class SelectedUserModel(
+ /** Information about the user. */
+ val userInfo: UserInfo,
+ /** The current status of the selection. */
+ val selectionStatus: SelectionStatus,
+)
+
+/** The current status of the selection. */
+enum class SelectionStatus {
+ /** This user has started being selected but the selection hasn't completed. */
+ SELECTION_IN_PROGRESS,
+ /** The selection of this user has completed. */
+ SELECTION_COMPLETE,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
index 3de75ca..954765c 100644
--- a/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/data/repository/UserRepository.kt
@@ -33,6 +33,8 @@
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.UserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.GlobalSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
@@ -69,6 +71,9 @@
/** List of all users on the device. */
val userInfos: Flow<List<UserInfo>>
+ /** Information about the currently-selected user, including [UserInfo] and other details. */
+ val selectedUser: StateFlow<SelectedUserModel>
+
/** [UserInfo] of the currently-selected user. */
val selectedUserInfo: Flow<UserInfo>
@@ -146,9 +151,6 @@
private val _userInfos = MutableStateFlow<List<UserInfo>?>(null)
override val userInfos: Flow<List<UserInfo>> = _userInfos.filterNotNull()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
-
override var mainUserId: Int = UserHandle.USER_NULL
private set
override var lastSelectedNonGuestUserId: Int = UserHandle.USER_NULL
@@ -174,12 +176,57 @@
override var isRefreshUsersPaused: Boolean = false
init {
- observeSelectedUser()
if (featureFlags.isEnabled(FACE_AUTH_REFACTOR)) {
observeUserSwitching()
}
}
+ override val selectedUser: StateFlow<SelectedUserModel> = run {
+ // Some callbacks don't modify the selection status, so maintain the current value.
+ var currentSelectionStatus = SelectionStatus.SELECTION_COMPLETE
+ conflatedCallbackFlow {
+ fun send(selectionStatus: SelectionStatus) {
+ currentSelectionStatus = selectionStatus
+ trySendWithFailureLogging(
+ SelectedUserModel(tracker.userInfo, selectionStatus),
+ TAG,
+ )
+ }
+
+ val callback =
+ object : UserTracker.Callback {
+ override fun onUserChanging(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ override fun onUserChanged(newUser: Int, userContext: Context) {
+ send(SelectionStatus.SELECTION_COMPLETE)
+ }
+
+ override fun onProfilesChanged(profiles: List<UserInfo>) {
+ send(currentSelectionStatus)
+ }
+ }
+
+ tracker.addCallback(callback, mainDispatcher.asExecutor())
+ send(currentSelectionStatus)
+
+ awaitClose { tracker.removeCallback(callback) }
+ }
+ .onEach {
+ if (!it.userInfo.isGuest) {
+ lastSelectedNonGuestUserId = it.userInfo.id
+ }
+ }
+ .stateIn(
+ applicationScope,
+ SharingStarted.Eagerly,
+ initialValue = SelectedUserModel(tracker.userInfo, currentSelectionStatus)
+ )
+ }
+
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
+
override fun refreshUsers() {
applicationScope.launch {
val result = withContext(backgroundDispatcher) { manager.aliveUsers }
@@ -201,7 +248,7 @@
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -234,38 +281,6 @@
.launchIn(applicationScope)
}
- private fun observeSelectedUser() {
- conflatedCallbackFlow {
- fun send() {
- trySendWithFailureLogging(tracker.userInfo, TAG)
- }
-
- val callback =
- object : UserTracker.Callback {
- override fun onUserChanging(newUser: Int, userContext: Context) {
- send()
- }
-
- override fun onProfilesChanged(profiles: List<UserInfo>) {
- send()
- }
- }
-
- tracker.addCallback(callback, mainDispatcher.asExecutor())
- send()
-
- awaitClose { tracker.removeCallback(callback) }
- }
- .onEach {
- if (!it.isGuest) {
- lastSelectedNonGuestUserId = it.id
- }
-
- _selectedUserInfo.value = it
- }
- .launchIn(applicationScope)
- }
-
private suspend fun getSettings(): UserSwitcherSettingsModel {
return withContext(backgroundDispatcher) {
val isSimpleUserSwitcher =
diff --git a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
index 891ee0c..e32ed5f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/kotlin/PackageManagerExt.kt
@@ -16,12 +16,19 @@
package com.android.systemui.util.kotlin
+import android.annotation.UserHandleAware
import android.annotation.WorkerThread
import android.content.pm.ComponentInfo
import android.content.pm.PackageManager
import com.android.systemui.util.Assert
+/**
+ * Determines whether a component is actually enabled (not just its default value).
+ *
+ * @throws IllegalArgumentException if the component is not found
+ */
@WorkerThread
+@UserHandleAware
fun PackageManager.isComponentActuallyEnabled(componentInfo: ComponentInfo): Boolean {
Assert.isNotMainThread()
return when (getComponentEnabledSetting(componentInfo.componentName)) {
diff --git a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
index 0926800..d69b10f 100644
--- a/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/wakelock/WakeLockLogger.kt
@@ -18,7 +18,7 @@
import android.os.PowerManager
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import javax.inject.Inject
class WakeLockLogger @Inject constructor(@WakeLockLog private val buffer: LogBuffer) {
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
index 7456d34..9362220 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumeDialogImpl.java
@@ -476,7 +476,8 @@
mWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mWindow.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
- mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ mWindow.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
diff --git a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
index 4c7e6b0..5144d19 100644
--- a/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
+++ b/packages/SystemUI/src/com/android/systemui/wmshell/WMShell.java
@@ -334,10 +334,8 @@
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, IBinder token, int vis,
+ int backDisposition, boolean showImeSwitcher) {
if (displayId == mDisplayTracker.getDefaultDisplayId()
&& (vis & InputMethodService.IME_VISIBLE) != 0) {
oneHanded.stopOneHanded(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
index 0dcd404..2318988 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/ClockEventControllerTest.kt
@@ -21,21 +21,22 @@
import android.widget.TextView
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.broadcast.BroadcastDispatcher
-import com.android.systemui.flags.FeatureFlags
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.broadcast.BroadcastDispatcher
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
+import com.android.systemui.log.LogBuffer
import com.android.systemui.plugins.ClockAnimations
import com.android.systemui.plugins.ClockController
import com.android.systemui.plugins.ClockEvents
-import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceConfig
+import com.android.systemui.plugins.ClockFaceController
import com.android.systemui.plugins.ClockFaceEvents
import com.android.systemui.plugins.ClockTickRate
-import com.android.systemui.log.LogBuffer
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.statusbar.policy.BatteryController
import com.android.systemui.statusbar.policy.ConfigurationController
@@ -64,7 +65,6 @@
import org.mockito.junit.MockitoJUnit
import java.util.TimeZone
import java.util.concurrent.Executor
-import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
import org.mockito.Mockito.`when` as whenever
@RunWith(AndroidTestingRunner::class)
@@ -122,7 +122,9 @@
bouncerRepository = bouncerRepository,
configurationRepository = FakeConfigurationRepository(),
),
- KeyguardTransitionInteractor(transitionRepository, TestScope().backgroundScope),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
broadcastDispatcher,
batteryController,
keyguardUpdateMonitor,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b21cc6d..9e561ed 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -408,4 +408,18 @@
any(ClockRegistry.ClockChangeListener.class));
verify(mClockEventController, times).registerListeners(mView);
}
+
+ @Test
+ public void testSplitShadeEnabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(true);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void testSplitShadeDisabledSetToSmartspaceController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(1)).setSplitShadeEnabled(false);
+ verify(mSmartspaceController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
index 5a56baf..d3b4190 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardPinViewControllerTest.kt
@@ -97,7 +97,21 @@
`when`(keyguardPinView.findViewById<NumPadButton>(R.id.delete_button))
.thenReturn(deleteButton)
`when`(keyguardPinView.findViewById<View>(R.id.key_enter)).thenReturn(enterButton)
- constructViewController()
+ pinViewController =
+ KeyguardPinViewController(
+ keyguardPinView,
+ keyguardUpdateMonitor,
+ securityMode,
+ lockPatternUtils,
+ mKeyguardSecurityCallback,
+ keyguardMessageAreaControllerFactory,
+ mLatencyTracker,
+ liftToActivateListener,
+ mEmergencyButtonController,
+ falsingCollector,
+ postureController,
+ featureFlags
+ )
}
@Test
@@ -121,10 +135,8 @@
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(3)
`when`(passwordTextView.text).thenReturn("")
- constructViewController()
pinViewController.startAppearAnimation()
-
verify(deleteButton).visibility = View.INVISIBLE
verify(enterButton).visibility = View.INVISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -138,10 +150,8 @@
`when`(lockPatternUtils.isAutoPinConfirmEnabled(anyInt())).thenReturn(true)
`when`(lockPatternUtils.getCurrentFailedPasswordAttempts(anyInt())).thenReturn(6)
`when`(passwordTextView.text).thenReturn("")
- constructViewController()
pinViewController.startAppearAnimation()
-
verify(deleteButton).visibility = View.VISIBLE
verify(enterButton).visibility = View.VISIBLE
verify(passwordTextView).setUsePinShapes(true)
@@ -153,22 +163,4 @@
pinViewController.handleAttemptLockout(0)
verify(lockPatternUtils).getCurrentFailedPasswordAttempts(anyInt())
}
-
- fun constructViewController() {
- pinViewController =
- KeyguardPinViewController(
- keyguardPinView,
- keyguardUpdateMonitor,
- securityMode,
- lockPatternUtils,
- mKeyguardSecurityCallback,
- keyguardMessageAreaControllerFactory,
- mLatencyTracker,
- liftToActivateListener,
- mEmergencyButtonController,
- falsingCollector,
- postureController,
- featureFlags
- )
- }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index e561f1f..58b1edc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -62,12 +62,12 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.SideFpsController;
import com.android.systemui.biometrics.SideFpsUiRequestSource;
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FakeFeatureFlags;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
-import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
@@ -395,6 +395,7 @@
// WHEN a request is made from the SimPin screens to show the next security method
when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(true);
mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
/* authenticated= */true,
TARGET_USER_ID,
@@ -423,6 +424,28 @@
}
@Test
+ public void showNextSecurityScreenOrFinish_SimPin_Swipe() {
+ // GIVEN the current security method is SimPin
+ when(mKeyguardUpdateMonitor.getUserHasTrust(anyInt())).thenReturn(false);
+ when(mKeyguardUpdateMonitor.getUserUnlockedWithBiometric(TARGET_USER_ID)).thenReturn(false);
+ mKeyguardSecurityContainerController.showSecurityScreen(SecurityMode.SimPin);
+
+ // WHEN a request is made from the SimPin screens to show the next security method
+ when(mKeyguardSecurityModel.getSecurityMode(TARGET_USER_ID)).thenReturn(SecurityMode.None);
+ // WHEN security method is SWIPE
+ when(mLockPatternUtils.isLockScreenDisabled(anyInt())).thenReturn(false);
+ mKeyguardSecurityContainerController.showNextSecurityScreenOrFinish(
+ /* authenticated= */true,
+ TARGET_USER_ID,
+ /* bypassSecondaryLockScreen= */true,
+ SecurityMode.SimPin);
+
+ // THEN the next security method of None will dismiss keyguard.
+ verify(mViewMediatorCallback, never()).keyguardDone(anyBoolean(), anyInt());
+ }
+
+
+ @Test
public void onSwipeUp_whenFaceDetectionIsNotRunning_initiatesFaceAuth() {
KeyguardSecurityContainer.SwipeListener registeredSwipeListener =
getRegisteredSwipeListener();
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
index a2c6329..512e5dc 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardStatusViewControllerTest.java
@@ -17,6 +17,7 @@
package com.android.keyguard;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -155,4 +156,18 @@
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_X, 20f, true);
verify(mControllerMock).setProperty(AnimatableProperty.SCALE_Y, 20f, true);
}
+
+ @Test
+ public void splitShadeEnabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(true);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(false);
+ }
+
+ @Test
+ public void splitShadeDisabledPassedToClockSwitchController() {
+ mController.setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(1)).setSplitShadeEnabled(false);
+ verify(mKeyguardClockSwitchController, times(0)).setSplitShadeEnabled(true);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 4263091..5abab62 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -3002,6 +3002,16 @@
}
@Test
+ public void testOnSimStateChanged_HandleSimStateNotReady() {
+ KeyguardUpdateMonitorCallback keyguardUpdateMonitorCallback = spy(
+ KeyguardUpdateMonitorCallback.class);
+ mKeyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.handleSimStateChange(-1, 0, TelephonyManager.SIM_STATE_NOT_READY);
+ verify(keyguardUpdateMonitorCallback).onSimStateChanged(-1, 0,
+ TelephonyManager.SIM_STATE_NOT_READY);
+ }
+
+ @Test
public void onAuthEnrollmentChangesCallbacksAreNotified() {
KeyguardUpdateMonitorCallback callback = mock(KeyguardUpdateMonitorCallback.class);
ArgumentCaptor<AuthController.Callback> authCallback = ArgumentCaptor.forClass(
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
index c88c4d6..e7e1cc9 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/LockIconViewControllerBaseTest.java
@@ -19,6 +19,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.flags.Flags.DOZING_MIGRATION_1;
import static com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR;
+import static com.android.systemui.flags.Flags.MIGRATE_LOCK_ICON;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
@@ -143,6 +144,7 @@
mFeatureFlags = new FakeFeatureFlags();
mFeatureFlags.set(FACE_AUTH_REFACTOR, false);
+ mFeatureFlags.set(MIGRATE_LOCK_ICON, false);
mUnderTest = new LockIconViewController(
mLockIconView,
mStatusBarStateController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 79c87cf..796e665 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -96,6 +96,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.settings.FakeDisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.commandline.CommandRegistry;
import com.android.systemui.statusbar.events.PrivacyDotViewController;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.concurrency.FakeThreadFactory;
@@ -139,6 +140,8 @@
@Mock
private Display mDisplay;
@Mock
+ private CommandRegistry mCommandRegistry;
+ @Mock
private UserTracker mUserTracker;
@Mock
private PrivacyDotViewController mDotViewController;
@@ -231,8 +234,9 @@
mExecutor,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer"))));
- mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
- mUserTracker, mDisplayTracker, mDotViewController, mThreadFactory,
+ mScreenDecorations = spy(new ScreenDecorations(mContext, mSecureSettings,
+ mCommandRegistry, mUserTracker, mDisplayTracker, mDotViewController,
+ mThreadFactory,
mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")),
mAuthController) {
@@ -1226,8 +1230,9 @@
mFaceScanningProviders.add(mFaceScanningDecorProvider);
when(mFaceScanningProviderFactory.getProviders()).thenReturn(mFaceScanningProviders);
when(mFaceScanningProviderFactory.getHasProviders()).thenReturn(true);
- ScreenDecorations screenDecorations = new ScreenDecorations(mContext, mExecutor,
- mSecureSettings, mUserTracker, mDisplayTracker, mDotViewController,
+ ScreenDecorations screenDecorations = new ScreenDecorations(mContext,
+ mSecureSettings, mCommandRegistry, mUserTracker, mDisplayTracker,
+ mDotViewController,
mThreadFactory, mPrivacyDotDecorProviderFactory, mFaceScanningProviderFactory,
new ScreenDecorationsLogger(logcatLogBuffer("TestLogBuffer")), mAuthController);
screenDecorations.start();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index 2248755..0e0d0e3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -43,11 +43,12 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -58,6 +59,7 @@
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
@@ -70,6 +72,7 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
+import javax.inject.Provider
import org.mockito.Mockito.`when` as whenever
private const val REQUEST_ID = 2L
@@ -80,6 +83,7 @@
private const val SENSOR_WIDTH = 30
private const val SENSOR_HEIGHT = 60
+@ExperimentalCoroutinesApi
@SmallTest
@RoboPilotTest
@RunWith(AndroidJUnit4::class)
@@ -116,6 +120,7 @@
@Mock private lateinit var udfpsUtils: UdfpsUtils
@Mock private lateinit var udfpsKeyguardAccessibilityDelegate:
UdfpsKeyguardAccessibilityDelegate
+ @Mock private lateinit var udfpsKeyguardViewModels: Provider<UdfpsKeyguardViewModels>
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -148,6 +153,7 @@
controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
primaryBouncerInteractor, alternateBouncerInteractor, isDebuggable, udfpsUtils,
udfpsKeyguardAccessibilityDelegate,
+ udfpsKeyguardViewModels,
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 30e5447..58982d1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -22,6 +22,7 @@
import static android.view.MotionEvent.ACTION_UP;
import static com.android.internal.util.FunctionalUtils.ThrowingConsumer;
+import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
@@ -59,6 +60,7 @@
import android.os.RemoteException;
import android.os.VibrationAttributes;
import android.testing.TestableLooper.RunWithLooper;
+import android.util.Pair;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.Surface;
@@ -83,13 +85,14 @@
import com.android.systemui.biometrics.udfps.NormalizedTouchData;
import com.android.systemui.biometrics.udfps.SinglePointerTouchProcessor;
import com.android.systemui.biometrics.udfps.TouchProcessorResult;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
+import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.ScreenLifecycle;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
-import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
+import com.android.systemui.keyguard.ui.viewmodel.UdfpsKeyguardViewModels;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -219,6 +222,8 @@
private SecureSettings mSecureSettings;
@Mock
private UdfpsKeyguardAccessibilityDelegate mUdfpsKeyguardAccessibilityDelegate;
+ @Mock
+ private Provider<UdfpsKeyguardViewModels> mUdfpsKeyguardViewModels;
// Capture listeners so that they can be used to send events
@Captor
@@ -318,7 +323,7 @@
mPrimaryBouncerInteractor, mSinglePointerTouchProcessor, mSessionTracker,
mAlternateBouncerInteractor, mSecureSettings, mInputManager, mUdfpsUtils,
mock(KeyguardFaceAuthInteractor.class),
- mUdfpsKeyguardAccessibilityDelegate);
+ mUdfpsKeyguardAccessibilityDelegate, mUdfpsKeyguardViewModels);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
@@ -1200,8 +1205,53 @@
}
@Test
+ public void fingerDown_falsingManagerInformed() throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> touchProcessorResult =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ touchProcessorResult.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // THEN falsing manager is informed of the touch
+ verify(mFalsingManager).isFalseTouch(UDFPS_AUTHENTICATION);
+ }
+
+ @Test
public void onTouch_withNewTouchDetection_shouldCallNewFingerprintManagerPath()
throws RemoteException {
+ final Pair<TouchProcessorResult, TouchProcessorResult> processorResultDownAndUp =
+ givenAcceptFingerDownEvent();
+
+ // WHEN ACTION_DOWN is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.first);
+ MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
+ mBiometricExecutor.runAllReady();
+ downEvent.recycle();
+
+ // AND ACTION_UP is received
+ when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
+ processorResultDownAndUp.second);
+ MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
+ mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
+ mBiometricExecutor.runAllReady();
+ upEvent.recycle();
+
+ // THEN the new FingerprintManager path is invoked.
+ verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
+ anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ }
+
+ private Pair<TouchProcessorResult, TouchProcessorResult> givenAcceptFingerDownEvent()
+ throws RemoteException {
final NormalizedTouchData touchData = new NormalizedTouchData(0, 0f, 0f, 0f, 0f, 0f, 0L,
0L);
final TouchProcessorResult processorResultDown = new TouchProcessorResult.ProcessedTouch(
@@ -1227,27 +1277,7 @@
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
- // WHEN ACTION_DOWN is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultDown);
- MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
- mBiometricExecutor.runAllReady();
- downEvent.recycle();
-
- // AND ACTION_UP is received
- when(mSinglePointerTouchProcessor.processTouch(any(), anyInt(), any())).thenReturn(
- processorResultUp);
- MotionEvent upEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_UP, 0, 0, 0);
- mTouchListenerCaptor.getValue().onTouch(mUdfpsView, upEvent);
- mBiometricExecutor.runAllReady();
- upEvent.recycle();
-
- // THEN the new FingerprintManager path is invoked.
- verify(mFingerprintManager).onPointerDown(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
- verify(mFingerprintManager).onPointerUp(anyLong(), anyInt(), anyInt(), anyFloat(),
- anyFloat(), anyFloat(), anyFloat(), anyFloat(), anyLong(), anyLong(), anyBoolean());
+ return new Pair<>(processorResultDown, processorResultUp);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
index 263ce1a..8f8004f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/domain/interactor/LogContextInteractorImplTest.kt
@@ -7,7 +7,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -54,10 +54,11 @@
LogContextInteractorImpl(
testScope.backgroundScope,
foldProvider,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ repository = keyguardTransitionRepository,
+ scope = testScope.backgroundScope,
+ )
+ .keyguardTransitionInteractor,
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
index bd029a7..a341ca3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/dump/LogBufferHelper.kt
@@ -17,7 +17,7 @@
package com.android.systemui.dump
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.LogcatEchoTracker
/**
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index 6f7c217..666978e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -205,6 +205,8 @@
when(mStatusBarKeyguardViewManager.getViewRootImpl()).thenReturn(testViewRoot);
when(mDreamingToLockscreenTransitionViewModel.getDreamOverlayAlpha())
.thenReturn(mock(Flow.class));
+ when(mDreamingToLockscreenTransitionViewModel.getTransitionEnded())
+ .thenReturn(mock(Flow.class));
mNotificationShadeWindowController = new NotificationShadeWindowControllerImpl(mContext,
mWindowManager, mActivityManager, mDozeParameters, mStatusBarStateController,
mConfigurationController, mViewMediator, mKeyguardBypassController,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
index b4bd473..925ac30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ResourceTrimmerTest.kt
@@ -10,7 +10,7 @@
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
@@ -67,10 +67,11 @@
resourceTrimmer =
ResourceTrimmer(
keyguardInteractor,
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
globalWindowManager,
testScope.backgroundScope,
testDispatcher,
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
index e042564..d62db5d 100644
--- 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
@@ -49,7 +49,7 @@
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
@@ -216,7 +216,11 @@
)
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
index ee5c1cc3..3e81cd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardFaceAuthInteractorTest.kt
@@ -89,7 +89,11 @@
faceAuthRepository = FakeDeviceEntryFaceAuthRepository()
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest =
SystemUIKeyguardFaceAuthInteractor(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
index e9c22f9..0050d64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardLongPressInteractorTest.kt
@@ -292,10 +292,11 @@
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- keyguardTransitionRepository,
- testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
repository = keyguardRepository,
logger = logger,
featureFlags =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index fa4941c..9e9c25e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -17,9 +17,9 @@
package com.android.systemui.keyguard.domain.interactor
-import com.android.systemui.RoboPilotTest
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
@@ -54,7 +54,10 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- underTest = KeyguardTransitionInteractor(repository, testScope.backgroundScope)
+ underTest = KeyguardTransitionInteractorFactory.create(
+ scope = testScope.backgroundScope,
+ repository = repository,
+ ).keyguardTransitionInteractor
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
index b559015..d01a46e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionScenariosTest.kt
@@ -19,6 +19,7 @@
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardSecurityModel
import com.android.keyguard.KeyguardSecurityModel.SecurityMode.PIN
+import com.android.keyguard.TestScopeProvider
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
import com.android.systemui.flags.FakeFeatureFlags
@@ -92,76 +93,97 @@
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- testScope = TestScope()
+ testScope = TestScopeProvider.getTestScope()
keyguardRepository = FakeKeyguardRepository()
bouncerRepository = FakeKeyguardBouncerRepository()
shadeRepository = FakeShadeRepository()
transitionRepository = spy(FakeKeyguardTransitionRepository())
- transitionInteractor = KeyguardTransitionInteractor(
- transitionRepository, testScope.backgroundScope)
whenever(keyguardSecurityModel.getSecurityMode(anyInt())).thenReturn(PIN)
featureFlags = FakeFeatureFlags().apply { set(Flags.FACE_AUTH_REFACTOR, true) }
- fromLockscreenTransitionInteractor = FromLockscreenTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- shadeRepository = shadeRepository,
- ).apply { start() }
+ transitionInteractor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = testScope,
+ repository = transitionRepository,
+ )
+ .keyguardTransitionInteractor
- fromPrimaryBouncerTransitionInteractor = FromPrimaryBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- keyguardSecurityModel = keyguardSecurityModel,
- ).apply { start() }
+ fromLockscreenTransitionInteractor =
+ FromLockscreenTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ shadeRepository = shadeRepository,
+ )
+ .apply { start() }
- fromDreamingTransitionInteractor = FromDreamingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromPrimaryBouncerTransitionInteractor =
+ FromPrimaryBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ keyguardSecurityModel = keyguardSecurityModel,
+ )
+ .apply { start() }
- fromAodTransitionInteractor = FromAodTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromDreamingTransitionInteractor =
+ FromDreamingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromGoneTransitionInteractor = FromGoneTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromAodTransitionInteractor =
+ FromAodTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromDozingTransitionInteractor = FromDozingTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromGoneTransitionInteractor =
+ FromGoneTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromOccludedTransitionInteractor = FromOccludedTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromDozingTransitionInteractor =
+ FromDozingTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
- fromAlternateBouncerTransitionInteractor = FromAlternateBouncerTransitionInteractor(
- scope = testScope,
- keyguardInteractor = createKeyguardInteractor(),
- transitionRepository = transitionRepository,
- transitionInteractor = transitionInteractor,
- ).apply { start() }
+ fromOccludedTransitionInteractor =
+ FromOccludedTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
+
+ fromAlternateBouncerTransitionInteractor =
+ FromAlternateBouncerTransitionInteractor(
+ scope = testScope,
+ keyguardInteractor = createKeyguardInteractor(),
+ transitionRepository = transitionRepository,
+ transitionInteractor = transitionInteractor,
+ )
+ .apply { start() }
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
index 08e99dc..6e7ba6d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -46,7 +46,11 @@
private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
private val keyguardTransitionInteractor =
- KeyguardTransitionInteractor(fakeKeyguardTransitionRepository, TestScope().backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = fakeKeyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
private lateinit var underTest: LightRevealScrimInteractor
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
new file mode 100644
index 0000000..1baca21
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/UdfpsKeyguardInteractorTest.kt
@@ -0,0 +1,168 @@
+/*
+ * 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.domain.interactor
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.shared.model.StatusBarState
+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.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.ArgumentMatchers.anyInt
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsKeyguardInteractorTest : SysuiTestCase() {
+ private val burnInProgress = 1f
+ private val burnInYOffset = 20
+ private val burnInXOffset = 10
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var burnInInteractor: BurnInInteractor
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ private lateinit var underTest: UdfpsKeyguardInteractor
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+ burnInInteractor =
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ )
+
+ underTest =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ burnInInteractor,
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+ }
+
+ @Test
+ fun dozeChanges_updatesUdfpsAodModel() =
+ testScope.runTest {
+ val burnInOffsets by collectLastValue(underTest.burnInOffsets)
+ initializeBurnInOffsets()
+
+ // WHEN we're not dozing
+ setAwake()
+ runCurrent()
+
+ // THEN burn in offsets are 0
+ assertThat(burnInOffsets?.burnInProgress).isEqualTo(0f)
+ assertThat(burnInOffsets?.burnInYOffset).isEqualTo(0)
+ assertThat(burnInOffsets?.burnInXOffset).isEqualTo(0)
+
+ // WHEN we're in the middle of the doze amount change
+ keyguardRepository.setDozeAmount(.50f)
+ runCurrent()
+
+ // THEN burn in is updated (between 0 and the full offset)
+ assertThat(burnInOffsets?.burnInProgress).isGreaterThan(0f)
+ assertThat(burnInOffsets?.burnInYOffset).isGreaterThan(0)
+ assertThat(burnInOffsets?.burnInXOffset).isGreaterThan(0)
+ assertThat(burnInOffsets?.burnInProgress).isLessThan(burnInProgress)
+ assertThat(burnInOffsets?.burnInYOffset).isLessThan(burnInYOffset)
+ assertThat(burnInOffsets?.burnInXOffset).isLessThan(burnInXOffset)
+
+ // WHEN we're fully dozing
+ keyguardRepository.setDozeAmount(1f)
+ runCurrent()
+
+ // THEN burn in offsets are updated to final current values (for the given time)
+ assertThat(burnInOffsets?.burnInProgress).isEqualTo(burnInProgress)
+ assertThat(burnInOffsets?.burnInYOffset).isEqualTo(burnInYOffset)
+ assertThat(burnInOffsets?.burnInXOffset).isEqualTo(burnInXOffset)
+ }
+
+ private fun initializeBurnInOffsets() {
+ whenever(burnInHelper.burnInProgressOffset()).thenReturn(burnInProgress)
+ whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(true)))
+ .thenReturn(burnInXOffset)
+ whenever(burnInHelper.burnInOffset(anyInt(), /* xAxis */ eq(false)))
+ .thenReturn(burnInYOffset)
+ }
+
+ private fun setAwake() {
+ keyguardRepository.setDozeAmount(0f)
+ burnInInteractor.dozeTimeTick()
+
+ bouncerRepository.setAlternateVisible(false)
+ keyguardRepository.setStatusBarState(StatusBarState.KEYGUARD)
+ bouncerRepository.setPrimaryShow(false)
+ keyguardRepository.setWakefulnessModel(
+ WakefulnessModel(
+ WakefulnessState.AWAKE,
+ WakeSleepReason.POWER_BUTTON,
+ WakeSleepReason.POWER_BUTTON,
+ )
+ )
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
new file mode 100644
index 0000000..2e97208
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/DefaultLockscreenLayoutTest.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import android.graphics.Point
+import android.view.ViewGroup
+import android.view.WindowManager
+import androidx.constraintlayout.widget.ConstraintSet
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.monet.utils.ArgbSubject.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Answers
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class DefaultLockscreenLayoutTest : SysuiTestCase() {
+ private lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ private lateinit var rootView: KeyguardRootView
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var windowManager: WindowManager
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ rootView = KeyguardRootView(context, null)
+ defaultLockscreenLayout =
+ DefaultLockscreenLayout(authController, keyguardUpdateMonitor, windowManager, context)
+ }
+
+ @Test
+ fun testLayoutViews_KeyguardIndicationArea() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.keyguard_indication_area)
+ assertThat(constraint.layout.bottomToBottom).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.endToEnd).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.mWidth).isEqualTo(ViewGroup.LayoutParams.MATCH_PARENT)
+ assertThat(constraint.layout.mHeight).isEqualTo(ViewGroup.LayoutParams.WRAP_CONTENT)
+ }
+
+ @Test
+ fun testLayoutViews_lockIconView() {
+ defaultLockscreenLayout.layoutViews(rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ }
+
+ @Test
+ fun testCenterLockIcon() {
+ defaultLockscreenLayout.centerLockIcon(Point(5, 6), 1F, 5, rootView)
+ val constraint = getViewConstraint(R.id.lock_icon_view)
+
+ assertThat(constraint.layout.mWidth).isEqualTo(2)
+ assertThat(constraint.layout.mHeight).isEqualTo(2)
+ assertThat(constraint.layout.topToTop).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.startToStart).isEqualTo(ConstraintSet.PARENT_ID)
+ assertThat(constraint.layout.topMargin).isEqualTo(5)
+ assertThat(constraint.layout.startMargin).isEqualTo(4)
+ }
+
+ /** Get the ConstraintLayout constraint of the view. */
+ private fun getViewConstraint(viewId: Int): ConstraintSet.Constraint {
+ val constraintSet = ConstraintSet()
+ constraintSet.clone(rootView)
+ return constraintSet.getConstraint(viewId)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
new file mode 100644
index 0000000..145b2fd
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerCommandListenerTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.commandline.Command
+import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import java.io.PrintWriter
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.ArgumentMatchers.anyString
+import org.mockito.Mock
+import org.mockito.Mockito.atLeastOnce
+import org.mockito.Mockito.mock
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerCommandListenerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManagerCommandListener: KeyguardLayoutManagerCommandListener
+ @Mock private lateinit var commandRegistry: CommandRegistry
+ @Mock private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock private lateinit var pw: PrintWriter
+ private lateinit var command: () -> Command
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ keyguardLayoutManagerCommandListener =
+ KeyguardLayoutManagerCommandListener(
+ commandRegistry,
+ keyguardLayoutManager,
+ )
+ keyguardLayoutManagerCommandListener.start()
+ command =
+ withArgCaptor<() -> Command> {
+ verify(commandRegistry).registerCommand(eq("layout"), capture())
+ }
+ }
+
+ @Test
+ fun testHelp() {
+ command().execute(pw, listOf("help"))
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testBlank() {
+ command().execute(pw, listOf())
+ verify(pw, atLeastOnce()).println(anyString())
+ verify(keyguardLayoutManager, never()).transitionToLayout(anyString())
+ }
+
+ @Test
+ fun testValidArg() {
+ bindFakeIdMapToLayoutManager()
+ command().execute(pw, listOf("fake"))
+ verify(keyguardLayoutManager).transitionToLayout("fake")
+ }
+
+ private fun bindFakeIdMapToLayoutManager() {
+ val map = mapOf("fake" to mock(LockscreenLayout::class.java))
+ whenever(keyguardLayoutManager.layoutIdMap).thenReturn(map)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
new file mode 100644
index 0000000..95b2030
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/view/layout/KeyguardLayoutManagerTest.kt
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.ui.view.layout
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.ui.view.KeyguardRootView
+import com.android.systemui.keyguard.ui.view.layout.DefaultLockscreenLayout.Companion.DEFAULT
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.Mock
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+class KeyguardLayoutManagerTest : SysuiTestCase() {
+ private lateinit var keyguardLayoutManager: KeyguardLayoutManager
+ @Mock lateinit var configurationController: ConfigurationController
+ @Mock lateinit var defaultLockscreenLayout: DefaultLockscreenLayout
+ @Mock lateinit var keyguardRootView: KeyguardRootView
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(defaultLockscreenLayout.id).thenReturn(DEFAULT)
+ keyguardLayoutManager =
+ KeyguardLayoutManager(
+ configurationController,
+ setOf(defaultLockscreenLayout),
+ keyguardRootView
+ )
+ }
+
+ @Test
+ fun testDefaultLayout() {
+ keyguardLayoutManager.transitionToLayout(DEFAULT)
+ verify(defaultLockscreenLayout).layoutViews(keyguardRootView)
+ }
+
+ @Test
+ fun testTransitionToLayout_validId() {
+ assertThat(keyguardLayoutManager.transitionToLayout(DEFAULT)).isTrue()
+ }
+ @Test
+ fun testTransitionToLayout_invalidId() {
+ assertThat(keyguardLayoutManager.transitionToLayout("abc")).isFalse()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
index a341346..c67f535 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/DreamingToLockscreenTransitionViewModelTest.kt
@@ -22,8 +22,18 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
+import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
+import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
+import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
+import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionState.CANCELED
+import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
+import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
+import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.mockito.mock
import com.google.common.collect.Range
@@ -47,7 +57,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = DreamingToLockscreenTransitionViewModel(interactor, mock())
}
@@ -60,7 +75,7 @@
val job =
underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -82,7 +97,7 @@
val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
// Should start running here...
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.5f))
@@ -104,7 +119,7 @@
val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.1f))
repository.sendTransitionStep(step(0.2f))
@@ -126,7 +141,7 @@
val job =
underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
- repository.sendTransitionStep(step(0f, TransitionState.STARTED))
+ repository.sendTransitionStep(step(0f, STARTED))
repository.sendTransitionStep(step(0f))
repository.sendTransitionStep(step(0.3f))
repository.sendTransitionStep(step(0.5f))
@@ -138,13 +153,44 @@
job.cancel()
}
- private fun step(
- value: Float,
- state: TransitionState = TransitionState.RUNNING
- ): TransitionStep {
+ @Test
+ fun transitionEnded() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<TransitionStep>()
+
+ val job = underTest.transitionEnded.onEach { values.add(it) }.launchIn(this)
+
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.1f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 1.0f, FINISHED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.5f, RUNNING))
+ repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 1.0f, CANCELED))
+
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 0.0f, STARTED))
+ repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 1.0f, FINISHED))
+
+ assertThat(values.size).isEqualTo(3)
+ values.forEach {
+ assertThat(it.transitionState == FINISHED || it.transitionState == CANCELED)
+ .isTrue()
+ }
+
+ job.cancel()
+ }
+
+ private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
return TransitionStep(
- from = KeyguardState.DREAMING,
- to = KeyguardState.LOCKSCREEN,
+ from = DREAMING,
+ to = LOCKSCREEN,
value = value,
transitionState = state,
ownerName = "DreamingToLockscreenTransitionViewModelTest"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
index 694539b..75c8bff 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/GoneToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = GoneToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
index 29886d5..d02b3fc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBottomAreaViewModelTest.kt
@@ -41,13 +41,12 @@
import com.android.systemui.keyguard.data.quickaffordance.KeyguardQuickAffordanceRemoteUserSelectionManager
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.data.repository.KeyguardQuickAffordanceRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractorFactory
import com.android.systemui.keyguard.domain.interactor.KeyguardLongPressInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardQuickAffordanceInteractor
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.domain.quickaffordance.FakeKeyguardQuickAffordanceRegistry
import com.android.systemui.keyguard.shared.quickaffordance.ActivationState
import com.android.systemui.keyguard.shared.quickaffordance.KeyguardQuickAffordancePosition
@@ -210,10 +209,10 @@
appContext = mContext,
scope = testScope.backgroundScope,
transitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
repository = repository,
logger = UiEventLoggerFake(),
featureFlags = featureFlags,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
index ea17751..12fe07f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToDreamingTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToDreamingTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
index bf56a98..83ae631 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenToOccludedTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = LockscreenToOccludedTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
index 34da26e..8860399 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/OccludedToLockscreenTransitionViewModelTest.kt
@@ -21,7 +21,7 @@
import com.android.systemui.RoboPilotTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -46,7 +46,12 @@
@Before
fun setUp() {
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest = OccludedToLockscreenTransitionViewModel(interactor)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
index f88b71d..d8c78eb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/PrimaryBouncerToGoneTransitionViewModelTest.kt
@@ -22,7 +22,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.ScrimAlpha
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -55,7 +55,12 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
repository = FakeKeyguardTransitionRepository()
- val interactor = KeyguardTransitionInteractor(repository, TestScope().backgroundScope)
+ val interactor =
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = repository,
+ )
+ .keyguardTransitionInteractor
underTest =
PrimaryBouncerToGoneTransitionViewModel(
interactor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
new file mode 100644
index 0000000..436c09c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsAodViewModelTest.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.RoboPilotTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@ExperimentalCoroutinesApi
+@SmallTest
+@RoboPilotTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsAodViewModelTest : SysuiTestCase() {
+ private val defaultPadding = 12
+ private lateinit var underTest: UdfpsAodViewModel
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+
+ val udfpsKeyguardInteractor =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+
+ underTest =
+ UdfpsAodViewModel(
+ udfpsKeyguardInteractor,
+ context,
+ )
+ }
+
+ @Test
+ fun alphaAndVisibleUpdates_onDozeAmountChanges() =
+ testScope.runTest {
+ val alpha by collectLastValue(underTest.alpha)
+ val visible by collectLastValue(underTest.isVisible)
+
+ keyguardRepository.setDozeAmount(0f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ keyguardRepository.setDozeAmount(.65f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(.65f)
+ assertThat(visible).isTrue()
+
+ keyguardRepository.setDozeAmount(.23f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(.23f)
+ assertThat(visible).isTrue()
+
+ keyguardRepository.setDozeAmount(1f)
+ runCurrent()
+ assertThat(alpha).isEqualTo(1f)
+ assertThat(visible).isTrue()
+ }
+
+ @Test
+ fun paddingUpdates_onScaleForResolutionChanges() =
+ testScope.runTest {
+ val padding by collectLastValue(underTest.padding)
+
+ configRepository.setScaleForResolution(1f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding)
+
+ configRepository.setScaleForResolution(2f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding * 2)
+
+ configRepository.setScaleForResolution(.5f)
+ runCurrent()
+ assertThat(padding).isEqualTo((defaultPadding * .5f).toInt())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
new file mode 100644
index 0000000..a30e2a6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsFingerprintViewModelTest.kt
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.bouncer.data.repository.FakeKeyguardBouncerRepository
+import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
+import com.android.systemui.common.ui.data.repository.FakeConfigurationRepository
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.doze.util.BurnInHelperWrapper
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.data.repository.FakeCommandQueue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.BurnInInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.UdfpsKeyguardInteractor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+/** Tests UdfpsFingerprintViewModel specific flows. */
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsFingerprintViewModelTest : SysuiTestCase() {
+ private val defaultPadding = 12
+ private lateinit var underTest: FingerprintViewModel
+
+ private lateinit var testScope: TestScope
+ private lateinit var configRepository: FakeConfigurationRepository
+ private lateinit var bouncerRepository: KeyguardBouncerRepository
+ private lateinit var keyguardRepository: FakeKeyguardRepository
+ private lateinit var fakeCommandQueue: FakeCommandQueue
+ private lateinit var featureFlags: FakeFeatureFlags
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+
+ @Mock private lateinit var burnInHelper: BurnInHelperWrapper
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ overrideResource(com.android.systemui.R.dimen.lock_icon_padding, defaultPadding)
+ testScope = TestScope()
+ configRepository = FakeConfigurationRepository()
+ keyguardRepository = FakeKeyguardRepository()
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ fakeCommandQueue = FakeCommandQueue()
+ featureFlags =
+ FakeFeatureFlags().apply {
+ set(Flags.REFACTOR_UDFPS_KEYGUARD_VIEWS, true)
+ set(Flags.FACE_AUTH_REFACTOR, false)
+ }
+ bouncerRepository = FakeKeyguardBouncerRepository()
+ transitionRepository = FakeKeyguardTransitionRepository()
+ val transitionInteractor =
+ KeyguardTransitionInteractor(
+ transitionRepository,
+ testScope.backgroundScope,
+ )
+ val udfpsKeyguardInteractor =
+ UdfpsKeyguardInteractor(
+ configRepository,
+ BurnInInteractor(
+ context,
+ burnInHelper,
+ testScope.backgroundScope,
+ configRepository,
+ FakeSystemClock(),
+ ),
+ KeyguardInteractor(
+ keyguardRepository,
+ fakeCommandQueue,
+ featureFlags,
+ bouncerRepository,
+ configRepository,
+ ),
+ )
+
+ underTest =
+ FingerprintViewModel(
+ context,
+ transitionInteractor,
+ udfpsKeyguardInteractor,
+ )
+ }
+
+ @Test
+ fun paddingUpdates_onScaleForResolutionChanges() =
+ testScope.runTest {
+ val padding by collectLastValue(underTest.padding)
+
+ configRepository.setScaleForResolution(1f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding)
+
+ configRepository.setScaleForResolution(2f)
+ runCurrent()
+ assertThat(padding).isEqualTo(defaultPadding * 2)
+
+ configRepository.setScaleForResolution(.5f)
+ runCurrent()
+ assertThat(padding).isEqualTo((defaultPadding * .5).toInt())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
new file mode 100644
index 0000000..d58ceee
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/UdfpsLockscreenViewModelTest.kt
@@ -0,0 +1,505 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.settingslib.Utils
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.google.common.collect.Range
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.MockitoAnnotations
+
+/** Tests UDFPS lockscreen view model transitions. */
+@ExperimentalCoroutinesApi
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class UdfpsLockscreenViewModelTest : SysuiTestCase() {
+ private val lockscreenColorResId = android.R.attr.textColorPrimary
+ private val alternateBouncerResId = com.android.internal.R.attr.materialColorOnPrimaryFixed
+ private val lockscreenColor = Utils.getColorAttrDefaultColor(context, lockscreenColorResId)
+ private val alternateBouncerColor =
+ Utils.getColorAttrDefaultColor(context, alternateBouncerResId)
+
+ private lateinit var underTest: UdfpsLockscreenViewModel
+ private lateinit var testScope: TestScope
+ private lateinit var transitionRepository: FakeKeyguardTransitionRepository
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ testScope = TestScope()
+ transitionRepository = FakeKeyguardTransitionRepository()
+ val transitionInteractor =
+ KeyguardTransitionInteractor(
+ transitionRepository,
+ testScope.backgroundScope,
+ )
+ underTest =
+ UdfpsLockscreenViewModel(
+ context,
+ lockscreenColorResId,
+ alternateBouncerResId,
+ transitionInteractor,
+ )
+ }
+
+ @Test
+ fun goneToAodTransition() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.RUNNING: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+
+ // TransitionState.FINISHED: gone -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.GONE,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "goneToAodTransition",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun lockscreenToAod() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> AOD
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun aodToLockscreen() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+
+ // TransitionState.RUNNING: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: AOD -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "aodToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+ }
+
+ @Test
+ fun lockscreenToAlternateBouncer() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> alternate bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.ALTERNATE_BOUNCER,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToAlternateBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+ }
+
+ fun alternateBouncerToPrimaryBouncer() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.59f, .61f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: alternate bouncer -> primary bouncer
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.PRIMARY_BOUNCER,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "alternateBouncerToPrimaryBouncer",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isFalse()
+ }
+
+ fun alternateBouncerToAod() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: alternate bouncer -> aod
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.ALTERNATE_BOUNCER,
+ to = KeyguardState.AOD,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "alternateBouncerToAod",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(alternateBouncerColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun lockscreenToOccluded() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isIn(Range.closed(.39f, .41f))
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: lockscreen -> occluded
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.OCCLUDED,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "lockscreenToOccluded",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(0f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isFalse()
+ }
+
+ @Test
+ fun occludedToLockscreen() =
+ testScope.runTest {
+ val transition by collectLastValue(underTest.transition)
+ val visible by collectLastValue(underTest.visible)
+
+ // TransitionState.STARTED: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0f,
+ transitionState = TransitionState.STARTED,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.RUNNING: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = .6f,
+ transitionState = TransitionState.RUNNING,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+
+ // TransitionState.FINISHED: occluded -> lockscreen
+ transitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.OCCLUDED,
+ to = KeyguardState.LOCKSCREEN,
+ value = 1f,
+ transitionState = TransitionState.FINISHED,
+ ownerName = "occludedToLockscreen",
+ )
+ )
+ runCurrent()
+ assertThat(transition?.alpha).isEqualTo(1f)
+ assertThat(transition?.scale).isEqualTo(1f)
+ assertThat(transition?.color).isEqualTo(lockscreenColor)
+ assertThat(visible).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
index 0cf6d3d..b5eae5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/LogBufferTest.kt
@@ -2,6 +2,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.core.Logger
import com.google.common.truth.Truth.assertThat
import java.io.PrintWriter
import java.io.StringWriter
@@ -32,7 +33,8 @@
@Test
fun log_shouldSaveLogToBuffer() {
- buffer.log("Test", LogLevel.INFO, "Some test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("Some test message")
val dumpedString = dumpBuffer()
@@ -41,8 +43,9 @@
@Test
fun log_shouldRotateIfLogBufferIsFull() {
- buffer.log("Test", LogLevel.INFO, "This should be rotated")
- buffer.log("Test", LogLevel.INFO, "New test message")
+ val logger = Logger(buffer, "Test")
+ logger.i("This should be rotated")
+ logger.i("New test message")
val dumpedString = dumpBuffer()
@@ -53,7 +56,8 @@
fun dump_writesExceptionAndStacktrace() {
buffer = createBuffer()
val exception = createTestException("Exception message", "TestClass")
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -72,7 +76,8 @@
"TestClass",
cause = createTestException("The real cause!", "TestClass")
)
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedString = dumpBuffer()
@@ -93,7 +98,8 @@
)
)
exception.addSuppressed(createTestException("Second suppressed exception", "SecondClass"))
- buffer.log("Tag", LogLevel.ERROR, { str1 = "Extra message" }, { str1!! }, exception)
+ val logger = Logger(buffer, "Test")
+ logger.e("Extra message", exception)
val dumpedStr = dumpBuffer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
new file mode 100644
index 0000000..ab19b3a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/core/LoggerTest.kt
@@ -0,0 +1,140 @@
+package com.android.systemui.log.core
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.log.LogMessageImpl
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.anyString
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+import org.mockito.junit.MockitoJUnitRunner
+
+@SmallTest
+@RunWith(MockitoJUnitRunner::class)
+class LoggerTest : SysuiTestCase() {
+ @Mock private lateinit var buffer: MessageBuffer
+ private lateinit var message: LogMessage
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ whenever(buffer.obtain(any(), any(), any(), isNull())).thenAnswer {
+ message = LogMessageImpl.Factory.create()
+ return@thenAnswer message
+ }
+ }
+
+ @Test
+ fun log_shouldCommitLogMessage() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) {
+ int1 = 1
+ str1 = "test"
+ bool1 = true
+ }
+
+ assertThat(message.int1).isEqualTo(1)
+ assertThat(message.str1).isEqualTo("test")
+ assertThat(message.bool1).isEqualTo(true)
+ }
+
+ @Test
+ fun log_shouldUseCorrectLoggerTag() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.log(LogLevel.DEBUG, { "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(eq("LoggerTest"), any(), any(), nullable())
+ }
+
+ @Test
+ fun v_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun v_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.v("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.VERBOSE), any(), nullable())
+ }
+
+ @Test
+ fun d_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun d_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.d("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.DEBUG), any(), nullable())
+ }
+
+ @Test
+ fun i_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun i_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.i("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.INFO), any(), nullable())
+ }
+
+ @Test
+ fun w_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun w_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.w("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WARNING), any(), nullable())
+ }
+
+ @Test
+ fun e_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun e_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.e("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.ERROR), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withMessageInitializer_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf({ "count=$int1" }) { int1 = 1 }
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+
+ @Test
+ fun wtf_withCompileTimeMessage_shouldLogAtCorrectLevel() {
+ val logger = Logger(buffer, "LoggerTest")
+ logger.wtf("Message")
+ verify(buffer).obtain(anyString(), eq(LogLevel.WTF), any(), nullable())
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
index 12f4689..83182c5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/log/table/TableLogBufferTest.kt
@@ -18,8 +18,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.log.table.TableChange.Companion.IS_INITIAL_PREFIX
import com.android.systemui.log.table.TableChange.Companion.MAX_STRING_LENGTH
import com.android.systemui.util.mockito.any
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
index b40ebc9..91b0245 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/ui/KeyguardMediaControllerTest.kt
@@ -193,6 +193,17 @@
}
@Test
+ fun dozeWakeUpAnimationWaiting_inSplitShade_mediaIsHidden() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = true
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(splitShadeContainer.visibility).isEqualTo(GONE)
+ }
+
+ @Test
fun dozing_inSingleShade_mediaIsVisible() {
val splitShadeContainer = FrameLayout(context)
keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
@@ -203,6 +214,17 @@
assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
}
+ @Test
+ fun dozeWakeUpAnimationWaiting_inSingleShade_mediaIsVisible() {
+ val splitShadeContainer = FrameLayout(context)
+ keyguardMediaController.attachSplitShadeContainer(splitShadeContainer)
+ keyguardMediaController.useSplitShade = false
+
+ keyguardMediaController.isDozeWakeUpAnimationWaiting = true
+
+ assertThat(mediaContainerView.visibility).isEqualTo(VISIBLE)
+ }
+
private fun setDozing() {
whenever(statusBarStateController.isDozing).thenReturn(true)
statusBarStateListener.onDozingChanged(true)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
index 7df54d4..e4f89a2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputAdapterTest.java
@@ -291,13 +291,13 @@
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTitleText.getText().toString()).isEqualTo(TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mTwoLineLayout.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mEndTouchArea.getVisibility()).isEqualTo(View.VISIBLE);
}
@Test
@@ -525,16 +525,16 @@
mMediaOutputAdapter.onBindViewHolder(mViewHolder, 0);
assertThat(mViewHolder.mTitleText.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.GONE);
+ assertThat(mViewHolder.mSeekBar.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mProgressBar.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mCheckBox.getVisibility()).isEqualTo(View.GONE);
- assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.VISIBLE);
+ assertThat(mViewHolder.mStatusIcon.getVisibility()).isEqualTo(View.GONE);
assertThat(mViewHolder.mSubTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mTwoLineTitleText.getVisibility()).isEqualTo(View.VISIBLE);
assertThat(mViewHolder.mSubTitleText.getText().toString()).isEqualTo(TEST_CUSTOM_SUBTEXT);
assertThat(mViewHolder.mTwoLineTitleText.getText().toString()).isEqualTo(
TEST_DEVICE_NAME_1);
- assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isTrue();
+ assertThat(mViewHolder.mContainerLayout.hasOnClickListeners()).isFalse();
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
index 9e54224..5890cbd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt
@@ -25,7 +25,7 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -97,10 +97,11 @@
multiShadeInteractor = interactor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = keyguardTransitionRepository,
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor,
falsingManager = falsingManager,
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 697d1a3..25d494c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -20,6 +20,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS;
@@ -363,7 +364,7 @@
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
defaultNavBar.setImeWindowStatus(
- DEFAULT_DISPLAY, null, 0 /* vis */, BACK_DISPOSITION_DEFAULT, false);
+ DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
| NAVIGATION_HINT_IME_SWITCHER_SHOWN,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
index a60dad4..fe6c9b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSPanelTest.kt
@@ -15,9 +15,11 @@
import android.graphics.Rect
import android.testing.AndroidTestingRunner
+import android.testing.TestableContext
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import android.testing.ViewUtils
+import android.view.ContextThemeWrapper
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.accessibility.AccessibilityNodeInfo
@@ -55,19 +57,24 @@
private lateinit var footer: View
+ private val themedContext = TestableContext(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ )
+
@Before
@Throws(Exception::class)
fun setup() {
MockitoAnnotations.initMocks(this)
testableLooper = TestableLooper.get(this)
+ // Apply only the values of the theme that are not defined
testableLooper.runWithLooper {
- qsPanel = QSPanel(context, null)
+ qsPanel = QSPanel(themedContext, null)
qsPanel.mUsingMediaPlayer = true
qsPanel.initialize(qsLogger)
// QSPanel inflates a footer inside of it, mocking it here
- footer = LinearLayout(context).apply { id = R.id.qs_footer }
+ footer = LinearLayout(themedContext).apply { id = R.id.qs_footer }
qsPanel.addView(footer, MATCH_PARENT, 100)
qsPanel.onFinishInflate()
// Provides a parent with non-zero size for QSPanel
@@ -105,12 +112,12 @@
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(context, QSIconViewImpl(context))
+ QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
)
)
- val mediaView = FrameLayout(context)
- mediaView.addView(View(context), MATCH_PARENT, 800)
+ val mediaView = FrameLayout(themedContext)
+ mediaView.addView(View(themedContext), MATCH_PARENT, 800)
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
@@ -135,12 +142,12 @@
qsPanel.tileLayout?.addTile(
QSPanelControllerBase.TileRecord(
mock(QSTile::class.java),
- QSTileViewImpl(context, QSIconViewImpl(context))
+ QSTileViewImpl(themedContext, QSIconViewImpl(themedContext))
)
)
- val mediaView = FrameLayout(context)
- mediaView.addView(View(context), MATCH_PARENT, 800)
+ val mediaView = FrameLayout(themedContext)
+ mediaView.addView(View(themedContext), MATCH_PARENT, 800)
qsPanel.setUsingHorizontalLayout(/* horizontal */ true, mediaView, /* force */ true)
qsPanel.measure(
@@ -161,7 +168,10 @@
@Test
fun testBottomPadding() {
val padding = 10
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_bottom, padding)
+ themedContext.orCreateTestableResources.addOverride(
+ R.dimen.qs_panel_padding_bottom,
+ padding
+ )
qsPanel.updatePadding()
assertThat(qsPanel.paddingBottom).isEqualTo(padding)
}
@@ -170,8 +180,11 @@
fun testTopPadding() {
val padding = 10
val paddingCombined = 100
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
- context.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, paddingCombined)
+ themedContext.orCreateTestableResources.addOverride(R.dimen.qs_panel_padding_top, padding)
+ themedContext.orCreateTestableResources.addOverride(
+ R.dimen.qs_panel_padding_top,
+ paddingCombined
+ )
qsPanel.updatePadding()
assertThat(qsPanel.paddingTop).isEqualTo(paddingCombined)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
index 8789253..f55ef65 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/TileLayoutTest.java
@@ -26,12 +26,14 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.TestableLooper;
+import android.view.ContextThemeWrapper;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -56,14 +58,17 @@
private Resources mResources;
private int mLayoutSizeForOneTile;
private TileLayout mTileLayout; // under test
+ private Context mSpyContext;
+
@Before
public void setUp() throws Exception {
- Context context = Mockito.spy(mContext);
- mResources = Mockito.spy(context.getResources());
- Mockito.when(mContext.getResources()).thenReturn(mResources);
+ mSpyContext = Mockito.spy(
+ new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_QuickSettings));
+ mResources = Mockito.spy(mSpyContext.getResources());
+ when(mSpyContext.getResources()).thenReturn(mResources);
- mTileLayout = new TileLayout(context);
+ mTileLayout = new TileLayout(mSpyContext);
// Layout needs to leave space for the tile margins. Three times the margin size is
// sufficient for any number of columns.
mLayoutSizeForOneTile =
@@ -73,7 +78,7 @@
private QSPanelControllerBase.TileRecord createTileRecord() {
return new QSPanelControllerBase.TileRecord(
mock(QSTile.class),
- spy(new QSTileViewImpl(mContext, new QSIconViewImpl(mContext))));
+ spy(new QSTileViewImpl(mSpyContext, new QSIconViewImpl(mSpyContext))));
}
@Test
@@ -161,7 +166,7 @@
.layout(left2.capture(), top2.capture(), right2.capture(), bottom2.capture());
// We assume two tiles will always fit side-by-side.
- assertTrue(mContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1);
+ assertTrue(mSpyContext.getResources().getInteger(R.integer.quick_settings_num_columns) > 1);
// left <= right, top <= bottom
assertTrue(left1.getValue() <= right1.getValue());
@@ -218,16 +223,16 @@
@Test
public void resourcesChanged_updateResources_returnsTrue() {
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
mTileLayout.updateResources(); // setup with 1
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(2);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(2);
assertEquals(true, mTileLayout.updateResources());
}
@Test
public void resourcesSame_updateResources_returnsFalse() {
- Mockito.when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
+ when(mResources.getInteger(R.integer.quick_settings_num_columns)).thenReturn(1);
mTileLayout.updateResources(); // setup with 1
assertEquals(false, mTileLayout.updateResources());
@@ -250,7 +255,7 @@
QSPanelControllerBase.TileRecord tileRecord = createTileRecord();
mTileLayout.addTile(tileRecord);
- FakeTileView tileView = new FakeTileView(mContext);
+ FakeTileView tileView = new FakeTileView(mSpyContext);
QSTile.State state = new QSTile.State();
state.label = "TEST LABEL";
state.secondaryLabel = "TEST SECONDARY LABEL";
@@ -276,9 +281,10 @@
}
private void changeFontScaling(float scale) {
- Configuration configuration = new Configuration(mContext.getResources().getConfiguration());
+ Configuration configuration =
+ new Configuration(mSpyContext.getResources().getConfiguration());
configuration.fontScale = scale;
// updateConfiguration could help update on both resource configuration and displayMetrics
- mContext.getResources().updateConfiguration(configuration, null, null);
+ mSpyContext.getResources().updateConfiguration(configuration, null, null);
}
}
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 2cc6709..d647d6a 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
@@ -21,6 +21,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
+import android.view.ContextThemeWrapper
import androidx.test.filters.SmallTest
import com.android.settingslib.Utils
import com.android.settingslib.drawable.UserIconDrawable
@@ -63,6 +64,8 @@
private val testScope = TestScope()
private lateinit var utils: FooterActionsTestUtils
+ private val themedContext = ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+
@Before
fun setUp() {
utils = FooterActionsTestUtils(context, TestableLooper.get(this), testScope.testScheduler)
@@ -84,8 +87,14 @@
ContentDescription.Resource(R.string.accessibility_quick_settings_settings)
)
)
- assertThat(settings.backgroundColor).isEqualTo(R.attr.offStateColor)
- assertThat(settings.iconTint).isNull()
+ assertThat(settings.backgroundColor).isEqualTo(R.attr.shadeInactive)
+ assertThat(settings.iconTint)
+ .isEqualTo(
+ Utils.getColorAttrDefaultColor(
+ themedContext,
+ R.attr.onShadeInactiveVariant,
+ )
+ )
}
@Test
@@ -105,12 +114,12 @@
ContentDescription.Resource(R.string.accessibility_quick_settings_power_menu)
)
)
- assertThat(power.backgroundColor).isEqualTo(com.android.internal.R.attr.colorAccent)
+ assertThat(power.backgroundColor).isEqualTo(R.attr.shadeActive)
assertThat(power.iconTint)
.isEqualTo(
Utils.getColorAttrDefaultColor(
- context,
- com.android.internal.R.attr.textColorOnAccent,
+ themedContext,
+ R.attr.onShadeActive,
),
)
}
@@ -170,7 +179,7 @@
assertThat(userSwitcher).isNotNull()
assertThat(userSwitcher!!.icon)
.isEqualTo(Icon.Loaded(picture, ContentDescription.Loaded("Signed in as foo")))
- assertThat(userSwitcher.backgroundColor).isEqualTo(R.attr.offStateColor)
+ assertThat(userSwitcher.backgroundColor).isEqualTo(R.attr.shadeInactive)
// Change the current user name.
userSwitcherControllerWrapper.currentUserName = "bar"
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
index 18f3837..dc0fae5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/pipeline/data/repository/InstalledTilesComponentRepositoryImplTest.kt
@@ -38,6 +38,7 @@
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.nullable
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -75,6 +76,9 @@
fun setUp() {
MockitoAnnotations.initMocks(this)
+ whenever(context.createContextAsUser(any(), anyInt())).thenReturn(context)
+ whenever(context.packageManager).thenReturn(packageManager)
+
// Use the default value set in the ServiceInfo
whenever(packageManager.getComponentEnabledSetting(any()))
.thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
@@ -86,7 +90,6 @@
underTest =
InstalledTilesComponentRepositoryImpl(
context,
- packageManager,
testDispatcher,
)
}
@@ -224,6 +227,52 @@
assertThat(componentNames).isEmpty()
}
+ @Test
+ fun packageOnlyInSecondaryUser_noException() =
+ testScope.runTest {
+ val userId = 10
+ val secondaryUserContext: Context = mock()
+ whenever(context.userId).thenReturn(0) // System context
+ whenever(context.createContextAsUser(eq(UserHandle.of(userId)), anyInt()))
+ .thenReturn(secondaryUserContext)
+
+ val secondaryUserPackageManager: PackageManager = mock()
+ whenever(secondaryUserContext.packageManager).thenReturn(secondaryUserPackageManager)
+
+ // Use the default value set in the ServiceInfo
+ whenever(secondaryUserPackageManager.getComponentEnabledSetting(any()))
+ .thenReturn(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
+ // System User package manager throws exception if the component doesn't exist for that
+ // user
+ whenever(packageManager.getComponentEnabledSetting(TEST_COMPONENT))
+ .thenThrow(IllegalArgumentException()) // The package is not in the system user
+
+ val resolveInfo =
+ ResolveInfo(TEST_COMPONENT, hasPermission = true, defaultEnabled = true)
+ // Both package manager should return the same (because the query is for the secondary
+ // user)
+ whenever(
+ secondaryUserPackageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+ whenever(
+ packageManager.queryIntentServicesAsUser(
+ matchIntent(),
+ matchFlags(),
+ eq(userId)
+ )
+ )
+ .thenReturn(listOf(resolveInfo))
+
+ val componentNames by collectLastValue(underTest.getInstalledTilesComponents(userId))
+
+ assertThat(componentNames).containsExactly(TEST_COMPONENT)
+ }
+
private fun getRegisteredReceiver(): BroadcastReceiver {
verify(context)
.registerReceiverAsUser(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
index 962b537..22b1c7b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSTileImplTest.java
@@ -204,6 +204,19 @@
}
@Test
+ public void testLongClick_falsing() {
+ mFalsingManager.setFalseLongTap(true);
+ mTile.longClick(null /* view */);
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mLongClicked).isFalse();
+
+ mFalsingManager.setFalseLongTap(false);
+ mTile.longClick(null /* view */);
+ mTestableLooper.processAllMessages();
+ assertThat(mTile.mLongClicked).isTrue();
+ }
+
+ @Test
public void testSecondaryClick_Metrics() {
mTile.secondaryClick(null /* view */);
verify(mMetricsLogger).write(argThat(new TileLogMatcher(ACTION_QS_SECONDARY_CLICK)));
@@ -518,6 +531,7 @@
}
private static class TileImpl extends QSTileImpl<QSTile.BooleanState> {
boolean mClicked;
+ boolean mLongClicked;
int mRefreshes = 0;
protected TileImpl(
@@ -551,6 +565,11 @@
}
@Override
+ protected void handleLongClick(@Nullable View view) {
+ mLongClicked = true;
+ }
+
+ @Override
protected void handleUpdateState(BooleanState state, Object arg) {
mRefreshes++;
}
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 28aeba4..3c66772 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
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.text.TextUtils
+import android.view.ContextThemeWrapper
import android.view.View
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.TextView
@@ -386,7 +387,11 @@
context: Context,
icon: QSIconView,
collapsed: Boolean
- ) : QSTileViewImpl(context, icon, collapsed) {
+ ) : QSTileViewImpl(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings),
+ icon,
+ collapsed
+ ) {
fun changeState(state: QSTile.State) {
handleStateChanged(state)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
index f0e4e3a..77a4436 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/DndTileTest.kt
@@ -25,6 +25,7 @@
import android.provider.Settings.Global.ZEN_MODE_OFF
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
+import android.view.ContextThemeWrapper
import android.view.View
import androidx.test.filters.SmallTest
import com.android.internal.logging.MetricsLogger
@@ -110,7 +111,9 @@
whenever(qsHost.userId).thenReturn(DEFAULT_USER)
- val wrappedContext = object : ContextWrapper(context) {
+ val wrappedContext = object : ContextWrapper(
+ ContextThemeWrapper(context, R.style.Theme_SystemUI_QuickSettings)
+ ) {
override fun getSharedPreferences(file: File?, mode: Int): SharedPreferences {
return sharedPreferences
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index ef7c7bc..9188293 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -80,6 +80,7 @@
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
+import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.common.ui.view.LongPressHandlingView;
@@ -91,7 +92,6 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.KeyguardViewConfigurator;
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository;
-import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
@@ -629,7 +629,7 @@
mHeadsUpManager);
mNotificationPanelViewController.setTrackingStartedListener(() -> {});
mNotificationPanelViewController.setOpenCloseListener(
- new NotificationPanelViewController.OpenCloseListener() {
+ new OpenCloseListener() {
@Override
public void onClosingFinished() {}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 5802eb3..eb4ae1a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -514,6 +514,17 @@
}
@Test
+ public void keyguardStatusView_willPlayDelayedDoze_notifiesKeyguardMediaController() {
+ when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(2);
+ mStatusBarStateController.setState(KEYGUARD);
+ enableSplitShade(/* enabled= */ true);
+
+ mNotificationPanelViewController.setWillPlayDelayedDozeAmountAnimation(true);
+
+ verify(mKeyguardMediaController).setDozeWakeUpAnimationWaiting(true);
+ }
+
+ @Test
public void keyguardStatusView_willPlayDelayedDoze_isCentered_thenStillCenteredIfNoNotifs() {
when(mNotificationStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(0);
mStatusBarStateController.setState(KEYGUARD);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
index 2a9b403..5fb3a79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt
@@ -28,6 +28,11 @@
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.back.domain.interactor.BackActionInteractor
+import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
+import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
+import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
+import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
+import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.classifier.FalsingManagerFake
import com.android.systemui.dock.DockManager
@@ -35,14 +40,9 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.bouncer.data.factory.BouncerMessageFactory
-import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor
-import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil
-import com.android.systemui.bouncer.data.repository.FakeBouncerMessageRepository
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.TransitionStep
-import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -80,9 +80,9 @@
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.util.Optional
+import org.mockito.Mockito.`when` as whenever
@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@@ -198,10 +198,9 @@
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ ).keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
index 252a03b..544137e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt
@@ -40,8 +40,8 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.KeyguardUnlockAnimationController
-import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel
import com.android.systemui.log.BouncerLogger
import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy
@@ -211,10 +211,10 @@
multiShadeInteractor = multiShadeInteractor,
featureFlags = featureFlags,
keyguardTransitionInteractor =
- KeyguardTransitionInteractor(
- repository = FakeKeyguardTransitionRepository(),
- scope = testScope.backgroundScope
- ),
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ )
+ .keyguardTransitionInteractor,
falsingManager = FalsingManagerFake(),
shadeController = shadeController,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
index c72f4e7..a2c2912 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/QuickSettingsControllerBaseTest.java
@@ -169,6 +169,7 @@
when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
mShadeInteractor =
new ShadeInteractor(
+ mTestScope.getBackgroundScope(),
mDisableFlagsRepository,
mKeyguardRepository,
new FakeUserSetupRepository(),
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
index 00a0567..729c4a9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeControllerImplTest.kt
@@ -56,7 +56,7 @@
@Mock private lateinit var windowManager: WindowManager
@Mock private lateinit var assistManager: AssistManager
@Mock private lateinit var gutsManager: NotificationGutsManager
- @Mock private lateinit var notificationPanelViewController: NotificationPanelViewController
+ @Mock private lateinit var shadeViewController: ShadeViewController
@Mock private lateinit var nswvc: NotificationShadeWindowViewController
@Mock private lateinit var display: Display
@@ -82,7 +82,7 @@
Lazy { gutsManager },
)
shadeController.setNotificationShadeWindowViewController(nswvc)
- shadeController.setNotificationPanelViewController(notificationPanelViewController)
+ shadeController.setShadeViewController(shadeViewController)
}
@Test
@@ -91,9 +91,9 @@
// Trying to open it does nothing.
shadeController.animateExpandShade()
- verify(notificationPanelViewController, never()).expandToNotifications()
+ verify(shadeViewController, never()).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController, never()).expand(ArgumentMatchers.anyBoolean())
+ verify(shadeViewController, never()).expand(ArgumentMatchers.anyBoolean())
}
@Test
@@ -102,15 +102,15 @@
// Can now be opened.
shadeController.animateExpandShade()
- verify(notificationPanelViewController).expandToNotifications()
+ verify(shadeViewController).expandToNotifications()
shadeController.animateExpandQs()
- verify(notificationPanelViewController).expandToQs()
+ verify(shadeViewController).expandToQs()
}
@Test
fun cancelExpansionAndCollapseShade_callsCancelCurrentTouch() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(true)
+ whenever(shadeViewController.isTracking).thenReturn(true)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
@@ -122,7 +122,7 @@
@Test
fun cancelExpansionAndCollapseShade_doesNotCallAnimateCollapseShade_whenCollapsed() {
// GIVEN the shade is tracking a touch
- whenever(notificationPanelViewController.isTracking).thenReturn(false)
+ whenever(shadeViewController.isTracking).thenReturn(false)
// WHEN cancelExpansionAndCollapseShade is called
shadeController.cancelExpansionAndCollapseShade()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index 2da2e92..f542ab0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -237,11 +237,22 @@
whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(false)
makeShadeVisible()
+ shadeHeaderController.qsExpandedFraction = 1.0f
verify(statusIcons).addIgnoredSlots(carrierIconSlots)
}
@Test
+ fun dualCarrier_enablesCarrierIconsInStatusIcons_qsExpanded() {
+ whenever(mShadeCarrierGroupController.isSingleCarrier).thenReturn(false)
+
+ makeShadeVisible()
+ shadeHeaderController.qsExpandedFraction = 0.0f
+
+ verify(statusIcons, times(2)).removeIgnoredSlots(carrierIconSlots)
+ }
+
+ @Test
fun disableQS_notDisabled_visible() {
makeShadeVisible()
shadeHeaderController.disable(0, 0, false)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
index 4461310..dae9c97 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/carrier/ShadeCarrierTest.java
@@ -20,8 +20,10 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
+import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.view.ContextThemeWrapper;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
@@ -48,7 +50,9 @@
@Before
public void setUp() throws Exception {
mTestableLooper = TestableLooper.get(this);
- LayoutInflater inflater = LayoutInflater.from(mContext);
+ Context themedContext =
+ new ContextThemeWrapper(mContext, R.style.Theme_SystemUI_QuickSettings);
+ LayoutInflater inflater = LayoutInflater.from(themedContext);
mContext.ensureTestableResources();
mTestableLooper.runWithLooper(() ->
mShadeCarrier = (ShadeCarrier) inflater.inflate(R.layout.shade_carrier, null));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
index 7392a94..3ea8f54 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/data/repository/ShadeInteractorTest.kt
@@ -84,6 +84,7 @@
MockitoAnnotations.initMocks(this)
featureFlags.set(Flags.FACE_AUTH_REFACTOR, false)
+ featureFlags.set(Flags.FULL_SCREEN_USER_SWITCHER, true)
val refreshUsersScheduler =
RefreshUsersScheduler(
@@ -117,6 +118,7 @@
)
underTest =
ShadeInteractor(
+ testScope.backgroundScope,
disableFlagsRepository,
keyguardRepository,
userSetupRepository,
@@ -126,6 +128,20 @@
}
@Test
+ fun isShadeEnabled_matchesDisableFlagsRepo() =
+ testScope.runTest {
+ val actual by collectLastValue(underTest.isShadeEnabled)
+
+ disableFlagsRepository.disableFlags.value =
+ DisableFlagsModel(disable2 = DISABLE2_NOTIFICATION_SHADE)
+ assertThat(actual).isFalse()
+
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(disable2 = DISABLE2_NONE)
+
+ assertThat(actual).isTrue()
+ }
+
+ @Test
fun isExpandToQsEnabled_deviceNotProvisioned_false() =
testScope.runTest {
whenever(deviceProvisionedController.isDeviceProvisioned).thenReturn(false)
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 385d556..1643e17 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/CommandQueueTest.java
@@ -16,6 +16,7 @@
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
+import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowInsetsController.BEHAVIOR_DEFAULT;
@@ -200,7 +201,7 @@
mCommandQueue.setImeWindowStatus(SECONDARY_DISPLAY, null, 1, 2, true);
waitForIdleSync();
- verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(0),
+ verify(mCallbacks).setImeWindowStatus(eq(DEFAULT_DISPLAY), eq(null), eq(IME_INVISIBLE),
eq(BACK_DISPOSITION_DEFAULT), eq(false));
verify(mCallbacks).setImeWindowStatus(
eq(SECONDARY_DISPLAY), eq(null), eq(1), eq(2), eq(true));
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 21e0f68..ff2f106 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -1,5 +1,6 @@
package com.android.systemui.statusbar
+import android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
@@ -11,6 +12,7 @@
import com.android.systemui.classifier.FalsingCollectorFake
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.media.controls.ui.MediaHierarchyManager
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
@@ -19,6 +21,9 @@
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.shade.ShadeViewController
import com.android.systemui.shade.data.repository.FakeShadeRepository
+import com.android.systemui.shade.domain.interactor.ShadeInteractor
+import com.android.systemui.statusbar.disableflags.data.model.DisableFlagsModel
+import com.android.systemui.statusbar.disableflags.data.repository.FakeDisableFlagsRepository
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.AmbientState
@@ -28,8 +33,13 @@
import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.phone.LSShadeTransitionLogger
import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
import com.android.systemui.statusbar.policy.FakeConfigurationController
import com.android.systemui.util.mockito.mock
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runCurrent
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertNotNull
@@ -60,8 +70,11 @@
@SmallTest
@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidTestingRunner::class)
+@OptIn(ExperimentalCoroutinesApi::class)
class LockscreenShadeTransitionControllerTest : SysuiTestCase() {
+ private val testScope = TestScope(StandardTestDispatcher())
+
lateinit var transitionController: LockscreenShadeTransitionController
lateinit var row: ExpandableNotificationRow
@Mock lateinit var statusbarStateController: SysuiStatusBarStateController
@@ -87,6 +100,15 @@
@Mock lateinit var qsTransitionController: LockscreenShadeQsTransitionController
@Mock lateinit var activityStarter: ActivityStarter
@Mock lateinit var transitionControllerCallback: LockscreenShadeTransitionController.Callback
+ private val disableFlagsRepository = FakeDisableFlagsRepository()
+ private val shadeInteractor = ShadeInteractor(
+ testScope.backgroundScope,
+ disableFlagsRepository,
+ keyguardRepository = FakeKeyguardRepository(),
+ userSetupRepository = FakeUserSetupRepository(),
+ deviceProvisionedController = mock(),
+ userInteractor = mock(),
+ )
private val powerInteractor = PowerInteractor(
FakePowerRepository(),
FalsingCollectorFake(),
@@ -99,6 +121,10 @@
@Before
fun setup() {
+ // By default, have the shade enabled
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel()
+ testScope.runCurrent()
+
val helper = NotificationTestHelper(
mContext,
mDependency,
@@ -139,6 +165,7 @@
qsTransitionControllerFactory = { qsTransitionController },
activityStarter = activityStarter,
shadeRepository = FakeShadeRepository(),
+ shadeInteractor = shadeInteractor,
powerInteractor = powerInteractor,
)
transitionController.addCallback(transitionControllerCallback)
@@ -214,7 +241,10 @@
@Test
fun testDontGoWhenShadeDisabled() {
- whenever(mCentralSurfaces.isShadeDisabled).thenReturn(true)
+ disableFlagsRepository.disableFlags.value = DisableFlagsModel(
+ disable2 = DISABLE2_NOTIFICATION_SHADE,
+ )
+ testScope.runCurrent()
transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
new file mode 100644
index 0000000..cfbe8e3
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/CommandParserTest.kt
@@ -0,0 +1,212 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class CommandParserTest : SysuiTestCase() {
+ private val parser = CommandParser()
+
+ @Test
+ fun registerToken_cannotReuseNames() {
+ parser.flag("-f")
+ assertThrows(IllegalArgumentException::class.java) { parser.flag("-f") }
+ }
+
+ @Test
+ fun unknownToken_throws() {
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("unknown-token")) }
+ }
+
+ @Test
+ fun parseSingleFlag_present() {
+ val flag by parser.flag("-f")
+ parser.parse(listOf("-f"))
+ assertTrue(flag)
+ }
+
+ @Test
+ fun parseSingleFlag_notPresent() {
+ val flag by parser.flag("-f")
+ parser.parse(listOf())
+ assertFalse(flag)
+ }
+
+ @Test
+ fun parseSingleOptionalParam_present() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parse(listOf("-p", "123"))
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseSingleOptionalParam_notPresent() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parse(listOf())
+ assertThat(param).isNull()
+ }
+
+ @Test
+ fun parseSingleOptionalParam_missingArg_throws() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("-p")) }
+ }
+
+ @Test
+ fun parseSingleRequiredParam_present() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ parser.parse(listOf("-p", "123"))
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseSingleRequiredParam_notPresent_failsValidation() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ assertFalse(parser.parse(listOf()))
+ }
+
+ @Test
+ fun parseSingleRequiredParam_missingArg_throws() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ assertThrows(ArgParseError::class.java) { parser.parse(listOf("-p")) }
+ }
+
+ @Test
+ fun parseAsSubCommand_singleFlag_present() {
+ val flag by parser.flag("-f")
+ val args = listOf("-f").listIterator()
+ parser.parseAsSubCommand(args)
+
+ assertTrue(flag)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleFlag_notPresent() {
+ val flag by parser.flag("-f")
+ val args = listOf("--other-flag").listIterator()
+ parser.parseAsSubCommand(args)
+
+ assertFalse(flag)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleOptionalParam_present() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parseAsSubCommand(listOf("-p", "123", "--other-arg", "321").listIterator())
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleOptionalParam_notPresent() {
+ val param by parser.param("-p", valueParser = Type.Int)
+ parser.parseAsSubCommand(listOf("--other-arg", "321").listIterator())
+ assertThat(param).isNull()
+ }
+
+ @Test
+ fun parseAsSubCommand_singleRequiredParam_present() {
+ val param by parser.require(parser.param("-p", valueParser = Type.Int))
+ parser.parseAsSubCommand(listOf("-p", "123", "--other-arg", "321").listIterator())
+ assertThat(param).isEqualTo(123)
+ }
+
+ @Test
+ fun parseAsSubCommand_singleRequiredParam_notPresent() {
+ parser.require(parser.param("-p", valueParser = Type.Int))
+ assertFalse(parser.parseAsSubCommand(listOf("--other-arg", "321").listIterator()))
+ }
+
+ @Test
+ fun parseCommandWithSubCommand_required_provided() {
+ val topLevelFlag by parser.flag("flag", shortName = "-f")
+
+ val cmd =
+ object : ParseableCommand("test") {
+ val flag by flag("flag1")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ parser.require(parser.subCommand(cmd))
+ parser.parse(listOf("-f", "test", "--flag1"))
+
+ assertTrue(topLevelFlag)
+ assertThat(cmd).isNotNull()
+ assertTrue(cmd.flag)
+ }
+
+ @Test
+ fun parseCommandWithSubCommand_required_notProvided() {
+ val topLevelFlag by parser.flag("-f")
+
+ val cmd =
+ object : ParseableCommand("test") {
+ val flag by parser.flag("flag1")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ parser.require(parser.subCommand(cmd))
+
+ assertFalse(parser.parse(listOf("-f")))
+ }
+
+ @Test
+ fun flag_requiredParam_optionalParam_allProvided_failsValidation() {
+ val flag by parser.flag("-f")
+ val optionalParam by parser.param("-p", valueParser = Type.Int)
+ val requiredParam by parser.require(parser.param("-p2", valueParser = Type.Boolean))
+
+ parser.parse(
+ listOf(
+ "-f",
+ "-p",
+ "123",
+ "-p2",
+ "false",
+ )
+ )
+
+ assertTrue(flag)
+ assertThat(optionalParam).isEqualTo(123)
+ assertFalse(requiredParam)
+ }
+
+ @Test
+ fun flag_requiredParam_optionalParam_optionalExcluded() {
+ val flag by parser.flag("-f")
+ val optionalParam by parser.param("-p", valueParser = Type.Int)
+ val requiredParam by parser.require(parser.param("-p2", valueParser = Type.Boolean))
+
+ parser.parse(
+ listOf(
+ "-p2",
+ "true",
+ )
+ )
+
+ assertFalse(flag)
+ assertThat(optionalParam).isNull()
+ assertTrue(requiredParam)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
new file mode 100644
index 0000000..e391d6b
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParametersTest.kt
@@ -0,0 +1,55 @@
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class ParametersTest : SysuiTestCase() {
+ @Test
+ fun singleArgOptional_returnsNullBeforeParse() {
+ val optional by SingleArgParamOptional(longName = "longName", valueParser = Type.Int)
+ assertThat(optional).isNull()
+ }
+
+ @Test
+ fun singleArgOptional_returnsParsedValue() {
+ val param = SingleArgParamOptional(longName = "longName", valueParser = Type.Int)
+ param.parseArgsFromIter(listOf("3").listIterator())
+ val optional by param
+ assertThat(optional).isEqualTo(3)
+ }
+
+ @Test
+ fun singleArgRequired_throwsBeforeParse() {
+ val req by SingleArgParam(longName = "param", valueParser = Type.Boolean)
+ assertThrows(IllegalStateException::class.java) { req }
+ }
+
+ @Test
+ fun singleArgRequired_returnsParsedValue() {
+ val param = SingleArgParam(longName = "param", valueParser = Type.Boolean)
+ param.parseArgsFromIter(listOf("true").listIterator())
+ val req by param
+ assertTrue(req)
+ }
+
+ @Test
+ fun param_handledAfterParse() {
+ val optParam = SingleArgParamOptional(longName = "string1", valueParser = Type.String)
+ val reqParam = SingleArgParam(longName = "string2", valueParser = Type.Float)
+
+ assertFalse(optParam.handled)
+ assertFalse(reqParam.handled)
+
+ optParam.parseArgsFromIter(listOf("test").listIterator())
+ reqParam.parseArgsFromIter(listOf("1.23").listIterator())
+
+ assertTrue(optParam.handled)
+ assertTrue(reqParam.handled)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
new file mode 100644
index 0000000..86548d0
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ParseableCommandTest.kt
@@ -0,0 +1,317 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar.commandline
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import java.io.PrintWriter
+import org.junit.Assert.assertThrows
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+class ParseableCommandTest : SysuiTestCase() {
+ @Mock private lateinit var pw: PrintWriter
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
+
+ /**
+ * A little change-detector-y, but this is just a general assertion that building up a command
+ * parser via its wrapper works as expected.
+ */
+ @Test
+ fun testFactoryMethods() {
+ val mySubCommand =
+ object : ParseableCommand("subCommand") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val mySubCommand2 =
+ object : ParseableCommand("subCommand2") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ // Verify that the underlying parser contains the correct types
+ val myCommand =
+ object : ParseableCommand("testName") {
+ val flag by flag("flag", shortName = "f")
+ val requiredParam by
+ param(longName = "required-param", shortName = "r", valueParser = Type.String)
+ .required()
+ val optionalParam by
+ param(longName = "optional-param", shortName = "o", valueParser = Type.Boolean)
+ val optionalSubCommand by subCommand(mySubCommand)
+ val requiredSubCommand by subCommand(mySubCommand2).required()
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val flags = myCommand.parser.flags
+ val params = myCommand.parser.params
+ val subCommands = myCommand.parser.subCommands
+
+ assertThat(flags).hasSize(2)
+ assertThat(flags[0]).isInstanceOf(Flag::class.java)
+ assertThat(flags[1]).isInstanceOf(Flag::class.java)
+
+ assertThat(params).hasSize(2)
+ val req = params.filter { it is SingleArgParam<*> }
+ val opt = params.filter { it is SingleArgParamOptional<*> }
+ assertThat(req).hasSize(1)
+ assertThat(opt).hasSize(1)
+
+ val reqSub = subCommands.filter { it is RequiredSubCommand<*> }
+ val optSub = subCommands.filter { it is OptionalSubCommand<*> }
+ assertThat(reqSub).hasSize(1)
+ assertThat(optSub).hasSize(1)
+ }
+
+ @Test
+ fun factoryMethods_enforceShortNameRules() {
+ // Short names MUST be one character long
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val flag by flag("longName", "invalidShortName")
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val param by param("longName", "invalidShortName", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+ }
+
+ @Test
+ fun factoryMethods_enforceLongNames_notPrefixed() {
+ // Long names must not start with "-", since they will be added
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val flag by flag("--invalid")
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+
+ assertThrows(IllegalArgumentException::class.java) {
+ val myCommand =
+ object : ParseableCommand("test-command") {
+ val param by param("-invalid", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+ }
+ }
+
+ @Test
+ fun executeDoesNotPropagateExceptions() {
+ val cmd =
+ object : ParseableCommand("test-command") {
+ val flag by flag("flag")
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val throwingCommand = listOf("unknown-token")
+
+ // Given a command that would cause an ArgParseError
+ assertThrows(ArgParseError::class.java) { cmd.parser.parse(throwingCommand) }
+
+ // The parser consumes that error
+ cmd.execute(pw, throwingCommand)
+ }
+
+ @Test
+ fun executeFailingCommand_callsOnParseFailed() {
+ val cmd =
+ object : ParseableCommand("test-command") {
+ val flag by flag("flag")
+
+ var onParseFailedCalled = false
+
+ override fun execute(pw: PrintWriter) {}
+ override fun onParseFailed(error: ArgParseError) {
+ onParseFailedCalled = true
+ }
+ }
+
+ val throwingCommand = listOf("unknown-token")
+ cmd.execute(pw, throwingCommand)
+
+ assertTrue(cmd.onParseFailedCalled)
+ }
+
+ @Test
+ fun baseCommand() {
+ val myCommand = MyCommand()
+ myCommand.execute(pw, baseCommand)
+
+ assertThat(myCommand.flag1).isFalse()
+ assertThat(myCommand.singleParam).isNull()
+ }
+
+ @Test
+ fun commandWithFlags() {
+ val command = MyCommand()
+ command.execute(pw, cmdWithFlags)
+
+ assertThat(command.flag1).isTrue()
+ assertThat(command.flag2).isTrue()
+ }
+
+ @Test
+ fun commandWithArgs() {
+ val cmd = MyCommand()
+ cmd.execute(pw, cmdWithSingleArgParam)
+
+ assertThat(cmd.singleParam).isEqualTo("single_param")
+ }
+
+ @Test
+ fun commandWithRequiredParam_provided() {
+ val cmd =
+ object : ParseableCommand(name) {
+ val singleRequiredParam: String by
+ param(
+ longName = "param1",
+ shortName = "p",
+ valueParser = Type.String,
+ )
+ .required()
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val cli = listOf("-p", "value")
+ cmd.execute(pw, cli)
+
+ assertThat(cmd.singleRequiredParam).isEqualTo("value")
+ }
+
+ @Test
+ fun commandWithRequiredParam_not_provided_throws() {
+ val cmd =
+ object : ParseableCommand(name) {
+ val singleRequiredParam by
+ param(shortName = "p", longName = "param1", valueParser = Type.String)
+ .required()
+
+ override fun execute(pw: PrintWriter) {}
+
+ override fun execute(pw: PrintWriter, args: List<String>) {
+ parser.parse(args)
+ execute(pw)
+ }
+ }
+
+ val cli = listOf("")
+ assertThrows(ArgParseError::class.java) { cmd.execute(pw, cli) }
+ }
+
+ @Test
+ fun commandWithSubCommand() {
+ val subName = "sub-command"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ val singleOptionalParam: String? by param("param", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val cmd =
+ object : ParseableCommand(name) {
+ val subCmd by subCommand(subCmd)
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ cmd.execute(pw, listOf("sub-command", "--param", "test"))
+ assertThat(cmd.subCmd?.singleOptionalParam).isEqualTo("test")
+ }
+
+ @Test
+ fun complexCommandWithSubCommands_reusedNames() {
+ val commandLine = "-f --param1 arg1 sub-command1 -f -p arg2 --param2 arg3".split(" ")
+
+ val subName = "sub-command1"
+ val subCmd =
+ object : ParseableCommand(subName) {
+ val flag1 by flag("flag", shortName = "f")
+ val param1: String? by param("param1", shortName = "p", valueParser = Type.String)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ val myCommand =
+ object : ParseableCommand(name) {
+ val flag1 by flag(longName = "flag", shortName = "f")
+ val param1 by param("param1", shortName = "p", valueParser = Type.String).required()
+ val param2: String? by param(longName = "param2", valueParser = Type.String)
+ val subCommand by subCommand(subCmd)
+
+ override fun execute(pw: PrintWriter) {}
+ }
+
+ myCommand.execute(pw, commandLine)
+
+ assertThat(myCommand.flag1).isTrue()
+ assertThat(myCommand.param1).isEqualTo("arg1")
+ assertThat(myCommand.param2).isEqualTo("arg3")
+ assertThat(myCommand.subCommand).isNotNull()
+ assertThat(myCommand.subCommand?.flag1).isTrue()
+ assertThat(myCommand.subCommand?.param1).isEqualTo("arg2")
+ }
+
+ class MyCommand(
+ private val onExecute: ((MyCommand) -> Unit)? = null,
+ ) : ParseableCommand(name) {
+
+ val flag1 by flag(shortName = "f", longName = "flag1", description = "flag 1 for test")
+ val flag2 by flag(shortName = "g", longName = "flag2", description = "flag 2 for test")
+ val singleParam: String? by
+ param(
+ shortName = "a",
+ longName = "arg1",
+ valueParser = Type.String,
+ )
+
+ override fun execute(pw: PrintWriter) {
+ onExecute?.invoke(this)
+ }
+ }
+
+ companion object {
+ const val name = "my_command"
+ val baseCommand = listOf("")
+ val cmdWithFlags = listOf("-f", "--flag2")
+ val cmdWithSingleArgParam = listOf("--arg1", "single_param")
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
new file mode 100644
index 0000000..759f0bc
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/commandline/ValueParserTest.kt
@@ -0,0 +1,61 @@
+package com.android.systemui.statusbar.commandline
+
+import android.graphics.Rect
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Assert.assertTrue
+import org.junit.Test
+
+@SmallTest
+class ValueParserTest : SysuiTestCase() {
+ @Test
+ fun parseString() {
+ assertThat(Type.String.parseValue("test")).isEqualTo(Result.success("test"))
+ }
+
+ @Test
+ fun parseInt() {
+ assertThat(Type.Int.parseValue("123")).isEqualTo(Result.success(123))
+
+ assertTrue(Type.Int.parseValue("not an Int").isFailure)
+ }
+
+ @Test
+ fun parseFloat() {
+ assertThat(Type.Float.parseValue("1.23")).isEqualTo(Result.success(1.23f))
+
+ assertTrue(Type.Int.parseValue("not a Float").isFailure)
+ }
+
+ @Test
+ fun parseBoolean() {
+ assertThat(Type.Boolean.parseValue("true")).isEqualTo(Result.success(true))
+ assertThat(Type.Boolean.parseValue("false")).isEqualTo(Result.success(false))
+
+ assertTrue(Type.Boolean.parseValue("not a Boolean").isFailure)
+ }
+
+ @Test
+ fun mapToComplexType() {
+ val parseSquare = Type.Int.map { Rect(it, it, it, it) }
+
+ assertThat(parseSquare.parseValue("10")).isEqualTo(Result.success(Rect(10, 10, 10, 10)))
+ }
+
+ @Test
+ fun mapToFallibleComplexType() {
+ val fallibleParseSquare =
+ Type.Int.map {
+ if (it > 0) {
+ Rect(it, it, it, it)
+ } else {
+ null
+ }
+ }
+
+ assertThat(fallibleParseSquare.parseValue("10"))
+ .isEqualTo(Result.success(Rect(10, 10, 10, 10)))
+ assertTrue(fallibleParseSquare.parseValue("-10").isFailure)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
index 914301f..2af0ceb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/events/SystemStatusAnimationSchedulerImplTest.kt
@@ -69,6 +69,8 @@
@Mock private lateinit var listener: SystemStatusAnimationCallback
+ @Mock private lateinit var logger: SystemStatusAnimationSchedulerLogger
+
private lateinit var systemClock: FakeSystemClock
private lateinit var chipAnimationController: SystemEventChipAnimationController
private lateinit var systemStatusAnimationScheduler: SystemStatusAnimationScheduler
@@ -538,7 +540,8 @@
statusBarWindowController,
dumpManager,
systemClock,
- CoroutineScope(StandardTestDispatcher(testScope.testScheduler))
+ CoroutineScope(StandardTestDispatcher(testScope.testScheduler)),
+ logger
)
// add a mock listener
systemStatusAnimationScheduler.addCallback(listener)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
index a37c386..902dd51 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/NotificationWakeUpCoordinatorLoggerTest.kt
@@ -20,8 +20,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.StatusBarState
import com.google.common.truth.Truth.assertThat
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
index 7707a7e..47c5e5b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/logging/StackStateLoggerTest.kt
@@ -20,8 +20,8 @@
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.log.LogBuffer
-import com.android.systemui.log.LogLevel
import com.android.systemui.log.LogcatEchoTracker
+import com.android.systemui.log.core.LogLevel
import com.android.systemui.statusbar.notification.stack.StackStateLogger
import com.google.common.truth.Truth
import org.junit.Before
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
index 6ae7dca..ee8325e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutControllerTest.java
@@ -87,6 +87,7 @@
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController.NotificationPanelEvent;
+import com.android.systemui.statusbar.notification.stack.NotificationSwipeHelper.NotificationCallback;
import com.android.systemui.statusbar.notification.stack.ui.viewmodel.NotificationListViewModel;
import com.android.systemui.statusbar.phone.HeadsUpManagerPhone;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
@@ -415,6 +416,36 @@
}
@Test
+ public void callSwipeCallbacksDuringClearAll() {
+ initController(/* viewIsAttached= */ true);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ NotificationCallback notificationCallback = mController.mNotificationCallback;
+
+ when(mNotificationStackScrollLayout.getClearAllInProgress()).thenReturn(true);
+
+ notificationCallback.onBeginDrag(row);
+ verify(mNotificationStackScrollLayout).onSwipeBegin(row);
+
+ notificationCallback.handleChildViewDismissed(row);
+ verify(mNotificationStackScrollLayout).onSwipeEnd();
+ }
+
+ @Test
+ public void callSwipeCallbacksDuringClearNotification() {
+ initController(/* viewIsAttached= */ true);
+ ExpandableNotificationRow row = mock(ExpandableNotificationRow.class);
+ NotificationCallback notificationCallback = mController.mNotificationCallback;
+
+ when(mNotificationStackScrollLayout.getClearAllInProgress()).thenReturn(false);
+
+ notificationCallback.onBeginDrag(row);
+ verify(mNotificationStackScrollLayout).onSwipeBegin(row);
+
+ notificationCallback.handleChildViewDismissed(row);
+ verify(mNotificationStackScrollLayout).onSwipeEnd();
+ }
+
+ @Test
public void testOnMenuClickedLogging() {
ExpandableNotificationRow row = mock(ExpandableNotificationRow.class, RETURNS_DEEP_STUBS);
when(row.getEntry().getSbn().getLogMaker()).thenReturn(new LogMaker(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
index 036b8be..8545b89 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesCommandQueueCallbacksTest.java
@@ -20,6 +20,7 @@
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import static org.mockito.Mockito.when;
@@ -144,23 +145,32 @@
@Test
public void testDisableNotificationShade() {
- when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mCentralSurfaces.getDisabled2()).thenReturn(StatusBarManager.DISABLE_NONE);
+ // Start with nothing disabled
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NONE, false);
+
when(mCommandQueue.panelsEnabled()).thenReturn(false);
+ // WHEN the new disable flags have the shade disabled
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+ // THEN the shade is collapsed
verify(mShadeController).animateCollapseShade();
}
@Test
public void testEnableNotificationShade() {
- when(mCentralSurfaces.getDisabled1()).thenReturn(StatusBarManager.DISABLE_NONE);
- when(mCentralSurfaces.getDisabled2())
- .thenReturn(StatusBarManager.DISABLE2_NOTIFICATION_SHADE);
+ // Start with the shade disabled
+ mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
+ StatusBarManager.DISABLE2_NOTIFICATION_SHADE, false);
+ reset(mShadeController);
+
when(mCommandQueue.panelsEnabled()).thenReturn(true);
+ // WHEN the new disable flags have the shade enabled
mSbcqCallbacks.disable(DEFAULT_DISPLAY, StatusBarManager.DISABLE_NONE,
StatusBarManager.DISABLE2_NONE, false);
+
+ // THEN the shade is not collapsed
verify(mShadeController, never()).animateCollapseShade();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 1ffffe4..88d8dfc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -450,7 +450,7 @@
() -> mAssistManager,
() -> mNotificationGutsManager
));
- mShadeController.setNotificationPanelViewController(mNotificationPanelViewController);
+ mShadeController.setShadeViewController(mNotificationPanelViewController);
mShadeController.setNotificationShadeWindowViewController(
mNotificationShadeWindowViewController);
mShadeController.setNotificationPresenter(mNotificationPresenter);
@@ -490,9 +490,11 @@
mMetricsLogger,
mShadeLogger,
mUiBgExecutor,
+ mNotificationPanelViewController,
mNotificationMediaManager,
mLockscreenUserManager,
mRemoteInputManager,
+ mQuickSettingsController,
mUserSwitcherController,
mBatteryController,
mColorExtractor,
@@ -587,8 +589,6 @@
// TODO: we should be able to call mCentralSurfaces.start() and have all the below values
// initialized automatically and make NPVC private.
mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
- mCentralSurfaces.mShadeSurface = mNotificationPanelViewController;
- mCentralSurfaces.mQsController = mQuickSettingsController;
mCentralSurfaces.mDozeScrimController = mDozeScrimController;
mCentralSurfaces.mPresenter = mNotificationPresenter;
mCentralSurfaces.mKeyguardIndicationController = mKeyguardIndicationController;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
new file mode 100644
index 0000000..47671fb
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LockscreenWallpaperTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.statusbar.phone
+
+import android.app.WallpaperManager
+import android.content.pm.UserInfo
+import android.os.Looper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.user.data.model.SelectionStatus
+import com.android.systemui.user.data.repository.FakeUserRepository
+import com.android.systemui.util.kotlin.JavaAdapter
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.utils.os.FakeHandler
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+
+@SmallTest
+@OptIn(ExperimentalCoroutinesApi::class)
+class LockscreenWallpaperTest : SysuiTestCase() {
+
+ private lateinit var underTest: LockscreenWallpaper
+
+ private val testScope = TestScope(StandardTestDispatcher())
+ private val userRepository = FakeUserRepository()
+
+ private val wallpaperManager: WallpaperManager = mock()
+
+ @Before
+ fun setUp() {
+ whenever(wallpaperManager.isLockscreenLiveWallpaperEnabled).thenReturn(false)
+ whenever(wallpaperManager.isWallpaperSupported).thenReturn(true)
+ underTest =
+ LockscreenWallpaper(
+ /* wallpaperManager= */ wallpaperManager,
+ /* iWallpaperManager= */ mock(),
+ /* keyguardUpdateMonitor= */ mock(),
+ /* dumpManager= */ mock(),
+ /* mediaManager= */ mock(),
+ /* mainHandler= */ FakeHandler(Looper.getMainLooper()),
+ /* javaAdapter= */ JavaAdapter(testScope.backgroundScope),
+ /* userRepository= */ userRepository,
+ /* userTracker= */ mock(),
+ )
+ underTest.start()
+ }
+
+ @Test
+ fun getBitmap_matchesUserIdFromUserRepo() =
+ testScope.runTest {
+ val info = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info))
+ userRepository.setSelectedUserInfo(info)
+
+ underTest.bitmap
+
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+
+ @Test
+ fun getBitmap_usesOldUserIfNewUserInProgress() =
+ testScope.runTest {
+ val info5 = UserInfo(/* id= */ 5, /* name= */ "id5", /* flags= */ 0)
+ val info6 = UserInfo(/* id= */ 6, /* name= */ "id6", /* flags= */ 0)
+ userRepository.setUserInfos(listOf(info5, info6))
+ userRepository.setSelectedUserInfo(info5)
+
+ // WHEN the selection of user 6 is only in progress
+ userRepository.setSelectedUserInfo(
+ info6,
+ selectionStatus = SelectionStatus.SELECTION_IN_PROGRESS
+ )
+
+ underTest.bitmap
+
+ // THEN we still use user 5 for wallpaper selection
+ verify(wallpaperManager).getWallpaperFile(any(), eq(5))
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
index 74bcdee..862eb00 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/prod/MobileConnectionsRepositoryTest.kt
@@ -73,7 +73,6 @@
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
-import org.junit.Assert.assertThrows
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
@@ -588,11 +587,10 @@
}
@Test
- fun testConnectionRepository_invalidSubId_throws() =
+ fun testConnectionRepository_invalidSubId_doesNotThrow() =
testScope.runTest {
- assertThrows(IllegalArgumentException::class.java) {
- underTest.getRepoForSubId(SUB_1_ID)
- }
+ underTest.getRepoForSubId(SUB_1_ID)
+ // No exception
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
index 6301fa0..842d548 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ui/viewmodel/CollapsedStatusBarViewModelImplTest.kt
@@ -20,7 +20,7 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
-import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -48,7 +48,11 @@
keyguardTransitionRepository = FakeKeyguardTransitionRepository()
val interactor =
- KeyguardTransitionInteractor(keyguardTransitionRepository, testScope.backgroundScope)
+ KeyguardTransitionInteractorFactory.create(
+ scope = TestScope().backgroundScope,
+ repository = keyguardTransitionRepository,
+ )
+ .keyguardTransitionInteractor
underTest = CollapsedStatusBarViewModelImpl(interactor, testScope.backgroundScope)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
index 079fbcd..0c28cbb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/data/repository/UserRepositoryImplTest.kt
@@ -26,6 +26,8 @@
import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.settings.FakeUserTracker
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import com.android.systemui.util.settings.FakeSettings
import com.google.common.truth.Truth.assertThat
@@ -224,6 +226,40 @@
}
@Test
+ fun userTrackerCallback_updatesSelectionStatus() = runSelfCancelingTest {
+ underTest = create(this)
+ var selectedUser: SelectedUserModel? = null
+ underTest.selectedUser.onEach { selectedUser = it }.launchIn(this)
+ setUpUsers(count = 2, selectedIndex = 1)
+
+ // WHEN the user is changing
+ tracker.onUserChanging(userId = 1)
+
+ // THEN the selection status is IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN the user has finished changing
+ tracker.onUserChanged(userId = 1)
+
+ // THEN the selection status is COMPLETE
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ tracker.onProfileChanged()
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_COMPLETE)
+
+ setUpUsers(count = 2, selectedIndex = 0)
+
+ tracker.onUserChanging(userId = 0)
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+
+ // WHEN a profile change occurs while a user is changing
+ tracker.onProfileChanged()
+
+ // THEN the selection status remains as IN_PROGRESS
+ assertThat(selectedUser!!.selectionStatus).isEqualTo(SelectionStatus.SELECTION_IN_PROGRESS)
+ }
+
+ @Test
fun userSwitchingInProgress_registersUserTrackerCallback() = runSelfCancelingTest {
underTest = create(this)
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
new file mode 100644
index 0000000..312ade5
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorFactory.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import kotlinx.coroutines.CoroutineScope
+
+/**
+ * Helper to create a new KeyguardTransitionInteractor in a way that doesn't require modifying 20+
+ * tests whenever we add a constructor param.
+ */
+object KeyguardTransitionInteractorFactory {
+ @JvmOverloads
+ @JvmStatic
+ fun create(
+ scope: CoroutineScope,
+ repository: KeyguardTransitionRepository = FakeKeyguardTransitionRepository(),
+ ): WithDependencies {
+ return WithDependencies(
+ repository = repository,
+ KeyguardTransitionInteractor(
+ scope = scope,
+ repository = repository,
+ )
+ )
+ }
+
+ data class WithDependencies(
+ val repository: KeyguardTransitionRepository,
+ val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+ )
+}
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 61e5b5f..51ee0c0 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
@@ -19,18 +19,28 @@
import android.content.pm.UserInfo
import android.os.UserHandle
+import com.android.systemui.user.data.model.SelectedUserModel
+import com.android.systemui.user.data.model.SelectionStatus
import com.android.systemui.user.data.model.UserSwitcherSettingsModel
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.flow.filterNotNull
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.yield
class FakeUserRepository : UserRepository {
companion object {
// User id to represent a non system (human) user id. We presume this is the main user.
private const val MAIN_USER_ID = 10
+
+ private val DEFAULT_SELECTED_USER = 0
+ private val DEFAULT_SELECTED_USER_INFO =
+ UserInfo(
+ /* id= */ DEFAULT_SELECTED_USER,
+ /* name= */ "default selected user",
+ /* flags= */ 0,
+ )
}
private val _userSwitcherSettings = MutableStateFlow(UserSwitcherSettingsModel())
@@ -40,8 +50,11 @@
private val _userInfos = MutableStateFlow<List<UserInfo>>(emptyList())
override val userInfos: Flow<List<UserInfo>> = _userInfos.asStateFlow()
- private val _selectedUserInfo = MutableStateFlow<UserInfo?>(null)
- override val selectedUserInfo: Flow<UserInfo> = _selectedUserInfo.filterNotNull()
+ override val selectedUser =
+ MutableStateFlow(
+ SelectedUserModel(DEFAULT_SELECTED_USER_INFO, SelectionStatus.SELECTION_COMPLETE)
+ )
+ override val selectedUserInfo: Flow<UserInfo> = selectedUser.map { it.userInfo }
private val _userSwitchingInProgress = MutableStateFlow(false)
override val userSwitchingInProgress: Flow<Boolean>
@@ -72,7 +85,7 @@
}
override fun getSelectedUserInfo(): UserInfo {
- return checkNotNull(_selectedUserInfo.value)
+ return selectedUser.value.userInfo
}
override fun isSimpleUserSwitcher(): Boolean {
@@ -87,12 +100,15 @@
_userInfos.value = infos
}
- suspend fun setSelectedUserInfo(userInfo: UserInfo) {
+ suspend fun setSelectedUserInfo(
+ userInfo: UserInfo,
+ selectionStatus: SelectionStatus = SelectionStatus.SELECTION_COMPLETE,
+ ) {
check(_userInfos.value.contains(userInfo)) {
"Cannot select the following user, it is not in the list of user infos: $userInfo!"
}
- _selectedUserInfo.value = userInfo
+ selectedUser.value = SelectedUserModel(userInfo, selectionStatus)
yield()
}
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index c2ebddf..502ee4d 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -75,6 +75,7 @@
import android.util.Size;
import android.view.Surface;
+import androidx.annotation.NonNull;
import androidx.camera.extensions.impl.AutoImageCaptureExtenderImpl;
import androidx.camera.extensions.impl.AutoPreviewExtenderImpl;
import androidx.camera.extensions.impl.BeautyImageCaptureExtenderImpl;
@@ -204,7 +205,7 @@
* A per-process global camera extension manager instance, to track and
* initialize/release extensions depending on client activity.
*/
- private static final class CameraExtensionManagerGlobal {
+ private static final class CameraExtensionManagerGlobal implements IBinder.DeathRecipient {
private static final String TAG = "CameraExtensionManagerGlobal";
private final int EXTENSION_DELAY_MS = 1000;
@@ -212,8 +213,9 @@
private final HandlerThread mHandlerThread;
private final Object mLock = new Object();
- private long mCurrentClientCount = 0;
- private ArraySet<Long> mActiveClients = new ArraySet<>();
+ private ArraySet<IBinder> mActiveClients = new ArraySet<>();
+ private HashMap<IBinder, ArraySet<IBinder.DeathRecipient>> mClientDeathRecipient =
+ new HashMap<>();
private IInitializeSessionCallback mInitializeCb = null;
// Singleton instance
@@ -314,8 +316,20 @@
return GLOBAL_CAMERA_MANAGER;
}
- public long registerClient(Context ctx) {
+ public boolean registerClient(Context ctx, IBinder token) {
synchronized (mLock) {
+ if (mActiveClients.contains(token)) {
+ Log.e(TAG, "Failed to register existing client!");
+ return false;
+ }
+
+ try {
+ token.linkToDeath(this, 0);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to link to binder token!");
+ return false;
+ }
+
if (INIT_API_SUPPORTED) {
if (mActiveClients.isEmpty()) {
InitializerFuture status = new InitializerFuture();
@@ -327,43 +341,76 @@
TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
Log.e(TAG, "Timed out while initializing camera extensions!");
- return -1;
+ return false;
}
if (!initSuccess) {
Log.e(TAG, "Failed while initializing camera extensions!");
- return -1;
+ return false;
}
}
}
- long ret = mCurrentClientCount;
- mCurrentClientCount++;
- if (mCurrentClientCount < 0) {
- mCurrentClientCount = 0;
- }
- mActiveClients.add(ret);
+ mActiveClients.add(token);
+ mClientDeathRecipient.put(token, new ArraySet<>());
- return ret;
+ return true;
}
}
- public void unregisterClient(long clientId) {
+ public void unregisterClient(IBinder token) {
synchronized (mLock) {
- if (mActiveClients.remove(clientId) && mActiveClients.isEmpty() &&
- INIT_API_SUPPORTED) {
- InitializerFuture status = new InitializerFuture();
- InitializerImpl.deinit(new ReleaseHandler(status),
- new HandlerExecutor(mHandler));
- boolean releaseSuccess;
- try {
- releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS);
- } catch (TimeoutException e) {
- Log.e(TAG, "Timed out while releasing camera extensions!");
- return;
+ if (mActiveClients.remove(token)) {
+ token.unlinkToDeath(this, 0);
+ mClientDeathRecipient.remove(token);
+ if (mActiveClients.isEmpty() && INIT_API_SUPPORTED) {
+ InitializerFuture status = new InitializerFuture();
+ InitializerImpl.deinit(new ReleaseHandler(status),
+ new HandlerExecutor(mHandler));
+ boolean releaseSuccess;
+ try {
+ releaseSuccess = status.get(EXTENSION_DELAY_MS, TimeUnit.MILLISECONDS);
+ } catch (TimeoutException e) {
+ Log.e(TAG, "Timed out while releasing camera extensions!");
+ return;
+ }
+ if (!releaseSuccess) {
+ Log.e(TAG, "Failed while releasing camera extensions!");
+ }
}
- if (!releaseSuccess) {
- Log.e(TAG, "Failed while releasing camera extensions!");
- }
+ }
+ }
+ }
+
+ @Override
+ public void binderDied() {
+ // Do nothing, handled below
+ }
+
+ @Override
+ public void binderDied(@NonNull IBinder who) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(who)) {
+ mClientDeathRecipient.get(who).stream().forEach(
+ recipient -> recipient.binderDied(who));
+ }
+ unregisterClient(who);
+ }
+ }
+
+ public void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(token)) {
+ ArraySet<IBinder.DeathRecipient> recipients = mClientDeathRecipient.get(token);
+ recipients.add(recipient);
+ }
+ }
+ }
+
+ public void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ synchronized (mLock) {
+ if (mClientDeathRecipient.containsKey(token)) {
+ ArraySet<IBinder.DeathRecipient> recipients = mClientDeathRecipient.get(token);
+ recipients.remove(recipient);
}
}
}
@@ -406,21 +453,35 @@
/**
* @hide
*/
- private static long registerClient(Context ctx) {
+ private static boolean registerClient(Context ctx, IBinder token) {
if (!EXTENSIONS_PRESENT) {
- return -1;
+ return false;
}
- return CameraExtensionManagerGlobal.get().registerClient(ctx);
+ return CameraExtensionManagerGlobal.get().registerClient(ctx, token);
}
/**
* @hide
*/
- public static void unregisterClient(long clientId) {
+ public static void unregisterClient(IBinder token) {
if (!EXTENSIONS_PRESENT) {
return;
}
- CameraExtensionManagerGlobal.get().unregisterClient(clientId);
+ CameraExtensionManagerGlobal.get().unregisterClient(token);
+ }
+
+ /**
+ * @hide
+ */
+ private static void registerDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ CameraExtensionManagerGlobal.get().registerDeathRecipient(token, recipient);
+ }
+
+ /**
+ * @hide
+ */
+ private static void unregisterDeathRecipient(IBinder token, IBinder.DeathRecipient recipient) {
+ CameraExtensionManagerGlobal.get().unregisterDeathRecipient(token, recipient);
}
/**
@@ -649,13 +710,14 @@
private class CameraExtensionsProxyServiceStub extends ICameraExtensionsProxyService.Stub {
@Override
- public long registerClient() {
- return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this);
+ public boolean registerClient(IBinder token) {
+ return CameraExtensionsProxyService.registerClient(CameraExtensionsProxyService.this,
+ token);
}
@Override
- public void unregisterClient(long clientId) {
- CameraExtensionsProxyService.unregisterClient(clientId);
+ public void unregisterClient(IBinder token) {
+ CameraExtensionsProxyService.unregisterClient(token);
}
private boolean checkCameraPermission() {
@@ -1192,16 +1254,18 @@
}
}
- private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub {
+ private class SessionProcessorImplStub extends ISessionProcessorImpl.Stub implements
+ IBinder.DeathRecipient {
private final SessionProcessorImpl mSessionProcessor;
private String mCameraId = null;
+ private IBinder mToken;
public SessionProcessorImplStub(SessionProcessorImpl sessionProcessor) {
mSessionProcessor = sessionProcessor;
}
@Override
- public CameraSessionConfig initSession(String cameraId,
+ public CameraSessionConfig initSession(IBinder token, String cameraId,
Map<String, CameraMetadataNative> charsMapNative, OutputSurface previewSurface,
OutputSurface imageCaptureSurface, OutputSurface postviewSurface) {
OutputSurfaceImplStub outputPreviewSurfaceImpl =
@@ -1253,12 +1317,14 @@
ret.sessionParameter = initializeParcelableMetadata(
sessionConfig.getSessionParameters(), cameraId);
mCameraId = cameraId;
-
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
return ret;
}
@Override
- public void deInitSession() {
+ public void deInitSession(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mSessionProcessor.deInitSession();
}
@@ -1330,6 +1396,11 @@
return null;
}
+
+ @Override
+ public void binderDied() {
+ mSessionProcessor.deInitSession();
+ }
}
private class OutputSurfaceConfigurationImplStub implements OutputSurfaceConfigurationImpl {
@@ -1395,24 +1466,31 @@
}
}
- private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub {
+ private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements
+ IBinder.DeathRecipient {
private final PreviewExtenderImpl mPreviewExtender;
private String mCameraId = null;
+ private boolean mSessionEnabled;
+ private IBinder mToken;
public PreviewExtenderImplStub(PreviewExtenderImpl previewExtender) {
mPreviewExtender = previewExtender;
}
@Override
- public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
+ public void onInit(IBinder token, String cameraId,
+ CameraMetadataNative cameraCharacteristics) {
mCameraId = cameraId;
CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
mCameraManager.registerDeviceStateListener(chars);
mPreviewExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
}
@Override
- public void onDeInit() {
+ public void onDeInit(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mPreviewExtender.onDeInit();
}
@@ -1423,11 +1501,13 @@
@Override
public CaptureStageImpl onEnableSession() {
+ mSessionEnabled = true;
return initializeParcelable(mPreviewExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
+ mSessionEnabled = false;
return initializeParcelable(mPreviewExtender.onDisableSession(), mCameraId);
}
@@ -1516,26 +1596,41 @@
}
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mSessionEnabled) {
+ mPreviewExtender.onDisableSession();
+ }
+ mPreviewExtender.onDeInit();
+ }
}
- private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub {
+ private class ImageCaptureExtenderImplStub extends IImageCaptureExtenderImpl.Stub implements
+ IBinder.DeathRecipient {
private final ImageCaptureExtenderImpl mImageExtender;
private String mCameraId = null;
+ private boolean mSessionEnabled;
+ private IBinder mToken;
public ImageCaptureExtenderImplStub(ImageCaptureExtenderImpl imageExtender) {
mImageExtender = imageExtender;
}
@Override
- public void onInit(String cameraId, CameraMetadataNative cameraCharacteristics) {
+ public void onInit(IBinder token, String cameraId,
+ CameraMetadataNative cameraCharacteristics) {
CameraCharacteristics chars = new CameraCharacteristics(cameraCharacteristics);
mCameraManager.registerDeviceStateListener(chars);
mImageExtender.onInit(cameraId, chars, CameraExtensionsProxyService.this);
mCameraId = cameraId;
+ mToken = token;
+ CameraExtensionsProxyService.registerDeathRecipient(mToken, this);
}
@Override
- public void onDeInit() {
+ public void onDeInit(IBinder token) {
+ CameraExtensionsProxyService.unregisterDeathRecipient(mToken, this);
mImageExtender.onDeInit();
}
@@ -1564,11 +1659,13 @@
@Override
public CaptureStageImpl onEnableSession() {
+ mSessionEnabled = true;
return initializeParcelable(mImageExtender.onEnableSession(), mCameraId);
}
@Override
public CaptureStageImpl onDisableSession() {
+ mSessionEnabled = false;
return initializeParcelable(mImageExtender.onDisableSession(), mCameraId);
}
@@ -1737,6 +1834,14 @@
return null;
}
+
+ @Override
+ public void binderDied() {
+ if (mSessionEnabled) {
+ mImageExtender.onDisableSession();
+ }
+ mImageExtender.onDeInit();
+ }
}
private class ProcessResultCallback implements ProcessResultImpl {
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
index b1cdc50..ba3d434 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/FullScreenMagnificationController.java
@@ -319,6 +319,10 @@
FullScreenMagnificationController::onUserContextChanged,
FullScreenMagnificationController.this, mDisplayId);
mControllerCtx.getHandler().sendMessage(m);
+
+ synchronized (mLock) {
+ refreshThumbnail();
+ }
}
@Override
@@ -344,7 +348,7 @@
mMagnificationRegion.set(magnified);
mMagnificationRegion.getBounds(mMagnificationBounds);
- refreshThumbnail(getScale(), getCenterX(), getCenterY());
+ refreshThumbnail();
// It's possible that our magnification spec is invalid with the new bounds.
// Adjust the current spec's offsets if necessary.
@@ -602,13 +606,13 @@
}
@GuardedBy("mLock")
- void refreshThumbnail(float scale, float centerX, float centerY) {
+ void refreshThumbnail() {
if (mMagnificationThumbnail != null) {
mMagnificationThumbnail.setThumbnailBounds(
mMagnificationBounds,
- scale,
- centerX,
- centerY
+ getScale(),
+ getCenterX(),
+ getCenterY()
);
}
}
@@ -627,7 +631,7 @@
// We call refreshThumbnail when the thumbnail is just created to set current
// magnification bounds to thumbnail. It to prevent the thumbnail size has not yet
// updated properly and thus shows with huge size. (b/276314641)
- refreshThumbnail(getScale(), getCenterX(), getCenterY());
+ refreshThumbnail();
}
}
diff --git a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
index 03fa93d..a7bdd5a 100644
--- a/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
+++ b/services/accessibility/java/com/android/server/accessibility/magnification/MagnificationThumbnail.java
@@ -99,15 +99,17 @@
Log.d(LOG_TAG, "setThumbnailBounds " + currentBounds);
}
mHandler.post(() -> {
- mWindowBounds = currentBounds;
- setBackgroundBounds();
+ refreshBackgroundBounds(currentBounds);
if (mVisible) {
updateThumbnailMainThread(scale, centerX, centerY);
}
});
}
- private void setBackgroundBounds() {
+ @MainThread
+ private void refreshBackgroundBounds(Rect currentBounds) {
+ mWindowBounds = currentBounds;
+
Point magnificationBoundary = getMagnificationThumbnailPadding(mContext);
mThumbnailWidth = (int) (mWindowBounds.width() / BG_ASPECT_RATIO);
mThumbnailHeight = (int) (mWindowBounds.height() / BG_ASPECT_RATIO);
@@ -117,6 +119,10 @@
mBackgroundParams.height = mThumbnailHeight;
mBackgroundParams.x = initX;
mBackgroundParams.y = initY;
+
+ if (mVisible) {
+ mWindowManager.updateViewLayout(mThumbnailLayout, mBackgroundParams);
+ }
}
@MainThread
@@ -264,21 +270,16 @@
mThumbnailView.setScaleX(scaleDown);
mThumbnailView.setScaleY(scaleDown);
}
- float thumbnailWidth;
- float thumbnailHeight;
- if (mThumbnailView.getWidth() == 0 || mThumbnailView.getHeight() == 0) {
- // if the thumbnail view size is not updated correctly, we just use the cached values.
- thumbnailWidth = mThumbnailWidth;
- thumbnailHeight = mThumbnailHeight;
- } else {
- thumbnailWidth = mThumbnailView.getWidth();
- thumbnailHeight = mThumbnailView.getHeight();
- }
- if (!Float.isNaN(centerX)) {
+
+ if (!Float.isNaN(centerX)
+ && !Float.isNaN(centerY)
+ && mThumbnailWidth > 0
+ && mThumbnailHeight > 0
+ ) {
var padding = mThumbnailView.getPaddingTop();
var ratio = 1f / BG_ASPECT_RATIO;
- var centerXScaled = centerX * ratio - (thumbnailWidth / 2f + padding);
- var centerYScaled = centerY * ratio - (thumbnailHeight / 2f + padding);
+ var centerXScaled = centerX * ratio - (mThumbnailWidth / 2f + padding);
+ var centerYScaled = centerY * ratio - (mThumbnailHeight / 2f + padding);
if (DEBUG) {
Log.d(
diff --git a/services/core/java/com/android/server/am/PendingIntentController.java b/services/core/java/com/android/server/am/PendingIntentController.java
index 7ee96aa..a20623c 100644
--- a/services/core/java/com/android/server/am/PendingIntentController.java
+++ b/services/core/java/com/android/server/am/PendingIntentController.java
@@ -26,6 +26,7 @@
import android.annotation.Nullable;
import android.app.Activity;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.PendingIntent;
import android.app.PendingIntentStats;
@@ -126,6 +127,18 @@
}
}
Bundle.setDefusable(bOptions, true);
+ ActivityOptions opts = ActivityOptions.fromBundle(bOptions);
+ if (opts != null && opts.getPendingIntentBackgroundActivityStartMode()
+ != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ Slog.wtf(TAG, "Resetting option setPendingIntentBackgroundActivityStartMode("
+ + opts.getPendingIntentBackgroundActivityStartMode()
+ + ") to SYSTEM_DEFINED from the options provided by the pending "
+ + "intent creator ("
+ + packageName
+ + ") because this option is meant for the pending intent sender");
+ opts.setPendingIntentBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+ }
final boolean noCreate = (flags & PendingIntent.FLAG_NO_CREATE) != 0;
final boolean cancelCurrent = (flags & PendingIntent.FLAG_CANCEL_CURRENT) != 0;
@@ -135,7 +148,7 @@
PendingIntentRecord.Key key = new PendingIntentRecord.Key(type, packageName, featureId,
token, resultWho, requestCode, intents, resolvedTypes, flags,
- SafeActivityOptions.fromBundle(bOptions), userId);
+ new SafeActivityOptions(opts), userId);
WeakReference<PendingIntentRecord> ref;
ref = mIntentSenderRecords.get(key);
PendingIntentRecord rec = ref != null ? ref.get() : null;
diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java
index 202d407..a0e76f1 100644
--- a/services/core/java/com/android/server/am/PendingIntentRecord.java
+++ b/services/core/java/com/android/server/am/PendingIntentRecord.java
@@ -457,6 +457,20 @@
// can specify a consistent launch mode even if the PendingIntent is immutable
final ActivityOptions opts = ActivityOptions.fromBundle(options);
if (opts != null) {
+ if (opts.getPendingIntentCreatorBackgroundActivityStartMode()
+ != ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED) {
+ Slog.wtf(TAG,
+ "Resetting option "
+ + "setPendingIntentCreatorBackgroundActivityStartMode("
+ + opts.getPendingIntentCreatorBackgroundActivityStartMode()
+ + ") to SYSTEM_DEFINED from the options provided by the "
+ + "pending intent sender ("
+ + key.packageName
+ + ") because this option is meant for the pending intent "
+ + "creator");
+ opts.setPendingIntentCreatorBackgroundActivityStartMode(
+ ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_SYSTEM_DEFINED);
+ }
finalIntent.addFlags(opts.getPendingIntentLaunchFlags());
}
diff --git a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
index e498384..753fdae 100644
--- a/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
+++ b/services/core/java/com/android/server/am/ProcessErrorStateRecord.java
@@ -51,11 +51,11 @@
import com.android.internal.annotations.CompositeRWLock;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.modules.expresslog.Counter;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.TimeoutRecord;
import com.android.internal.os.anr.AnrLatencyTracker;
import com.android.internal.util.FrameworkStatsLog;
+import com.android.modules.expresslog.Counter;
import com.android.server.ResourcePressureUtil;
import com.android.server.criticalevents.CriticalEventLog;
import com.android.server.stats.pull.ProcfsMemoryUtil.MemorySnapshot;
@@ -456,6 +456,11 @@
String currentPsiState = ResourcePressureUtil.currentPsiState();
latencyTracker.currentPsiStateReturned();
report.append(currentPsiState);
+ // The 'processCpuTracker' variable is a shared resource that might be initialized and
+ // updated in a different thread. In order to prevent thread visibility issues, which
+ // can occur when one thread does not immediately see the changes made to
+ // 'processCpuTracker' by another thread, it is necessary to use synchronization whenever
+ // 'processCpuTracker' is accessed or modified.
ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(true);
// We push the native pids collection task to the helper thread through
@@ -517,12 +522,16 @@
}
mService.updateCpuStatsNow();
mService.mAppProfiler.printCurrentCpuState(report, anrTime);
- info.append(processCpuTracker.printCurrentLoad());
+ synchronized (processCpuTracker) {
+ info.append(processCpuTracker.printCurrentLoad());
+ }
info.append(report);
}
report.append(tracesFileException.getBuffer());
- info.append(processCpuTracker.printCurrentState(anrTime));
+ synchronized (processCpuTracker) {
+ info.append(processCpuTracker.printCurrentState(anrTime));
+ }
Slog.e(TAG, info.toString());
if (tracesFile == null) {
diff --git a/services/core/java/com/android/server/am/StackTracesDumpHelper.java b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
index cf69b53..ba0fd17 100644
--- a/services/core/java/com/android/server/am/StackTracesDumpHelper.java
+++ b/services/core/java/com/android/server/am/StackTracesDumpHelper.java
@@ -464,28 +464,31 @@
latencyTracker.processCpuTrackerMethodsCalled();
}
ArrayList<Integer> extraPids = new ArrayList<>();
- processCpuTracker.init();
+ synchronized (processCpuTracker) {
+ processCpuTracker.init();
+ }
try {
Thread.sleep(200);
} catch (InterruptedException ignored) {
}
- processCpuTracker.update();
+ synchronized (processCpuTracker) {
+ processCpuTracker.update();
+ // We'll take the stack crawls of just the top apps using CPU.
+ final int workingStatsNumber = processCpuTracker.countWorkingStats();
+ for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
+ ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
+ if (lastPids.indexOfKey(stats.pid) >= 0) {
+ if (DEBUG_ANR) {
+ Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ }
- // We'll take the stack crawls of just the top apps using CPU.
- final int workingStatsNumber = processCpuTracker.countWorkingStats();
- for (int i = 0; i < workingStatsNumber && extraPids.size() < 2; i++) {
- ProcessCpuTracker.Stats stats = processCpuTracker.getWorkingStats(i);
- if (lastPids.indexOfKey(stats.pid) >= 0) {
- if (DEBUG_ANR) {
- Slog.d(TAG, "Collecting stacks for extra pid " + stats.pid);
+ extraPids.add(stats.pid);
+ } else {
+ Slog.i(TAG,
+ "Skipping next CPU consuming process, not a java proc: "
+ + stats.pid);
}
-
- extraPids.add(stats.pid);
- } else {
- Slog.i(TAG,
- "Skipping next CPU consuming process, not a java proc: "
- + stats.pid);
}
}
if (latencyTracker != null) {
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 76a994e..99c2f8a 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1972,7 +1972,7 @@
}
private void dismissUserSwitchDialog(Runnable onDismissed) {
- mInjector.dismissUserSwitchingDialog(onDismissed);
+ mUiHandler.post(() -> mInjector.dismissUserSwitchingDialog(onDismissed));
}
private void showUserSwitchDialog(Pair<UserInfo, UserInfo> fromToUserPair) {
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index e4a5a3e..ca15dd7 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -2166,7 +2166,7 @@
@Override
public void onUidStateChanged(int uid, int procState, long procStateSeq, int capability) {
synchronized (mUidObserverLock) {
- if (ActivityManager.isProcStateBackground(procState)) {
+ if (procState != ActivityManager.PROCESS_STATE_TOP) {
disableGameMode(uid);
return;
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 29a1941..393e430 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -30,6 +30,8 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioDeviceInfo;
import android.media.AudioManager;
+import android.media.AudioPlaybackConfiguration;
+import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo;
@@ -289,37 +291,38 @@
* @param on
* @param eventSource for logging purposes
*/
- /*package*/ void setSpeakerphoneOn(IBinder cb, int pid, boolean on, String eventSource) {
+ /*package*/ void setSpeakerphoneOn(
+ IBinder cb, int uid, boolean on, boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "setSpeakerphoneOn, on: " + on + " pid: " + pid);
+ Log.v(TAG, "setSpeakerphoneOn, on: " + on + " uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
- cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
- on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
+ cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_SPEAKER, ""),
+ on, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
}
/**
* Select device for use for communication use cases.
* @param cb Client binder for death detection
- * @param pid Client pid
+ * @param uid Client uid
* @param device Device selected or null to unselect.
* @param eventSource for logging purposes
*/
private static final long SET_COMMUNICATION_DEVICE_TIMEOUT_MS = 3000;
- /*package*/ boolean setCommunicationDevice(
- IBinder cb, int pid, AudioDeviceInfo device, String eventSource) {
+ /*package*/ boolean setCommunicationDevice(IBinder cb, int uid, AudioDeviceInfo device,
+ boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "setCommunicationDevice, device: " + device + ", pid: " + pid);
+ Log.v(TAG, "setCommunicationDevice, device: " + device + ", uid: " + uid);
}
AudioDeviceAttributes deviceAttr =
(device != null) ? new AudioDeviceAttributes(device) : null;
- CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, pid, deviceAttr,
- device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true);
+ CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(cb, uid, deviceAttr,
+ device != null, BtHelper.SCO_MODE_UNDEFINED, eventSource, true, isPrivileged);
postSetCommunicationDeviceForClient(deviceInfo);
boolean status;
synchronized (deviceInfo) {
@@ -353,7 +356,7 @@
Log.v(TAG, "onSetCommunicationDeviceForClient: " + deviceInfo);
}
if (!deviceInfo.mOn) {
- CommunicationRouteClient client = getCommunicationRouteClientForPid(deviceInfo.mPid);
+ CommunicationRouteClient client = getCommunicationRouteClientForUid(deviceInfo.mUid);
if (client == null || (deviceInfo.mDevice != null
&& !deviceInfo.mDevice.equals(client.getDevice()))) {
return false;
@@ -361,22 +364,23 @@
}
AudioDeviceAttributes device = deviceInfo.mOn ? deviceInfo.mDevice : null;
- setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mPid, device,
- deviceInfo.mScoAudioMode, deviceInfo.mEventSource);
+ setCommunicationRouteForClient(deviceInfo.mCb, deviceInfo.mUid, device,
+ deviceInfo.mScoAudioMode, deviceInfo.mIsPrivileged, deviceInfo.mEventSource);
return true;
}
@GuardedBy("mDeviceStateLock")
/*package*/ void setCommunicationRouteForClient(
- IBinder cb, int pid, AudioDeviceAttributes device,
- int scoAudioMode, String eventSource) {
+ IBinder cb, int uid, AudioDeviceAttributes device,
+ int scoAudioMode, boolean isPrivileged, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "setCommunicationRouteForClient: device: " + device);
+ Log.v(TAG, "setCommunicationRouteForClient: device: " + device
+ + ", eventSource: " + eventSource);
}
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "setCommunicationRouteForClient for pid: " + pid
- + " device: " + device
+ "setCommunicationRouteForClient for uid: " + uid
+ + " device: " + device + " isPrivileged: " + isPrivileged
+ " from API: " + eventSource)).printLog(TAG));
final boolean wasBtScoRequested = isBluetoothScoRequested();
@@ -385,16 +389,18 @@
// Save previous client route in case of failure to start BT SCO audio
AudioDeviceAttributes prevClientDevice = null;
- client = getCommunicationRouteClientForPid(pid);
+ boolean prevPrivileged = false;
+ client = getCommunicationRouteClientForUid(uid);
if (client != null) {
prevClientDevice = client.getDevice();
+ prevPrivileged = client.isPrivileged();
}
if (device != null) {
- client = addCommunicationRouteClient(cb, pid, device);
+ client = addCommunicationRouteClient(cb, uid, device, isPrivileged);
if (client == null) {
- Log.w(TAG, "setCommunicationRouteForClient: could not add client for pid: "
- + pid + " and device: " + device);
+ Log.w(TAG, "setCommunicationRouteForClient: could not add client for uid: "
+ + uid + " and device: " + device);
}
} else {
client = removeCommunicationRouteClient(cb, true);
@@ -406,11 +412,11 @@
boolean isBtScoRequested = isBluetoothScoRequested();
if (isBtScoRequested && (!wasBtScoRequested || !isBluetoothScoActive())) {
if (!mBtHelper.startBluetoothSco(scoAudioMode, eventSource)) {
- Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for pid: "
- + pid);
+ Log.w(TAG, "setCommunicationRouteForClient: failure to start BT SCO for uid: "
+ + uid);
// clean up or restore previous client selection
if (prevClientDevice != null) {
- addCommunicationRouteClient(cb, pid, prevClientDevice);
+ addCommunicationRouteClient(cb, uid, prevClientDevice, prevPrivileged);
} else {
removeCommunicationRouteClient(cb, true);
}
@@ -447,11 +453,12 @@
@GuardedBy("mDeviceStateLock")
private CommunicationRouteClient topCommunicationRouteClient() {
for (CommunicationRouteClient crc : mCommunicationRouteClients) {
- if (crc.getPid() == mAudioModeOwner.mPid) {
+ if (crc.getUid() == mAudioModeOwner.mUid) {
return crc;
}
}
- if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0) {
+ if (!mCommunicationRouteClients.isEmpty() && mAudioModeOwner.mPid == 0
+ && mCommunicationRouteClients.get(0).isActive()) {
return mCommunicationRouteClients.get(0);
}
return null;
@@ -491,14 +498,48 @@
};
/*package */ static boolean isValidCommunicationDevice(AudioDeviceInfo device) {
+ return isValidCommunicationDeviceType(device.getType());
+ }
+
+ private static boolean isValidCommunicationDeviceType(int deviceType) {
for (int type : VALID_COMMUNICATION_DEVICE_TYPES) {
- if (device.getType() == type) {
+ if (deviceType == type) {
return true;
}
}
return false;
}
+ /*package */
+ void postCheckCommunicationDeviceRemoval(@NonNull AudioDeviceAttributes device) {
+ if (!isValidCommunicationDeviceType(
+ AudioDeviceInfo.convertInternalDeviceToDeviceType(device.getInternalType()))) {
+ return;
+ }
+ sendLMsgNoDelay(MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL, SENDMSG_QUEUE, device);
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ void onCheckCommunicationDeviceRemoval(@NonNull AudioDeviceAttributes device) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onCheckCommunicationDeviceRemoval device: " + device.toString());
+ }
+ for (CommunicationRouteClient crc : mCommunicationRouteClients) {
+ if (device.equals(crc.getDevice())) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onCheckCommunicationDeviceRemoval removing client: "
+ + crc.toString());
+ }
+ // Cancelling the route for this client will remove it from the stack and update
+ // the communication route.
+ CommunicationDeviceInfo deviceInfo = new CommunicationDeviceInfo(
+ crc.getBinder(), crc.getUid(), device, false,
+ BtHelper.SCO_MODE_UNDEFINED, "onCheckCommunicationDeviceRemoval",
+ false, crc.isPrivileged());
+ postSetCommunicationDeviceForClient(deviceInfo);
+ }
+ }
+ }
/* package */ static List<AudioDeviceInfo> getAvailableCommunicationDevices() {
ArrayList<AudioDeviceInfo> commDevices = new ArrayList<>();
AudioDeviceInfo[] allDevices =
@@ -1107,26 +1148,26 @@
sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
}
- /*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
- @NonNull String eventSource) {
+ /*package*/ void startBluetoothScoForClient(IBinder cb, int uid, int scoAudioMode,
+ boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "startBluetoothScoForClient, pid: " + pid);
+ Log.v(TAG, "startBluetoothScoForClient, uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
- cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
- true, scoAudioMode, eventSource, false));
+ cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+ true, scoAudioMode, eventSource, false, isPrivileged));
}
/*package*/ void stopBluetoothScoForClient(
- IBinder cb, int pid, @NonNull String eventSource) {
+ IBinder cb, int uid, boolean isPrivileged, @NonNull String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
- Log.v(TAG, "stopBluetoothScoForClient, pid: " + pid);
+ Log.v(TAG, "stopBluetoothScoForClient, uid: " + uid);
}
postSetCommunicationDeviceForClient(new CommunicationDeviceInfo(
- cb, pid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
- false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false));
+ cb, uid, new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, ""),
+ false, BtHelper.SCO_MODE_UNDEFINED, eventSource, false, isPrivileged));
}
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
@@ -1367,22 +1408,24 @@
/*package*/ static final class CommunicationDeviceInfo {
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
- final int mPid; // Requester process ID
+ final int mUid; // Requester UID
final @Nullable AudioDeviceAttributes mDevice; // Device being set or reset.
final boolean mOn; // true if setting, false if resetting
final int mScoAudioMode; // only used for SCO: requested audio mode
+ final boolean mIsPrivileged; // true if the client app has MODIFY_PHONE_STATE permission
final @NonNull String mEventSource; // caller identifier for logging
boolean mWaitForStatus; // true if the caller waits for a completion status (API dependent)
boolean mStatus = false; // completion status only used if mWaitForStatus is true
- CommunicationDeviceInfo(@NonNull IBinder cb, int pid,
+ CommunicationDeviceInfo(@NonNull IBinder cb, int uid,
@Nullable AudioDeviceAttributes device, boolean on, int scoAudioMode,
- @NonNull String eventSource, boolean waitForStatus) {
+ @NonNull String eventSource, boolean waitForStatus, boolean isPrivileged) {
mCb = cb;
- mPid = pid;
+ mUid = uid;
mDevice = device;
mOn = on;
mScoAudioMode = scoAudioMode;
+ mIsPrivileged = isPrivileged;
mEventSource = eventSource;
mWaitForStatus = waitForStatus;
}
@@ -1401,16 +1444,17 @@
}
return mCb.equals(((CommunicationDeviceInfo) o).mCb)
- && mPid == ((CommunicationDeviceInfo) o).mPid;
+ && mUid == ((CommunicationDeviceInfo) o).mUid;
}
@Override
public String toString() {
return "CommunicationDeviceInfo mCb=" + mCb.toString()
- + " mPid=" + mPid
+ + " mUid=" + mUid
+ " mDevice=[" + (mDevice != null ? mDevice.toString() : "null") + "]"
+ " mOn=" + mOn
+ " mScoAudioMode=" + mScoAudioMode
+ + " mIsPrivileged=" + mIsPrivileged
+ " mEventSource=" + mEventSource
+ " mWaitForStatus=" + mWaitForStatus
+ " mStatus=" + mStatus;
@@ -1440,7 +1484,7 @@
}
}
- /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes,
+ /*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
boolean connect, @Nullable BluetoothDevice btDevice) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.handleDeviceConnection(
@@ -1507,8 +1551,7 @@
pw.println("\n" + prefix + "Communication route clients:");
mCommunicationRouteClients.forEach((cl) -> {
- pw.println(" " + prefix + "pid: " + cl.getPid() + " device: "
- + cl.getDevice() + " cb: " + cl.getBinder()); });
+ pw.println(" " + prefix + cl.toString()); });
pw.println("\n" + prefix + "Computed Preferred communication device: "
+ preferredCommunicationDevice());
@@ -1850,6 +1893,15 @@
final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
} break;
+
+ case MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL: {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onCheckCommunicationDeviceRemoval((AudioDeviceAttributes) msg.obj);
+ }
+ }
+ } break;
+
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1926,6 +1978,7 @@
private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 52;
+ private static final int MSG_L_CHECK_COMMUNICATION_DEVICE_REMOVAL = 53;
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
@@ -2101,13 +2154,20 @@
private class CommunicationRouteClient implements IBinder.DeathRecipient {
private final IBinder mCb;
- private final int mPid;
+ private final int mUid;
+ private final boolean mIsPrivileged;
private AudioDeviceAttributes mDevice;
+ private boolean mPlaybackActive;
+ private boolean mRecordingActive;
- CommunicationRouteClient(IBinder cb, int pid, AudioDeviceAttributes device) {
+ CommunicationRouteClient(IBinder cb, int uid, AudioDeviceAttributes device,
+ boolean isPrivileged) {
mCb = cb;
- mPid = pid;
+ mUid = uid;
mDevice = device;
+ mIsPrivileged = isPrivileged;
+ mPlaybackActive = mAudioService.isPlaybackActiveForUid(uid);
+ mRecordingActive = mAudioService.isRecordingActiveForUid(uid);
}
public boolean registerDeathRecipient() {
@@ -2138,13 +2198,38 @@
return mCb;
}
- int getPid() {
- return mPid;
+ int getUid() {
+ return mUid;
+ }
+
+ boolean isPrivileged() {
+ return mIsPrivileged;
}
AudioDeviceAttributes getDevice() {
return mDevice;
}
+
+ public void setPlaybackActive(boolean active) {
+ mPlaybackActive = active;
+ }
+
+ public void setRecordingActive(boolean active) {
+ mRecordingActive = active;
+ }
+
+ public boolean isActive() {
+ return mIsPrivileged || mRecordingActive || mPlaybackActive;
+ }
+
+ @Override
+ public String toString() {
+ return "[CommunicationRouteClient: mUid: " + mUid
+ + " mDevice: " + mDevice.toString()
+ + " mIsPrivileged: " + mIsPrivileged
+ + " mPlaybackActive: " + mPlaybackActive
+ + " mRecordingActive: " + mRecordingActive + "]";
+ }
}
// @GuardedBy("mSetModeLock")
@@ -2154,8 +2239,9 @@
return;
}
Log.w(TAG, "Communication client died");
- setCommunicationRouteForClient(client.getBinder(), client.getPid(), null,
- BtHelper.SCO_MODE_UNDEFINED, "onCommunicationRouteClientDied");
+ setCommunicationRouteForClient(client.getBinder(), client.getUid(), null,
+ BtHelper.SCO_MODE_UNDEFINED, client.isPrivileged(),
+ "onCommunicationRouteClientDied");
}
/**
@@ -2242,8 +2328,8 @@
+ crc + " eventSource: " + eventSource);
}
if (crc != null) {
- setCommunicationRouteForClient(crc.getBinder(), crc.getPid(), crc.getDevice(),
- BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ setCommunicationRouteForClient(crc.getBinder(), crc.getUid(), crc.getDevice(),
+ BtHelper.SCO_MODE_UNDEFINED, crc.isPrivileged(), eventSource);
}
}
@@ -2267,6 +2353,7 @@
dispatchCommunicationDevice();
}
+ @GuardedBy("mDeviceStateLock")
private CommunicationRouteClient removeCommunicationRouteClient(
IBinder cb, boolean unregister) {
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
@@ -2282,11 +2369,12 @@
}
@GuardedBy("mDeviceStateLock")
- private CommunicationRouteClient addCommunicationRouteClient(
- IBinder cb, int pid, AudioDeviceAttributes device) {
+ private CommunicationRouteClient addCommunicationRouteClient(IBinder cb, int uid,
+ AudioDeviceAttributes device, boolean isPrivileged) {
// always insert new request at first position
removeCommunicationRouteClient(cb, true);
- CommunicationRouteClient client = new CommunicationRouteClient(cb, pid, device);
+ CommunicationRouteClient client =
+ new CommunicationRouteClient(cb, uid, device, isPrivileged);
if (client.registerDeathRecipient()) {
mCommunicationRouteClients.add(0, client);
return client;
@@ -2295,9 +2383,9 @@
}
@GuardedBy("mDeviceStateLock")
- private CommunicationRouteClient getCommunicationRouteClientForPid(int pid) {
+ private CommunicationRouteClient getCommunicationRouteClientForUid(int uid) {
for (CommunicationRouteClient cl : mCommunicationRouteClients) {
- if (cl.getPid() == pid) {
+ if (cl.getUid() == uid) {
return cl;
}
}
@@ -2330,6 +2418,45 @@
return device;
}
+ void updateCommunicationRouteClientsActivity(
+ List<AudioPlaybackConfiguration> playbackConfigs,
+ List<AudioRecordingConfiguration> recordConfigs) {
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ boolean updateCommunicationRoute = false;
+ for (CommunicationRouteClient crc : mCommunicationRouteClients) {
+ boolean wasActive = crc.isActive();
+ if (playbackConfigs != null) {
+ crc.setPlaybackActive(false);
+ for (AudioPlaybackConfiguration config : playbackConfigs) {
+ if (config.getClientUid() == crc.getUid()
+ && config.isActive()) {
+ crc.setPlaybackActive(true);
+ break;
+ }
+ }
+ }
+ if (recordConfigs != null) {
+ crc.setRecordingActive(false);
+ for (AudioRecordingConfiguration config : recordConfigs) {
+ if (config.getClientUid() == crc.getUid()
+ && !config.isClientSilenced()) {
+ crc.setRecordingActive(true);
+ break;
+ }
+ }
+ }
+ if (wasActive != crc.isActive()) {
+ updateCommunicationRoute = true;
+ }
+ }
+ if (updateCommunicationRoute) {
+ postUpdateCommunicationRouteClient("updateCommunicationRouteClientsActivity");
+ }
+ }
+ }
+ }
+
@Nullable UUID getDeviceSensorUuid(AudioDeviceAttributes device) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.getDeviceSensorUuid(device);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 0c7f11f..b70e11d 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -1245,8 +1245,9 @@
* @param btDevice the corresponding Bluetooth device when relevant.
* @return false if an error was reported by AudioSystem
*/
- /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
- boolean isForTesting, @Nullable BluetoothDevice btDevice) {
+ /*package*/ boolean handleDeviceConnection(@NonNull AudioDeviceAttributes attributes,
+ boolean connect, boolean isForTesting,
+ @Nullable BluetoothDevice btDevice) {
int device = attributes.getInternalType();
String address = attributes.getAddress();
String deviceName = attributes.getName();
@@ -1297,6 +1298,7 @@
AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
+ mDeviceBroker.postCheckCommunicationDeviceRemoval(attributes);
status = true;
}
if (status) {
@@ -1801,8 +1803,9 @@
// device to remove was visible by APM, update APM
mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
- final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
+ final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
@@ -1816,11 +1819,13 @@
"A2DP device addr=" + address + " made unavailable")).printLog(TAG));
}
mApmConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
+
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
mmi.record();
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
+ mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@GuardedBy("mDevicesLock")
@@ -1855,12 +1860,14 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcUnavailable(String address) {
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address);
+ mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
+ mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
@GuardedBy("mDevicesLock")
@@ -1893,8 +1900,9 @@
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
- mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_HEARING_AID, address),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_HEARING_AID, address);
+ mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
@@ -1906,6 +1914,7 @@
.set(MediaMetrics.Property.DEVICE,
AudioSystem.getDeviceName(AudioSystem.DEVICE_OUT_HEARING_AID))
.record();
+ mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
}
/**
@@ -2002,9 +2011,10 @@
@GuardedBy("mDevicesLock")
private void makeLeAudioDeviceUnavailableNow(String address, int device) {
+ AudioDeviceAttributes ada = null;
if (device != AudioSystem.DEVICE_NONE) {
- final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- device, address),
+ ada = new AudioDeviceAttributes(device, address);
+ final int res = AudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
@@ -2024,6 +2034,9 @@
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
updateBluetoothPreferredModes_l(null /*connectedDevice*/);
purgeDevicesRoles_l();
+ if (ada != null) {
+ mDeviceBroker.postCheckCommunicationDeviceRemoval(ada);
+ }
}
@GuardedBy("mDevicesLock")
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 53ed38e..b70b2b3 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -4262,22 +4262,41 @@
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
// and request an audio mode update immediately. Upon any other change, queue the message
// and request an audio mode update after a grace period.
+ updateAudioModeHandlers(
+ configs /* playbackConfigs */, null /* recordConfigs */);
+ mDeviceBroker.updateCommunicationRouteClientsActivity(
+ configs /* playbackConfigs */, null /* recordConfigs */);
+ }
+
+ void updateAudioModeHandlers(List<AudioPlaybackConfiguration> playbackConfigs,
+ List<AudioRecordingConfiguration> recordConfigs) {
synchronized (mDeviceBroker.mSetModeLock) {
boolean updateAudioMode = false;
int existingMsgPolicy = SENDMSG_QUEUE;
int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
for (SetModeDeathHandler h : mSetModeDeathHandlers) {
boolean wasActive = h.isActive();
- h.setPlaybackActive(false);
- for (AudioPlaybackConfiguration config : configs) {
- final int usage = config.getAudioAttributes().getUsage();
- if (config.getClientUid() == h.getUid()
- && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
+ if (playbackConfigs != null) {
+ h.setPlaybackActive(false);
+ for (AudioPlaybackConfiguration config : playbackConfigs) {
+ final int usage = config.getAudioAttributes().getUsage();
+ if (config.getClientUid() == h.getUid()
+ && (usage == AudioAttributes.USAGE_VOICE_COMMUNICATION
|| usage == AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
- && config.getPlayerState()
- == AudioPlaybackConfiguration.PLAYER_STATE_STARTED) {
- h.setPlaybackActive(true);
- break;
+ && config.isActive()) {
+ h.setPlaybackActive(true);
+ break;
+ }
+ }
+ }
+ if (recordConfigs != null) {
+ h.setRecordingActive(false);
+ for (AudioRecordingConfiguration config : recordConfigs) {
+ if (config.getClientUid() == h.getUid() && !config.isClientSilenced()
+ && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
+ h.setRecordingActive(true);
+ break;
+ }
}
}
if (wasActive != h.isActive()) {
@@ -4315,38 +4334,10 @@
// When the audio mode owner becomes active, replace any delayed MSG_UPDATE_AUDIO_MODE
// and request an audio mode update immediately. Upon any other change, queue the message
// and request an audio mode update after a grace period.
- synchronized (mDeviceBroker.mSetModeLock) {
- boolean updateAudioMode = false;
- int existingMsgPolicy = SENDMSG_QUEUE;
- int delay = CHECK_MODE_FOR_UID_PERIOD_MS;
- for (SetModeDeathHandler h : mSetModeDeathHandlers) {
- boolean wasActive = h.isActive();
- h.setRecordingActive(false);
- for (AudioRecordingConfiguration config : configs) {
- if (config.getClientUid() == h.getUid()
- && config.getAudioSource() == AudioSource.VOICE_COMMUNICATION) {
- h.setRecordingActive(true);
- break;
- }
- }
- if (wasActive != h.isActive()) {
- updateAudioMode = true;
- if (h.isActive() && h == getAudioModeOwnerHandler()) {
- existingMsgPolicy = SENDMSG_REPLACE;
- delay = 0;
- }
- }
- }
- if (updateAudioMode) {
- sendMsg(mAudioHandler,
- MSG_UPDATE_AUDIO_MODE,
- existingMsgPolicy,
- AudioSystem.MODE_CURRENT,
- android.os.Process.myPid(),
- mContext.getPackageName(),
- delay);
- }
- }
+ updateAudioModeHandlers(
+ null /* playbackConfigs */, configs /* recordConfigs */);
+ mDeviceBroker.updateCommunicationRouteClientsActivity(
+ null /* playbackConfigs */, configs /* recordConfigs */);
}
private void dumpAudioMode(PrintWriter pw) {
@@ -6299,10 +6290,12 @@
? MediaMetrics.Value.CONNECTED : MediaMetrics.Value.DISCONNECTED)
.record();
}
-
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
- return mDeviceBroker.setCommunicationDevice(cb, pid, device, eventSource);
+ return mDeviceBroker.setCommunicationDevice(cb, uid, device, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6348,6 +6341,9 @@
if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
return;
}
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
// for logging only
final int uid = Binder.getCallingUid();
@@ -6363,9 +6359,10 @@
.set(MediaMetrics.Property.STATE, on
? MediaMetrics.Value.ON : MediaMetrics.Value.OFF)
.record();
+
final long ident = Binder.clearCallingIdentity();
try {
- mDeviceBroker.setSpeakerphoneOn(cb, pid, on, eventSource);
+ mDeviceBroker.setSpeakerphoneOn(cb, uid, on, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6490,7 +6487,7 @@
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(scoAudioMode))
.record();
- startBluetoothScoInt(cb, pid, scoAudioMode, eventSource);
+ startBluetoothScoInt(cb, uid, scoAudioMode, eventSource);
}
@@ -6513,10 +6510,10 @@
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
BtHelper.scoAudioModeToString(BtHelper.SCO_MODE_VIRTUAL_CALL))
.record();
- startBluetoothScoInt(cb, pid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
+ startBluetoothScoInt(cb, uid, BtHelper.SCO_MODE_VIRTUAL_CALL, eventSource);
}
- void startBluetoothScoInt(IBinder cb, int pid, int scoAudioMode, @NonNull String eventSource) {
+ void startBluetoothScoInt(IBinder cb, int uid, int scoAudioMode, @NonNull String eventSource) {
MediaMetrics.Item mmi = new MediaMetrics.Item(MediaMetrics.Name.AUDIO_BLUETOOTH)
.set(MediaMetrics.Property.EVENT, "startBluetoothScoInt")
.set(MediaMetrics.Property.SCO_AUDIO_MODE,
@@ -6527,9 +6524,13 @@
mmi.set(MediaMetrics.Property.EARLY_RETURN, "permission or systemReady").record();
return;
}
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
- mDeviceBroker.startBluetoothScoForClient(cb, pid, scoAudioMode, eventSource);
+ mDeviceBroker.startBluetoothScoForClient(
+ cb, uid, scoAudioMode, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -6547,9 +6548,12 @@
final String eventSource = new StringBuilder("stopBluetoothSco()")
.append(") from u/pid:").append(uid).append("/")
.append(pid).toString();
+ final boolean isPrivileged = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.MODIFY_PHONE_STATE)
+ == PackageManager.PERMISSION_GRANTED;
final long ident = Binder.clearCallingIdentity();
try {
- mDeviceBroker.stopBluetoothScoForClient(cb, pid, eventSource);
+ mDeviceBroker.stopBluetoothScoForClient(cb, uid, isPrivileged, eventSource);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9284,8 +9288,8 @@
break;
}
boolean wasActive = h.isActive();
- h.setPlaybackActive(mPlaybackMonitor.isPlaybackActiveForUid(h.getUid()));
- h.setRecordingActive(mRecordMonitor.isRecordingActiveForUid(h.getUid()));
+ h.setPlaybackActive(isPlaybackActiveForUid(h.getUid()));
+ h.setRecordingActive(isRecordingActiveForUid(h.getUid()));
if (wasActive != h.isActive()) {
onUpdateAudioMode(AudioSystem.MODE_CURRENT, android.os.Process.myPid(),
mContext.getPackageName(), false /*force*/);
@@ -12378,6 +12382,16 @@
}
}
+ /* package */
+ boolean isPlaybackActiveForUid(int uid) {
+ return mPlaybackMonitor.isPlaybackActiveForUid(uid);
+ }
+
+ /* package */
+ boolean isRecordingActiveForUid(int uid) {
+ return mRecordMonitor.isRecordingActiveForUid(uid);
+ }
+
//======================
// Audio device management
//======================
diff --git a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 50ffbcb9..3f4f981 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -957,6 +957,7 @@
players = (HashMap<Integer, AudioPlaybackConfiguration>) mPlayers.clone();
}
mFadingManager.unfadeOutUid(uid, players);
+ mDuckingManager.unduckUid(uid, players);
}
//=================================================================
diff --git a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
index 652ea52..4332fdd 100644
--- a/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/RecordingActivityMonitor.java
@@ -227,8 +227,8 @@
synchronized (mRecordStates) {
for (RecordingState state : mRecordStates) {
// Note: isActiveConfiguration() == true => state.getConfig() != null
- if (state.isActiveConfiguration()
- && state.getConfig().getClientUid() == uid) {
+ if (state.isActiveConfiguration() && state.getConfig().getClientUid() == uid
+ && !state.getConfig().isClientSilenced()) {
return true;
}
}
diff --git a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
index eb7fa10..add94b1 100644
--- a/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
+++ b/services/core/java/com/android/server/devicestate/DeviceStateManagerService.java
@@ -172,6 +172,7 @@
private DeviceState mRearDisplayState;
// TODO(259328837) Generalize for all pending feature requests in the future
+ @GuardedBy("mLock")
@Nullable
private OverrideRequest mRearDisplayPendingOverrideRequest;
@@ -779,7 +780,7 @@
* {@link StatusBarManagerInternal} to notify SystemUI to display the educational dialog.
*/
@GuardedBy("mLock")
- private void showRearDisplayEducationalOverlayLocked(OverrideRequest request) {
+ private void showRearDisplayEducationalOverlayLocked(@NonNull OverrideRequest request) {
mRearDisplayPendingOverrideRequest = request;
StatusBarManagerInternal statusBar =
@@ -844,8 +845,8 @@
* request if it was dismissed in a way that should cancel the feature.
*/
private void onStateRequestOverlayDismissedInternal(boolean shouldCancelRequest) {
- if (mRearDisplayPendingOverrideRequest != null) {
- synchronized (mLock) {
+ synchronized (mLock) {
+ if (mRearDisplayPendingOverrideRequest != null) {
if (shouldCancelRequest) {
ProcessRecord processRecord = mProcessRecords.get(
mRearDisplayPendingOverrideRequest.getPid());
diff --git a/services/core/java/com/android/server/display/BrightnessRangeController.java b/services/core/java/com/android/server/display/BrightnessRangeController.java
index 47cde15..5b11cfe 100644
--- a/services/core/java/com/android/server/display/BrightnessRangeController.java
+++ b/services/core/java/com/android/server/display/BrightnessRangeController.java
@@ -18,29 +18,42 @@
import android.hardware.display.BrightnessInfo;
import android.os.IBinder;
+import android.provider.DeviceConfigInterface;
+
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.function.BooleanSupplier;
class BrightnessRangeController {
- private static final boolean NBM_FEATURE_FLAG = false;
-
private final HighBrightnessModeController mHbmController;
private final NormalBrightnessModeController mNormalBrightnessModeController =
new NormalBrightnessModeController();
private final Runnable mModeChangeCallback;
+ private final boolean mUseNbmController;
+
BrightnessRangeController(HighBrightnessModeController hbmController,
Runnable modeChangeCallback) {
- mHbmController = hbmController;
- mModeChangeCallback = modeChangeCallback;
+ this(hbmController, modeChangeCallback,
+ new DeviceConfigParameterProvider(DeviceConfigInterface.REAL));
}
+ BrightnessRangeController(HighBrightnessModeController hbmController,
+ Runnable modeChangeCallback, DeviceConfigParameterProvider configParameterProvider) {
+ mHbmController = hbmController;
+ mModeChangeCallback = modeChangeCallback;
+ mUseNbmController = configParameterProvider.isNormalBrightnessControllerFeatureEnabled();
+ }
void dump(PrintWriter pw) {
+ pw.println("BrightnessRangeController:");
+ pw.println(" mUseNormalBrightnessController=" + mUseNbmController);
mHbmController.dump(pw);
+ mNormalBrightnessModeController.dump(pw);
+
}
void onAmbientLuxChange(float ambientLux) {
@@ -90,7 +103,7 @@
float getCurrentBrightnessMax() {
- if (NBM_FEATURE_FLAG && mHbmController.getHighBrightnessMode()
+ if (mUseNbmController && mHbmController.getHighBrightnessMode()
== BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF) {
return Math.min(mHbmController.getCurrentBrightnessMax(),
mNormalBrightnessModeController.getCurrentBrightnessMax());
@@ -111,7 +124,7 @@
}
private void applyChanges(BooleanSupplier nbmChangesFunc, Runnable hbmChangesFunc) {
- if (NBM_FEATURE_FLAG) {
+ if (mUseNbmController) {
boolean nbmTransitionChanged = nbmChangesFunc.getAsBoolean();
hbmChangesFunc.run();
// if nbm transition changed - trigger callback
diff --git a/services/core/java/com/android/server/display/BrightnessThrottler.java b/services/core/java/com/android/server/display/BrightnessThrottler.java
index cfdcd63..c421ec0 100644
--- a/services/core/java/com/android/server/display/BrightnessThrottler.java
+++ b/services/core/java/com/android/server/display/BrightnessThrottler.java
@@ -22,7 +22,6 @@
import android.annotation.Nullable;
import android.content.Context;
import android.hardware.display.BrightnessInfo;
-import android.hardware.display.DisplayManager;
import android.os.Handler;
import android.os.HandlerExecutor;
import android.os.IThermalEventListener;
@@ -38,6 +37,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData;
import com.android.server.display.DisplayDeviceConfig.ThermalBrightnessThrottlingData.ThrottlingLevel;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -63,7 +63,7 @@
private final Runnable mThrottlingChangeCallback;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
private final DeviceConfigListener mDeviceConfigListener;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private int mThrottlingStatus;
@@ -118,7 +118,7 @@
mSkinThermalStatusObserver = new SkinThermalStatusObserver(mInjector, mHandler);
mUniqueDisplayId = uniqueDisplayId;
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigListener = new DeviceConfigListener();
mThermalBrightnessThrottlingDataId = throttlingDataId;
mDdcThermalThrottlingDataMap = thermalBrightnessThrottlingDataMap;
@@ -145,7 +145,7 @@
void stop() {
mSkinThermalStatusObserver.stopObserving();
- mDeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigListener);
+ mConfigParameterProvider.removeOnPropertiesChangedListener(mDeviceConfigListener);
// We're asked to stop throttling, so reset brightness restrictions.
mBrightnessCap = PowerManager.BRIGHTNESS_MAX;
mBrightnessMaxReason = BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE;
@@ -248,12 +248,6 @@
mSkinThermalStatusObserver.dump(pw);
}
- private String getThermalBrightnessThrottlingDataString() {
- return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA,
- /* defaultValue= */ null);
- }
-
// The brightness throttling data id may or may not be specified in the string that is passed
// in, if there is none specified, we assume it is for the default case. Each string passed in
// here must be for one display and one throttling id.
@@ -318,7 +312,8 @@
private void loadThermalBrightnessThrottlingDataFromDeviceConfig() {
HashMap<String, HashMap<String, ThermalBrightnessThrottlingData>> tempThrottlingData =
new HashMap<>(1);
- mThermalBrightnessThrottlingDataString = getThermalBrightnessThrottlingDataString();
+ mThermalBrightnessThrottlingDataString =
+ mConfigParameterProvider.getBrightnessThrottlingData();
boolean validConfig = true;
mThermalBrightnessThrottlingDataOverride.clear();
if (mThermalBrightnessThrottlingDataString != null) {
@@ -390,8 +385,7 @@
public Executor mExecutor = new HandlerExecutor(mDeviceConfigHandler);
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- mExecutor, this);
+ mConfigParameterProvider.addOnPropertiesChangedListener(mExecutor, this);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 858800a..dbe15b6 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -24,7 +24,6 @@
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_CACHED;
import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_GONE;
import static android.hardware.display.DisplayManager.EventsMask;
-import static android.hardware.display.DisplayManager.HDR_OUTPUT_CONTROL_FLAG;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
@@ -42,7 +41,6 @@
import static android.hardware.display.HdrConversionMode.HDR_CONVERSION_UNSUPPORTED;
import static android.os.Process.FIRST_APPLICATION_UID;
import static android.os.Process.ROOT_UID;
-import static android.provider.DeviceConfig.NAMESPACE_DISPLAY_MANAGER;
import android.Manifest;
import android.annotation.NonNull;
@@ -116,7 +114,7 @@
import android.os.Trace;
import android.os.UserHandle;
import android.os.UserManager;
-import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.ArraySet;
@@ -152,6 +150,7 @@
import com.android.server.UiThread;
import com.android.server.companion.virtual.VirtualDeviceManagerInternal;
import com.android.server.display.DisplayDeviceConfig.SensorData;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.layout.Layout;
import com.android.server.display.mode.DisplayModeDirector;
import com.android.server.display.utils.SensorUtils;
@@ -506,6 +505,8 @@
private final BrightnessSynchronizer mBrightnessSynchronizer;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
+
/**
* Applications use {@link android.view.Display#getRefreshRate} and
* {@link android.view.Display.Mode#getRefreshRate} to know what is the display refresh rate.
@@ -558,6 +559,7 @@
mWideColorSpace = colorSpaces[1];
mOverlayProperties = SurfaceControl.getOverlaySupport();
mSystemReady = false;
+ mConfigParameterProvider = new DeviceConfigParameterProvider(DeviceConfigInterface.REAL);
}
public void setupSchedulerPolicies() {
@@ -694,11 +696,11 @@
synchronized (mSyncRoot) {
mSafeMode = safeMode;
mSystemReady = true;
- mIsHdrOutputControlEnabled = isDeviceConfigHdrOutputControlEnabled();
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_DISPLAY_MANAGER,
- BackgroundThread.getExecutor(),
+ mIsHdrOutputControlEnabled =
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled();
+ mConfigParameterProvider.addOnPropertiesChangedListener(BackgroundThread.getExecutor(),
properties -> mIsHdrOutputControlEnabled =
- isDeviceConfigHdrOutputControlEnabled());
+ mConfigParameterProvider.isHdrOutputControlFeatureEnabled());
// Just in case the top inset changed before the system was ready. At this point, any
// relevant configuration should be in place.
recordTopInsetLocked(mLogicalDisplayMapper.getDisplayLocked(Display.DEFAULT_DISPLAY));
@@ -729,12 +731,6 @@
mContext.registerReceiver(mIdleModeReceiver, filter);
}
- private boolean isDeviceConfigHdrOutputControlEnabled() {
- return DeviceConfig.getBoolean(NAMESPACE_DISPLAY_MANAGER,
- HDR_OUTPUT_CONTROL_FLAG,
- true);
- }
-
@VisibleForTesting
Handler getDisplayHandler() {
return mHandler;
@@ -3158,8 +3154,7 @@
+ "display: " + display.getDisplayIdLocked());
return null;
}
- if (DeviceConfig.getBoolean("display_manager",
- "use_newly_structured_display_power_controller", true)) {
+ if (mConfigParameterProvider.isNewPowerControllerFeatureEnabled()) {
displayPowerController = new DisplayPowerController2(
mContext, /* injector= */ null, mDisplayPowerCallbacks, mPowerHandler,
mSensorManager, mDisplayBlanker, display, mBrightnessTracker, brightnessSetting,
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 7701bc6..89d865e 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -496,7 +496,7 @@
private void loadDisplayDeviceConfig() {
// Load display device config
final Context context = getOverlayContext();
- mDisplayDeviceConfig = DisplayDeviceConfig.create(context, mPhysicalDisplayId,
+ mDisplayDeviceConfig = mInjector.createDisplayDeviceConfig(context, mPhysicalDisplayId,
mIsFirstDisplay);
// Load brightness HWC quirk
@@ -1336,6 +1336,11 @@
public SurfaceControlProxy getSurfaceControlProxy() {
return new SurfaceControlProxy();
}
+
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay);
+ }
}
public interface DisplayEventListener {
diff --git a/services/core/java/com/android/server/display/NormalBrightnessModeController.java b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
index dbabc24..135ebd8 100644
--- a/services/core/java/com/android/server/display/NormalBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/NormalBrightnessModeController.java
@@ -21,6 +21,7 @@
import com.android.server.display.DisplayDeviceConfig.BrightnessLimitMapType;
+import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@@ -60,6 +61,14 @@
return recalculateMaxBrightness();
}
+ void dump(PrintWriter pw) {
+ pw.println("NormalBrightnessModeController:");
+ pw.println(" mAutoBrightnessEnabled=" + mAutoBrightnessEnabled);
+ pw.println(" mAmbientLux=" + mAmbientLux);
+ pw.println(" mMaxBrightness=" + mMaxBrightness);
+ pw.println(" mMaxBrightnessLimits=" + mMaxBrightnessLimits);
+ }
+
private boolean recalculateMaxBrightness() {
float foundAmbientBoundary = Float.MAX_VALUE;
float foundMaxBrightness = PowerManager.BRIGHTNESS_MAX;
diff --git a/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
new file mode 100644
index 0000000..feebdf1
--- /dev/null
+++ b/services/core/java/com/android/server/display/feature/DeviceConfigParameterProvider.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display.feature;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.hardware.display.DisplayManager;
+import android.provider.DeviceConfig;
+import android.provider.DeviceConfigInterface;
+import android.util.Slog;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Helper class to access all DeviceConfig features for display_manager namespace
+ *
+ **/
+public class DeviceConfigParameterProvider {
+
+ private static final String TAG = "DisplayFeatureProvider";
+
+ private final DeviceConfigInterface mDeviceConfig;
+
+ public DeviceConfigParameterProvider(DeviceConfigInterface deviceConfig) {
+ mDeviceConfig = deviceConfig;
+ }
+
+ // feature: revamping_display_power_controller_feature
+ // parameter: use_newly_structured_display_power_controller
+ public boolean isNewPowerControllerFeatureEnabled() {
+ return mDeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_NEW_POWER_CONTROLLER, true);
+ }
+
+ // feature: hdr_output_control
+ // parameter: enable_hdr_output_control
+ public boolean isHdrOutputControlFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.HDR_OUTPUT_CONTROL_FLAG, true);
+ }
+
+ // feature: flexible_brightness_range_feature
+ // parameter: normal_brightness_mode_controller_enabled
+ public boolean isNormalBrightnessControllerFeatureEnabled() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_USE_NORMAL_BRIGHTNESS_MODE_CONTROLLER, false);
+ }
+
+ // feature: smooth_display_feature
+ // parameter: peak_refresh_rate_default
+ public float getPeakRefreshRateDefault() {
+ return mDeviceConfig.getFloat(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
+ }
+
+ // Test parameters
+ // usage e.g.: adb shell device_config put display_manager refresh_rate_in_hbm_sunlight 90
+
+ // allows to customize brightness throttling data
+ public String getBrightnessThrottlingData() {
+ return mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_BRIGHTNESS_THROTTLING_DATA, null);
+ }
+
+ public int getRefreshRateInHbmSunlight() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT, -1);
+ }
+
+ public int getRefreshRateInHbmHdr() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR, -1);
+ }
+
+
+ public int getRefreshRateInHighZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE, -1);
+ }
+
+ public int getRefreshRateInLowZone() {
+ return mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getHighDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowDisplayBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** Return null if no such property or wrong format (not comma separated integers). */
+ @Nullable
+ public int[] getLowAmbientBrightnessThresholds() {
+ return getIntArrayProperty(DisplayManager.DeviceConfig
+ .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ }
+
+ /** add property change listener to DeviceConfig */
+ public void addOnPropertiesChangedListener(Executor executor,
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ executor, listener);
+ }
+
+ /** remove property change listener from DeviceConfig */
+ public void removeOnPropertiesChangedListener(
+ DeviceConfig.OnPropertiesChangedListener listener) {
+ mDeviceConfig.removeOnPropertiesChangedListener(listener);
+ }
+
+ @Nullable
+ private int[] getIntArrayProperty(String prop) {
+ String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
+ null);
+
+ if (strArray != null) {
+ return parseIntArray(strArray);
+ }
+ return null;
+ }
+
+ @Nullable
+ private int[] parseIntArray(@NonNull String strArray) {
+ String[] items = strArray.split(",");
+ int[] array = new int[items.length];
+
+ try {
+ for (int i = 0; i < array.length; i++) {
+ array[i] = Integer.parseInt(items[i]);
+ }
+ } catch (NumberFormatException e) {
+ Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
+ array = null;
+ }
+
+ return array;
+ }
+}
diff --git a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
index 1889578..11e35ce 100644
--- a/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/mode/DisplayModeDirector.java
@@ -20,6 +20,7 @@
import static android.hardware.display.DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE;
import static android.os.PowerManager.BRIGHTNESS_INVALID;
+import android.annotation.IntegerRes;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ContentResolver;
@@ -68,6 +69,7 @@
import com.android.internal.os.BackgroundThread;
import com.android.server.LocalServices;
import com.android.server.display.DisplayDeviceConfig;
+import com.android.server.display.feature.DeviceConfigParameterProvider;
import com.android.server.display.utils.AmbientFilter;
import com.android.server.display.utils.AmbientFilterFactory;
import com.android.server.display.utils.SensorUtils;
@@ -84,6 +86,7 @@
import java.util.Locale;
import java.util.Objects;
import java.util.concurrent.Callable;
+import java.util.function.IntSupplier;
/**
* The DisplayModeDirector is responsible for determining what modes are allowed to be automatically
@@ -117,7 +120,7 @@
private final SensorObserver mSensorObserver;
private final HbmObserver mHbmObserver;
private final SkinThermalStatusObserver mSkinThermalStatusObserver;
- private final DeviceConfigInterface mDeviceConfig;
+ private final DeviceConfigParameterProvider mConfigParameterProvider;
private final DeviceConfigDisplaySettings mDeviceConfigDisplaySettings;
@GuardedBy("mLock")
@@ -157,7 +160,7 @@
mSupportedModesByDisplay = new SparseArray<>();
mDefaultModeByDisplay = new SparseArray<>();
mAppRequestObserver = new AppRequestObserver();
- mDeviceConfig = injector.getDeviceConfig();
+ mConfigParameterProvider = new DeviceConfigParameterProvider(injector.getDeviceConfig());
mDeviceConfigDisplaySettings = new DeviceConfigDisplaySettings();
mSettingsObserver = new SettingsObserver(context, handler);
mBrightnessObserver = new BrightnessObserver(context, handler, injector);
@@ -681,9 +684,9 @@
synchronized (mLock) {
mDefaultDisplayDeviceConfig = displayDeviceConfig;
mSettingsObserver.setRefreshRates(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.updateBlockingZoneThresholds(displayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ true);
+ /* attemptReadFromFeatureParams= */ true);
mBrightnessObserver.reloadLightSensor(displayDeviceConfig);
mHbmObserver.setupHdrRefreshRates(displayDeviceConfig);
}
@@ -1087,7 +1090,7 @@
// reading from the DeviceConfig is an intensive IO operation and having it in the
// startup phase where we thrive to keep the latency very low has significant impact.
setRefreshRates(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
/**
@@ -1095,8 +1098,8 @@
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
public void setRefreshRates(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- setDefaultPeakRefreshRate(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ boolean attemptReadFromFeatureParams) {
+ setDefaultPeakRefreshRate(displayDeviceConfig, attemptReadFromFeatureParams);
mDefaultRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultRefreshRate)
@@ -1113,9 +1116,9 @@
cr.registerContentObserver(mMatchContentFrameRateSetting, false /*notifyDescendants*/,
this);
- Float deviceConfigDefaultPeakRefresh =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
- if (deviceConfigDefaultPeakRefresh != null) {
+ float deviceConfigDefaultPeakRefresh =
+ mConfigParameterProvider.getPeakRefreshRateDefault();
+ if (deviceConfigDefaultPeakRefresh != -1) {
mDefaultPeakRefreshRate = deviceConfigDefaultPeakRefresh;
}
@@ -1137,7 +1140,7 @@
synchronized (mLock) {
if (defaultPeakRefreshRate == null) {
setDefaultPeakRefreshRate(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
updateRefreshRateSettingLocked();
} else if (mDefaultPeakRefreshRate != defaultPeakRefreshRate) {
mDefaultPeakRefreshRate = defaultPeakRefreshRate;
@@ -1171,18 +1174,17 @@
}
private void setDefaultPeakRefreshRate(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- Float defaultPeakRefreshRate = null;
+ boolean attemptReadFromFeatureParams) {
+ float defaultPeakRefreshRate = -1;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
- defaultPeakRefreshRate =
- mDeviceConfigDisplaySettings.getDefaultPeakRefreshRate();
+ defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
} catch (Exception exception) {
// Do nothing
}
}
- if (defaultPeakRefreshRate == null) {
+ if (defaultPeakRefreshRate == -1) {
defaultPeakRefreshRate =
(displayDeviceConfig == null) ? (float) mContext.getResources().getInteger(
R.integer.config_defaultPeakRefreshRate)
@@ -1528,7 +1530,7 @@
mHandler = handler;
mInjector = injector;
updateBlockingZoneThresholds(/* displayDeviceConfig= */ null,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
mRefreshRateInHighZone = context.getResources().getInteger(
R.integer.config_fixedRefreshRateInHighZone);
}
@@ -1537,10 +1539,10 @@
* This is used to update the blocking zone thresholds from the DeviceConfig, which
* if missing from DisplayDeviceConfig, and finally fallback to config.xml.
*/
- public void updateBlockingZoneThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadLowBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadHighBrightnessThresholds(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ public void updateBlockingZoneThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadLowBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadHighBrightnessThresholds(displayDeviceConfig, attemptReadFromFeatureParams);
}
@VisibleForTesting
@@ -1579,20 +1581,20 @@
return mRefreshRateInLowZone;
}
- private void loadLowBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- loadRefreshRateInHighZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
- loadRefreshRateInLowZone(displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ private void loadLowBrightnessThresholds(@Nullable DisplayDeviceConfig displayDeviceConfig,
+ boolean attemptReadFromFeatureParams) {
+ loadRefreshRateInHighZone(displayDeviceConfig, attemptReadFromFeatureParams);
+ loadRefreshRateInLowZone(displayDeviceConfig, attemptReadFromFeatureParams);
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mLowDisplayBrightnessThresholds.length != mLowAmbientBrightnessThresholds.length) {
throw new RuntimeException("display low brightness threshold array and ambient "
+ "brightness threshold array have different length: "
@@ -1604,55 +1606,55 @@
}
private void loadRefreshRateInLowZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInLowZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInZone)
- : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInLowZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInLowZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE,
- refreshRateInLowZone);
+ refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInLowZone == -1) {
+ refreshRateInLowZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_defaultRefreshRateInZone)
+ : displayDeviceConfig.getDefaultLowBlockingZoneRefreshRate();
+ }
mRefreshRateInLowZone = refreshRateInLowZone;
}
private void loadRefreshRateInHighZone(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
- int refreshRateInHighZone =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_fixedRefreshRateInHighZone) : displayDeviceConfig
- .getDefaultHighBlockingZoneRefreshRate();
- if (attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
+ int refreshRateInHighZone = -1;
+ if (attemptReadFromFeatureParams) {
try {
- refreshRateInHighZone = mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- refreshRateInHighZone);
+ refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
} catch (Exception exception) {
// Do nothing
}
}
+ if (refreshRateInHighZone == -1) {
+ refreshRateInHighZone = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(
+ R.integer.config_fixedRefreshRateInHighZone)
+ : displayDeviceConfig.getDefaultHighBlockingZoneRefreshRate();
+ }
mRefreshRateInHighZone = refreshRateInHighZone;
}
private void loadHighBrightnessThresholds(DisplayDeviceConfig displayDeviceConfig,
- boolean attemptLoadingFromDeviceConfig) {
+ boolean attemptReadFromFeatureParams) {
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, attemptLoadingFromDeviceConfig);
+ displayDeviceConfig, attemptReadFromFeatureParams);
if (mHighDisplayBrightnessThresholds.length
!= mHighAmbientBrightnessThresholds.length) {
throw new RuntimeException("display high brightness threshold array and ambient "
@@ -1668,10 +1670,10 @@
Callable<int[]> loadFromDeviceConfigDisplaySettingsCallable,
Callable<int[]> loadFromDisplayDeviceConfigCallable,
int brightnessThresholdOfFixedRefreshRateKey,
- DisplayDeviceConfig displayDeviceConfig, boolean attemptLoadingFromDeviceConfig) {
+ DisplayDeviceConfig displayDeviceConfig, boolean attemptReadFromFeatureParams) {
int[] brightnessThresholds = null;
- if (attemptLoadingFromDeviceConfig) {
+ if (attemptReadFromFeatureParams) {
try {
brightnessThresholds =
loadFromDeviceConfigDisplaySettingsCallable.call();
@@ -1715,9 +1717,9 @@
// DeviceConfig is accessible after system ready.
int[] lowDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds();
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
int[] lowAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds();
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
if (lowDisplayBrightnessThresholds != null && lowAmbientBrightnessThresholds != null
&& lowDisplayBrightnessThresholds.length
@@ -1727,9 +1729,9 @@
}
int[] highDisplayBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds();
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
int[] highAmbientBrightnessThresholds =
- mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds();
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
if (highDisplayBrightnessThresholds != null && highAmbientBrightnessThresholds != null
&& highDisplayBrightnessThresholds.length
@@ -1738,14 +1740,12 @@
mHighAmbientBrightnessThresholds = highAmbientBrightnessThresholds;
}
- final int refreshRateInLowZone = mDeviceConfigDisplaySettings
- .getRefreshRateInLowZone();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
if (refreshRateInLowZone != -1) {
mRefreshRateInLowZone = refreshRateInLowZone;
}
- final int refreshRateInHighZone = mDeviceConfigDisplaySettings
- .getRefreshRateInHighZone();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
if (refreshRateInHighZone != -1) {
mRefreshRateInHighZone = refreshRateInHighZone;
}
@@ -1799,15 +1799,15 @@
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mLowDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getLowDisplayBrightnessThresholds(),
R.array.config_brightnessThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mLowAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getLowAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getLowAmbientBrightnessThresholds(),
R.array.config_ambientThresholdsOfPeakRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1822,7 +1822,7 @@
// from there.
synchronized (mLock) {
loadRefreshRateInLowZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInLowZone) {
@@ -1843,15 +1843,15 @@
displayDeviceConfig = mDefaultDisplayDeviceConfig;
}
mHighDisplayBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighDisplayBrightnessThresholds(),
+ () -> mConfigParameterProvider.getLowDisplayBrightnessThresholds(),
() -> displayDeviceConfig.getHighDisplayBrightnessThresholds(),
R.array.config_highDisplayBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
mHighAmbientBrightnessThresholds = loadBrightnessThresholds(
- () -> mDeviceConfigDisplaySettings.getHighAmbientBrightnessThresholds(),
+ () -> mConfigParameterProvider.getHighAmbientBrightnessThresholds(),
() -> displayDeviceConfig.getHighAmbientBrightnessThresholds(),
R.array.config_highAmbientBrightnessThresholdsOfFixedRefreshRate,
- displayDeviceConfig, /* attemptLoadingFromDeviceConfig= */ false);
+ displayDeviceConfig, /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
}
@@ -1866,7 +1866,7 @@
// from there.
synchronized (mLock) {
loadRefreshRateInHighZone(mDefaultDisplayDeviceConfig,
- /* attemptLoadingFromDeviceConfig= */ false);
+ /* attemptReadFromFeatureParams= */ false);
}
restartObserver();
} else if (refreshRate != mRefreshRateInHighZone) {
@@ -2675,113 +2675,55 @@
private class DeviceConfigDisplaySettings implements DeviceConfig.OnPropertiesChangedListener {
public void startListening() {
- mDeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
+ mConfigParameterProvider.addOnPropertiesChangedListener(
BackgroundThread.getExecutor(), this);
}
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_DISPLAY_BRIGHTNESS_THRESHOLDS);
+ private int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmHdr(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmHdr(),
+ R.integer.config_defaultRefreshRateInHbmHdr,
+ displayDeviceConfig
+ );
}
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getLowAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_LOW_AMBIENT_BRIGHTNESS_THRESHOLDS);
+ private int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
+ return getRefreshRate(
+ () -> mConfigParameterProvider.getRefreshRateInHbmSunlight(),
+ () -> displayDeviceConfig.getDefaultRefreshRateInHbmSunlight(),
+ R.integer.config_defaultRefreshRateInHbmSunlight,
+ displayDeviceConfig
+ );
}
- public int getRefreshRateInLowZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_LOW_ZONE, -1);
-
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighDisplayBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_DISPLAY_BRIGHTNESS_THRESHOLDS);
- }
-
- /*
- * Return null if no such property or wrong format (not comma separated integers).
- */
- public int[] getHighAmbientBrightnessThresholds() {
- return getIntArrayProperty(
- DisplayManager.DeviceConfig
- .KEY_FIXED_REFRESH_RATE_HIGH_AMBIENT_BRIGHTNESS_THRESHOLDS);
- }
-
- public int getRefreshRateInHighZone() {
- return mDeviceConfig.getInt(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HIGH_ZONE,
- -1);
- }
-
- public int getRefreshRateInHbmHdr(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources().getInteger(
- R.integer.config_defaultRefreshRateInHbmHdr)
- : displayDeviceConfig.getDefaultRefreshRateInHbmHdr();
+ private int getRefreshRate(IntSupplier fromConfigPram, IntSupplier fromDisplayDeviceConfig,
+ @IntegerRes int configKey, DisplayDeviceConfig displayDeviceConfig) {
+ int refreshRate = -1;
try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_HDR,
- refreshRate);
- } catch (NullPointerException e) {
+ refreshRate = fromConfigPram.getAsInt();
+ } catch (NullPointerException npe) {
// Do Nothing
}
- return refreshRate;
- }
-
- public int getRefreshRateInHbmSunlight(DisplayDeviceConfig displayDeviceConfig) {
- int refreshRate =
- (displayDeviceConfig == null) ? mContext.getResources()
- .getInteger(R.integer.config_defaultRefreshRateInHbmSunlight)
- : displayDeviceConfig.getDefaultRefreshRateInHbmSunlight();
- try {
- refreshRate = mDeviceConfig.getInt(DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_REFRESH_RATE_IN_HBM_SUNLIGHT,
- refreshRate);
- } catch (NullPointerException e) {
- // Do Nothing
+ if (refreshRate == -1) {
+ refreshRate = (displayDeviceConfig == null)
+ ? mContext.getResources().getInteger(configKey)
+ : fromDisplayDeviceConfig.getAsInt();
}
return refreshRate;
}
- /*
- * Return null if no such property
- */
- public Float getDefaultPeakRefreshRate() {
- float defaultPeakRefreshRate = mDeviceConfig.getFloat(
- DeviceConfig.NAMESPACE_DISPLAY_MANAGER,
- DisplayManager.DeviceConfig.KEY_PEAK_REFRESH_RATE_DEFAULT, -1);
-
- if (defaultPeakRefreshRate == -1) {
- return null;
- }
- return defaultPeakRefreshRate;
- }
-
@Override
public void onPropertiesChanged(@NonNull DeviceConfig.Properties properties) {
- Float defaultPeakRefreshRate = getDefaultPeakRefreshRate();
+ float defaultPeakRefreshRate = mConfigParameterProvider.getPeakRefreshRateDefault();
mHandler.obtainMessage(MSG_DEFAULT_PEAK_REFRESH_RATE_CHANGED,
- defaultPeakRefreshRate).sendToTarget();
+ defaultPeakRefreshRate == -1 ? null : defaultPeakRefreshRate).sendToTarget();
- int[] lowDisplayBrightnessThresholds = getLowDisplayBrightnessThresholds();
- int[] lowAmbientBrightnessThresholds = getLowAmbientBrightnessThresholds();
- final int refreshRateInLowZone = getRefreshRateInLowZone();
+ int[] lowDisplayBrightnessThresholds =
+ mConfigParameterProvider.getLowDisplayBrightnessThresholds();
+ int[] lowAmbientBrightnessThresholds =
+ mConfigParameterProvider.getLowAmbientBrightnessThresholds();
+ final int refreshRateInLowZone = mConfigParameterProvider.getRefreshRateInLowZone();
mHandler.obtainMessage(MSG_LOW_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(lowDisplayBrightnessThresholds, lowAmbientBrightnessThresholds))
@@ -2790,9 +2732,11 @@
mHandler.obtainMessage(MSG_REFRESH_RATE_IN_LOW_ZONE_CHANGED, refreshRateInLowZone,
0).sendToTarget();
- int[] highDisplayBrightnessThresholds = getHighDisplayBrightnessThresholds();
- int[] highAmbientBrightnessThresholds = getHighAmbientBrightnessThresholds();
- final int refreshRateInHighZone = getRefreshRateInHighZone();
+ int[] highDisplayBrightnessThresholds =
+ mConfigParameterProvider.getHighDisplayBrightnessThresholds();
+ int[] highAmbientBrightnessThresholds =
+ mConfigParameterProvider.getHighAmbientBrightnessThresholds();
+ final int refreshRateInHighZone = mConfigParameterProvider.getRefreshRateInHighZone();
mHandler.obtainMessage(MSG_HIGH_BRIGHTNESS_THRESHOLDS_CHANGED,
new Pair<>(highDisplayBrightnessThresholds, highAmbientBrightnessThresholds))
@@ -2814,33 +2758,6 @@
.sendToTarget();
}
}
-
- private int[] getIntArrayProperty(String prop) {
- String strArray = mDeviceConfig.getString(DeviceConfig.NAMESPACE_DISPLAY_MANAGER, prop,
- null);
-
- if (strArray != null) {
- return parseIntArray(strArray);
- }
-
- return null;
- }
-
- private int[] parseIntArray(@NonNull String strArray) {
- String[] items = strArray.split(",");
- int[] array = new int[items.length];
-
- try {
- for (int i = 0; i < array.length; i++) {
- array[i] = Integer.parseInt(items[i]);
- }
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Incorrect format for array: '" + strArray + "'", e);
- array = null;
- }
-
- return array;
- }
}
interface Injector {
diff --git a/services/core/java/com/android/server/dreams/DreamController.java b/services/core/java/com/android/server/dreams/DreamController.java
index 6d70d21..633bf731 100644
--- a/services/core/java/com/android/server/dreams/DreamController.java
+++ b/services/core/java/com/android/server/dreams/DreamController.java
@@ -16,11 +16,11 @@
package com.android.server.dreams;
-import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
import static android.content.Intent.FLAG_RECEIVER_FOREGROUND;
import android.app.ActivityTaskManager;
import android.app.BroadcastOptions;
+import android.app.IAppTask;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -214,6 +214,27 @@
}
/**
+ * Provides an appTask for the dream with token {@code dreamToken}, so that the dream controller
+ * can stop the dream task when necessary.
+ */
+ void setDreamAppTask(Binder dreamToken, IAppTask appTask) {
+ if (mCurrentDream == null || mCurrentDream.mToken != dreamToken
+ || mCurrentDream.mAppTask != null) {
+ Slog.e(TAG, "Illegal dream activity start. mCurrentDream.mToken = "
+ + mCurrentDream.mToken + ", illegal dreamToken = " + dreamToken
+ + ". Ending this dream activity.");
+ try {
+ appTask.finishAndRemoveTask();
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Unable to stop illegal dream activity.");
+ }
+ return;
+ }
+
+ mCurrentDream.mAppTask = appTask;
+ }
+
+ /**
* Stops dreaming.
*
* The current dream, if any, and any unstopped previous dreams are stopped. The device stops
@@ -303,8 +324,14 @@
mSentStartBroadcast = false;
}
- mActivityTaskManager.removeRootTasksWithActivityTypes(
- new int[] {ACTIVITY_TYPE_DREAM});
+ if (mCurrentDream != null && mCurrentDream.mAppTask != null) {
+ // Finish the dream task in case it hasn't finished by itself already.
+ try {
+ mCurrentDream.mAppTask.finishAndRemoveTask();
+ } catch (RemoteException | RuntimeException e) {
+ Slog.e(TAG, "Unable to stop dream activity.");
+ }
+ }
mListener.onDreamStopped(dream.mToken);
}
@@ -364,6 +391,7 @@
public final boolean mIsPreviewMode;
public final boolean mCanDoze;
public final int mUserId;
+ public IAppTask mAppTask;
public PowerManager.WakeLock mWakeLock;
public boolean mBound;
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 68cf59f..d88fe8a 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -27,6 +27,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
+import android.app.IAppTask;
import android.app.TaskInfo;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -37,6 +38,7 @@
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
import android.content.pm.ServiceInfo;
import android.database.ContentObserver;
import android.hardware.display.AmbientDisplayConfiguration;
@@ -116,6 +118,7 @@
private final PowerManagerInternal mPowerManagerInternal;
private final PowerManager.WakeLock mDozeWakeLock;
private final ActivityTaskManagerInternal mAtmInternal;
+ private final PackageManagerInternal mPmInternal;
private final UserManager mUserManager;
private final UiEventLogger mUiEventLogger;
private final DreamUiEventLogger mDreamUiEventLogger;
@@ -216,6 +219,7 @@
mPowerManager = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mPowerManagerInternal = getLocalService(PowerManagerInternal.class);
mAtmInternal = getLocalService(ActivityTaskManagerInternal.class);
+ mPmInternal = getLocalService(PackageManagerInternal.class);
mUserManager = context.getSystemService(UserManager.class);
mDozeWakeLock = mPowerManager.newWakeLock(PowerManager.DOZE_WAKE_LOCK, DOZE_WAKE_LOCK_TAG);
mDozeConfig = new AmbientDisplayConfiguration(mContext);
@@ -1064,6 +1068,64 @@
Binder.restoreCallingIdentity(ident);
}
}
+
+ @Override // Binder call
+ public void startDreamActivity(@NonNull Intent intent) {
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ // We post here, because startDreamActivity and setDreamAppTask have to run
+ // synchronously and DreamController#setDreamAppTask has to run on mHandler.
+ mHandler.post(() -> {
+ final Binder dreamToken;
+ final String dreamPackageName;
+ synchronized (mLock) {
+ if (mCurrentDream == null) {
+ Slog.e(TAG, "Attempt to start DreamActivity, but the device is not "
+ + "dreaming. Aborting without starting the DreamActivity.");
+ return;
+ }
+ dreamToken = mCurrentDream.token;
+ dreamPackageName = mCurrentDream.name.getPackageName();
+ }
+
+ if (!canLaunchDreamActivity(dreamPackageName, intent.getPackage(),
+ callingUid)) {
+ Slog.e(TAG, "The dream activity can be started only when the device is dreaming"
+ + " and only by the active dream package.");
+ return;
+ }
+
+ final IAppTask appTask = mAtmInternal.startDreamActivity(intent, callingUid,
+ callingPid);
+ if (appTask == null) {
+ Slog.e(TAG, "Could not start dream activity.");
+ stopDreamInternal(true, "DreamActivity not started");
+ return;
+ }
+ mController.setDreamAppTask(dreamToken, appTask);
+ });
+ }
+
+ boolean canLaunchDreamActivity(String dreamPackageName, String packageName,
+ int callingUid) {
+ if (dreamPackageName == null || packageName == null) {
+ Slog.e(TAG, "Cannot launch dream activity due to invalid state. dream component= "
+ + dreamPackageName + ", packageName=" + packageName);
+ return false;
+ }
+ if (!mPmInternal.isSameApp(packageName, callingUid, UserHandle.getUserId(callingUid))) {
+ Slog.e(TAG, "Cannot launch dream activity because package="
+ + packageName + " does not match callingUid=" + callingUid);
+ return false;
+ }
+ if (packageName.equals(dreamPackageName)) {
+ return true;
+ }
+ Slog.e(TAG, "Dream packageName does not match active dream. Package " + packageName
+ + " does not match " + dreamPackageName);
+ return false;
+ }
+
}
private final class LocalService extends DreamManagerInternal {
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
index b0b1d67..ba9e280 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodBindingController.java
@@ -352,7 +352,6 @@
clearCurMethodAndSessions();
mService.clearInputShownLocked();
mService.unbindCurrentClientLocked(UnbindReason.DISCONNECT_IME);
- mService.resetSystemUiLocked();
}
}
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index 4dbd820..1ab83f7 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -48,6 +48,7 @@
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeTargetWindowState;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.ImeVisibilityResult;
+import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.InputMethodBindingController.TIME_TO_RECONNECT;
import static com.android.server.inputmethod.InputMethodUtils.isSoftInputModeStateVisibleAllowed;
@@ -633,9 +634,8 @@
private InputMethodSubtype mCurrentSubtype;
/**
- * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}.
+ * {@code true} if the IME has not been mostly hidden via {@link android.view.InsetsController}
*/
- @GuardedBy("ImfLock.class")
private boolean mCurPerceptible;
/**
@@ -749,26 +749,33 @@
SparseArray<AccessibilitySessionState> mEnabledAccessibilitySessions = new SparseArray<>();
/**
- * {@code true} if the device is currently interactive with the user, initially true.
- *
- * @see #handleSetInteractive
+ * True if the device is currently interactive with user. The value is true initially.
*/
- @GuardedBy("ImfLock.class")
boolean mIsInteractive = true;
- @GuardedBy("ImfLock.class")
- @InputMethodService.BackDispositionMode
int mBackDisposition = InputMethodService.BACK_DISPOSITION_DEFAULT;
/**
- * The {@link InputMethodService.ImeWindowVisibility} of the currently bound IME,
- * or {@code 0} if no IME is bound.
+ * A set of status bits regarding the active IME.
*
- * <p><em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
+ * <p>This value is a combination of following two bits:</p>
+ * <dl>
+ * <dt>{@link InputMethodService#IME_ACTIVE}</dt>
+ * <dd>
+ * If this bit is ON, connected IME is ready to accept touch/key events.
+ * </dd>
+ * <dt>{@link InputMethodService#IME_VISIBLE}</dt>
+ * <dd>
+ * If this bit is ON, some of IME view, e.g. software input, candidate view, is visible.
+ * </dd>
+ * <dt>{@link InputMethodService#IME_INVISIBLE}</dt>
+ * <dd> If this bit is ON, IME is ready with views from last EditorInfo but is
+ * currently invisible.
+ * </dd>
+ * </dl>
+ * <em>Do not update this value outside of {@link #setImeWindowStatus(IBinder, int, int)} and
* {@link InputMethodBindingController#unbindCurrentMethod()}.</em>
*/
- @GuardedBy("ImfLock.class")
- @InputMethodService.ImeWindowVisibility
int mImeWindowVis;
private LocaleList mLastSystemLocales;
@@ -1529,6 +1536,7 @@
// Uh oh, current input method is no longer around!
// Pick another one...
Slog.i(TAG, "Current input method removed: " + curInputMethodId);
+ updateSystemUiLocked(0 /* vis */, mBackDisposition);
if (!chooseNewDefaultIMELocked()) {
changed = true;
curIm = null;
@@ -2360,6 +2368,28 @@
}
}
+ /**
+ * Called when {@link #resetCurrentMethodAndClientLocked(int)} invoked for clean-up states
+ * before unbinding the current method.
+ */
+ @GuardedBy("ImfLock.class")
+ void onUnbindCurrentMethodByReset() {
+ final ImeTargetWindowState winState = mVisibilityStateComputer.getWindowStateOrNull(
+ mCurFocusedWindow);
+ if (winState != null && !winState.isRequestedImeVisible()
+ && !mVisibilityStateComputer.isInputShown()) {
+ // Normally, the focus window will apply the IME visibility state to
+ // WindowManager when the IME has applied it. But it would be too late when
+ // switching IMEs in between different users. (Since the focused IME will
+ // first unbind the service to switch to bind the next user of the IME
+ // service, that wouldn't make the attached IME token validity check in time)
+ // As a result, we have to notify WM to apply IME visibility before clearing the
+ // binding states in the first place.
+ mVisibilityApplier.applyImeVisibility(mCurFocusedWindow, mCurStatsToken,
+ STATE_HIDE_IME);
+ }
+ }
+
/** {@code true} when a {@link ClientState} has attached from starting the input connection. */
@GuardedBy("ImfLock.class")
boolean hasAttachedClient() {
@@ -2831,6 +2861,8 @@
@GuardedBy("ImfLock.class")
void resetCurrentMethodAndClientLocked(@UnbindReason int unbindClientReason) {
setSelectedMethodIdLocked(null);
+ // Callback before clean-up binding states.
+ onUnbindCurrentMethodByReset();
mBindingController.unbindCurrentMethod();
unbindCurrentClientLocked(unbindClientReason);
}
@@ -2931,6 +2963,7 @@
sessionState.mSession.finishSession();
} catch (RemoteException e) {
Slog.w(TAG, "Session failed to close due to remote exception", e);
+ updateSystemUiLocked(0 /* vis */, mBackDisposition);
}
sessionState.mSession = null;
}
@@ -3040,8 +3073,7 @@
}
@GuardedBy("ImfLock.class")
- private boolean shouldShowImeSwitcherLocked(
- @InputMethodService.ImeWindowVisibility int visibility) {
+ private boolean shouldShowImeSwitcherLocked(int visibility) {
if (!mShowOngoingImeSwitcherForPhones) return false;
// When the IME switcher dialog is shown, the IME switcher button should be hidden.
if (mMenuController.getSwitchingDialogLocked() != null) return false;
@@ -3053,7 +3085,8 @@
&& mWindowManagerInternal.isKeyguardSecure(mSettings.getCurrentUserId())) {
return false;
}
- if ((visibility & InputMethodService.IME_ACTIVE) == 0) {
+ if ((visibility & InputMethodService.IME_ACTIVE) == 0
+ || (visibility & InputMethodService.IME_INVISIBLE) != 0) {
return false;
}
if (mWindowManagerInternal.isHardKeyboardAvailable()) {
@@ -3112,9 +3145,7 @@
@BinderThread
@SuppressWarnings("deprecation")
- private void setImeWindowStatus(@NonNull IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ private void setImeWindowStatus(@NonNull IBinder token, int vis, int backDisposition) {
final int topFocusedDisplayId = mWindowManagerInternal.getTopFocusedDisplayId();
synchronized (ImfLock.class) {
@@ -3131,7 +3162,7 @@
}
mImeWindowVis = vis;
mBackDisposition = backDisposition;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked(vis, backDisposition);
}
final boolean dismissImeOnBackKeyPressed;
@@ -3166,46 +3197,37 @@
private void updateImeWindowStatus(boolean disableImeIcon) {
synchronized (ImfLock.class) {
- // TODO(b/285109020): disableImeIcon should be stored in a property like
- // mIsSwitcherIconDisabled, but it is currently not reliably cleared.
- updateSystemUiLocked(disableImeIcon ? 0 : mImeWindowVis, mBackDisposition);
+ if (disableImeIcon) {
+ updateSystemUiLocked(0, mBackDisposition);
+ } else {
+ updateSystemUiLocked();
+ }
}
}
@GuardedBy("ImfLock.class")
void updateSystemUiLocked() {
- // This is only used by InputMethodMenuController to trigger the IME switcher icon
- // visibility, by having {@code shouldShowImeSwitcherLocked} called, which depends on the
- // visibility of the IME switcher dialog.
updateSystemUiLocked(mImeWindowVis, mBackDisposition);
}
// Caution! This method is called in this class. Handle multi-user carefully
@GuardedBy("ImfLock.class")
- private void updateSystemUiLocked(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ private void updateSystemUiLocked(int vis, int backDisposition) {
if (getCurTokenLocked() == null) {
return;
}
if (DEBUG) {
Slog.d(TAG, "IME window vis: " + vis
- + " active: " + ((vis & InputMethodService.IME_ACTIVE) != 0)
- + " visible: " + ((vis & InputMethodService.IME_VISIBLE) != 0)
- + " backDisposition: " + backDisposition
- + " isInteractive: " + mIsInteractive
- + " curPerceptible: " + mCurPerceptible
+ + " active: " + (vis & InputMethodService.IME_ACTIVE)
+ + " inv: " + (vis & InputMethodService.IME_INVISIBLE)
+ " displayId: " + mCurTokenDisplayId);
}
// TODO: Move this clearing calling identity block to setImeWindowStatus after making sure
- // all updateSystemUi happens on system privilege.
+ // all updateSystemUi happens on system privilege.
final long ident = Binder.clearCallingIdentity();
try {
- if (!mIsInteractive) {
- // When we are not interactive,
- // the visibility should be 0 (no IME icons should be shown).
- vis = 0;
- } else if (!mCurPerceptible) {
+ if (!mCurPerceptible) {
if ((vis & InputMethodService.IME_VISIBLE) != 0) {
vis &= ~InputMethodService.IME_VISIBLE;
vis |= InputMethodService.IME_VISIBLE_IMPERCEPTIBLE;
@@ -3540,7 +3562,7 @@
return;
}
mCurPerceptible = perceptible;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked();
}
});
}
@@ -5102,11 +5124,8 @@
private void handleSetInteractive(final boolean interactive) {
synchronized (ImfLock.class) {
- if (mIsInteractive == interactive) {
- return;
- }
mIsInteractive = interactive;
- updateSystemUiLocked(mImeWindowVis, mBackDisposition);
+ updateSystemUiLocked(interactive ? mImeWindowVis : 0, mBackDisposition);
// Inform the current client of the change in active status
if (mCurClient == null || mCurClient.mClient == null) {
@@ -6741,8 +6760,7 @@
@BinderThread
@Override
- public void setImeWindowStatusAsync(@InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition) {
+ public void setImeWindowStatusAsync(int vis, int backDisposition) {
mImms.setImeWindowStatus(mToken, vis, backDisposition);
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 65e34e6..398e470 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -147,7 +147,7 @@
mInjector = injector;
mClock = injector.createClock();
mDeathEaters = new ArrayMap<IBinder, IBinder.DeathRecipient>();
- mCallbackDelegate = new CallbackDelegate();
+ mCallbackDelegate = new CallbackDelegate(injector.createCallbackLooper());
mAppOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class);
mPackageManager = mContext.getPackageManager();
@@ -182,6 +182,11 @@
Clock createClock() {
return SystemClock::uptimeMillis;
}
+
+ /** Creates the {@link Looper} to be used when notifying callbacks. */
+ Looper createCallbackLooper() {
+ return Looper.getMainLooper();
+ }
}
@Override
@@ -268,7 +273,8 @@
dispatchStop(projection);
}
- private void addCallback(final IMediaProjectionWatcherCallback callback) {
+ @VisibleForTesting
+ void addCallback(final IMediaProjectionWatcherCallback callback) {
IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
@@ -315,6 +321,12 @@
mCallbackDelegate.dispatchStop(projection);
}
+ private void dispatchSessionSet(
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallbackDelegate.dispatchSession(projectionInfo, session);
+ }
+
/**
* Returns {@code true} when updating the current mirroring session on WM succeeded, and
* {@code false} otherwise.
@@ -335,6 +347,7 @@
if (mProjectionGrant != null) {
// Cache the session details.
mProjectionGrant.mSession = incomingSession;
+ dispatchSessionSet(mProjectionGrant.getProjectionInfo(), incomingSession);
}
return true;
}
@@ -1155,8 +1168,8 @@
private Handler mHandler;
private final Object mLock = new Object();
- public CallbackDelegate() {
- mHandler = new Handler(Looper.getMainLooper(), null, true /*async*/);
+ CallbackDelegate(Looper callbackLooper) {
+ mHandler = new Handler(callbackLooper, null, true /*async*/);
mClientCallbacks = new ArrayMap<IBinder, IMediaProjectionCallback>();
mWatcherCallbacks = new ArrayMap<IBinder, IMediaProjectionWatcherCallback>();
}
@@ -1219,6 +1232,16 @@
}
}
+ public void dispatchSession(
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ synchronized (mLock) {
+ for (IMediaProjectionWatcherCallback callback : mWatcherCallbacks.values()) {
+ mHandler.post(new WatcherSessionCallback(callback, projectionInfo, session));
+ }
+ }
+ }
+
public void dispatchResize(MediaProjection projection, int width, int height) {
if (projection == null) {
Slog.e(TAG,
@@ -1335,6 +1358,29 @@
}
}
+ private static final class WatcherSessionCallback implements Runnable {
+ private final IMediaProjectionWatcherCallback mCallback;
+ private final MediaProjectionInfo mProjectionInfo;
+ private final ContentRecordingSession mSession;
+
+ WatcherSessionCallback(
+ @NonNull IMediaProjectionWatcherCallback callback,
+ @NonNull MediaProjectionInfo projectionInfo,
+ @Nullable ContentRecordingSession session) {
+ mCallback = callback;
+ mProjectionInfo = projectionInfo;
+ mSession = session;
+ }
+
+ @Override
+ public void run() {
+ try {
+ mCallback.onRecordingSessionSet(mProjectionInfo, mSession);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "Failed to notify content recording session changed", e);
+ }
+ }
+ }
private static String typeToString(int type) {
switch (type) {
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5b32273..68f49fd 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -5595,6 +5595,11 @@
boolean granted, boolean userSet) {
Objects.requireNonNull(listener);
checkNotificationListenerAccess();
+ if (granted && listener.flattenToString().length()
+ > NotificationManager.MAX_SERVICE_COMPONENT_NAME_LENGTH) {
+ throw new IllegalArgumentException(
+ "Component name too long: " + listener.flattenToString());
+ }
if (!userSet && isNotificationListenerAccessUserSet(listener)) {
// Don't override user's choice
return;
diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java
index 5f52c16..c1d5af5 100644
--- a/services/core/java/com/android/server/pm/DeletePackageHelper.java
+++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java
@@ -610,7 +610,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashScreenTheme*/,
- 0 /*firstInstallTime*/);
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
}
mPm.mSettings.writeKernelMappingLPr(ps);
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index db47306..2fc22bf 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -30,6 +30,7 @@
import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.content.pm.PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
import static android.os.Process.INVALID_UID;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
import static android.os.storage.StorageManager.FLAG_STORAGE_CE;
@@ -5239,6 +5240,20 @@
}
@Override
+ @PackageManager.UserMinAspectRatio
+ public int getUserMinAspectRatio(@NonNull String packageName, int userId) {
+ final Computer snapshot = snapshotComputer();
+ final int callingUid = Binder.getCallingUid();
+ snapshot.enforceCrossUserPermission(
+ callingUid, userId, false /* requireFullPermission */,
+ false /* checkShell */, "getUserMinAspectRatio");
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ return packageState == null ? USER_MIN_ASPECT_RATIO_UNSET
+ : packageState.getUserStateOrDefault(userId).getMinAspectRatio();
+ }
+
+ @Override
public Bundle getSuspendedPackageAppExtras(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
final Computer snapshot = snapshot();
@@ -6201,6 +6216,32 @@
return true;
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.INSTALL_PACKAGES)
+ @Override
+ public void setUserMinAspectRatio(@NonNull String packageName, int userId,
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ setUserMinAspectRatio_enforcePermission();
+ final int callingUid = Binder.getCallingUid();
+ final Computer snapshot = snapshotComputer();
+ snapshot.enforceCrossUserPermission(callingUid, userId,
+ false /* requireFullPermission */, false /* checkShell */,
+ "setUserMinAspectRatio");
+ enforceOwnerRights(snapshot, packageName, callingUid);
+
+ final PackageStateInternal packageState = snapshot
+ .getPackageStateForInstalledAndFiltered(packageName, callingUid, userId);
+ if (packageState == null) {
+ return;
+ }
+
+ if (packageState.getUserStateOrDefault(userId).getMinAspectRatio() == aspectRatio) {
+ return;
+ }
+
+ commitPackageStateMutation(null, packageName, state ->
+ state.userState(userId).setMinAspectRatio(aspectRatio));
+ }
+
@Override
@SuppressWarnings("GuardedBy")
public void setRuntimePermissionsVersion(int version, @UserIdInt int userId) {
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index 7fda092..3e9ccac 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -337,7 +337,7 @@
final long previousFirstInstallTime =
replacedPkgSetting.getUserStateOrDefault(userId).getFirstInstallTimeMillis();
if (previousFirstInstallTime != 0) {
- modifyUserState(userId).setFirstInstallTime(previousFirstInstallTime);
+ modifyUserState(userId).setFirstInstallTimeMillis(previousFirstInstallTime);
}
}
onChanged();
@@ -352,10 +352,10 @@
if (userId == UserHandle.USER_ALL) {
int userStateCount = mUserStates.size();
for (int i = 0; i < userStateCount; i++) {
- mUserStates.valueAt(i).setFirstInstallTime(firstInstallTime);
+ mUserStates.valueAt(i).setFirstInstallTimeMillis(firstInstallTime);
}
} else {
- modifyUserState(userId).setFirstInstallTime(firstInstallTime);
+ modifyUserState(userId).setFirstInstallTimeMillis(firstInstallTime);
}
onChanged();
return this;
@@ -875,7 +875,7 @@
ArraySet<String> enabledComponents, ArraySet<String> disabledComponents,
int installReason, int uninstallReason,
String harmfulAppWarning, String splashScreenTheme,
- long firstInstallTime) {
+ long firstInstallTime, int aspectRatio) {
modifyUserState(userId)
.setSuspendParams(suspendParams)
.setCeDataInode(ceDataInode)
@@ -894,7 +894,8 @@
.setVirtualPreload(virtualPreload)
.setHarmfulAppWarning(harmfulAppWarning)
.setSplashScreenTheme(splashScreenTheme)
- .setFirstInstallTime(firstInstallTime);
+ .setFirstInstallTimeMillis(firstInstallTime)
+ .setMinAspectRatio(aspectRatio);
onChanged();
}
@@ -912,7 +913,7 @@
? null : otherState.getDisabledComponentsNoCopy().untrackedStorage(),
otherState.getInstallReason(), otherState.getUninstallReason(),
otherState.getHarmfulAppWarning(), otherState.getSplashScreenTheme(),
- otherState.getFirstInstallTimeMillis());
+ otherState.getFirstInstallTimeMillis(), otherState.getMinAspectRatio());
}
WatchedArraySet<String> getEnabledComponents(int userId) {
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index aaf13eb..532ae71 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -351,6 +351,7 @@
private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload";
private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning";
private static final String ATTR_SPLASH_SCREEN_THEME = "splash-screen-theme";
+ private static final String ATTR_MIN_ASPECT_RATIO = "min-aspect-ratio";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_BUILD_FINGERPRINT = "buildFingerprint";
@@ -1122,7 +1123,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /*splashscreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
}
@@ -1776,7 +1778,8 @@
PackageManager.UNINSTALL_REASON_UNKNOWN,
null /*harmfulAppWarning*/,
null /* splashScreenTheme*/,
- 0 /*firstInstallTime*/
+ 0 /*firstInstallTime*/,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET
);
}
return;
@@ -1871,6 +1874,9 @@
ATTR_SPLASH_SCREEN_THEME);
final long firstInstallTime = parser.getAttributeLongHex(null,
ATTR_FIRST_INSTALL_TIME, 0);
+ final int minAspectRatio = parser.getAttributeInt(null,
+ ATTR_MIN_ASPECT_RATIO,
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
ArraySet<String> enabledComponents = null;
ArraySet<String> disabledComponents = null;
@@ -1947,7 +1953,8 @@
enabledCaller, enabledComponents, disabledComponents, installReason,
uninstallReason, harmfulAppWarning, splashScreenTheme,
firstInstallTime != 0 ? firstInstallTime :
- origFirstInstallTimes.getOrDefault(name, 0L));
+ origFirstInstallTimes.getOrDefault(name, 0L),
+ minAspectRatio);
mDomainVerificationManager.setLegacyUserState(name, userId, verifState);
} else if (tagName.equals("preferred-activities")) {
@@ -2242,6 +2249,11 @@
serializer.attribute(null, ATTR_SPLASH_SCREEN_THEME,
ustate.getSplashScreenTheme());
}
+ if (ustate.getMinAspectRatio()
+ != PackageManager.USER_MIN_ASPECT_RATIO_UNSET) {
+ serializer.attributeInt(null, ATTR_MIN_ASPECT_RATIO,
+ ustate.getMinAspectRatio());
+ }
if (ustate.isSuspended()) {
for (int i = 0; i < ustate.getSuspendParams().size(); i++) {
final String suspendingPackage = ustate.getSuspendParams().keyAt(i);
diff --git a/services/core/java/com/android/server/pm/ShortcutService.java b/services/core/java/com/android/server/pm/ShortcutService.java
index 5b3514c..710e0b7 100644
--- a/services/core/java/com/android/server/pm/ShortcutService.java
+++ b/services/core/java/com/android/server/pm/ShortcutService.java
@@ -15,6 +15,7 @@
*/
package com.android.server.pm;
+import static android.app.ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_DENIED;
import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import android.Manifest.permission;
@@ -24,6 +25,7 @@
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
+import android.app.ActivityOptions;
import android.app.AppGlobals;
import android.app.IUidObserver;
import android.app.IUriGrantsManager;
@@ -4407,8 +4409,11 @@
return;
}
try {
+ ActivityOptions options = ActivityOptions.makeBasic()
+ .setPendingIntentBackgroundActivityStartMode(
+ MODE_BACKGROUND_ACTIVITY_START_DENIED);
intentSender.sendIntent(mContext, /* code= */ 0, extras,
- /* onFinished=*/ null, /* handler= */ null);
+ /* onFinished=*/ null, /* handler= */ null, null, options.toBundle());
} catch (SendIntentException e) {
Slog.w(TAG, "sendIntent failed().", e);
}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 7e88e13..f8bd328 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -1037,7 +1037,7 @@
final UserData userData = mUsers.valueAt(i);
final int userId = userData.info.id;
if (userId != currentUser && userData.info.isFull() && !userData.info.partial
- && !mRemovingUserIds.get(userId)) {
+ && userData.info.isEnabled() && !mRemovingUserIds.get(userId)) {
final long userEnteredTime = userData.mLastEnteredForegroundTimeMillis;
if (userEnteredTime > latestEnteredTime) {
latestEnteredTime = userEnteredTime;
@@ -2927,14 +2927,14 @@
Preconditions.checkState(mCachedEffectiveUserRestrictions.getRestrictions(userId)
!= newBaseRestrictions);
- if (mBaseUserRestrictions.updateRestrictions(userId, newBaseRestrictions)) {
+ if (mBaseUserRestrictions.updateRestrictions(userId, new Bundle(newBaseRestrictions))) {
scheduleWriteUser(userId);
}
}
final Bundle effective = computeEffectiveUserRestrictionsLR(userId);
- mCachedEffectiveUserRestrictions.updateRestrictions(userId, effective);
+ mCachedEffectiveUserRestrictions.updateRestrictions(userId, new Bundle(effective));
// Apply the new restrictions.
if (DBG) {
@@ -5589,8 +5589,14 @@
}
}
- @GuardedBy("mUsersLock")
@VisibleForTesting
+ void addRemovingUserId(@UserIdInt int userId) {
+ synchronized (mUsersLock) {
+ addRemovingUserIdLocked(userId);
+ }
+ }
+
+ @GuardedBy("mUsersLock")
void addRemovingUserIdLocked(@UserIdInt int userId) {
// We remember deleted user IDs to prevent them from being
// reused during the current boot; they can still be reused
diff --git a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
index 91a25db3..3d056e8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java
@@ -377,6 +377,8 @@
private final int mUninstallReason;
@Nullable
private final String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private final int mMinAspectRatio;
private final long mFirstInstallTimeMillis;
private UserStateImpl(@NonNull PackageUserState userState) {
@@ -392,6 +394,7 @@
mSharedLibraryOverlayPaths = userState.getSharedLibraryOverlayPaths();
mUninstallReason = userState.getUninstallReason();
mSplashScreenTheme = userState.getSplashScreenTheme();
+ mMinAspectRatio = userState.getMinAspectRatio();
setBoolean(Booleans.HIDDEN, userState.isHidden());
setBoolean(Booleans.INSTALLED, userState.isInstalled());
setBoolean(Booleans.INSTANT_APP, userState.isInstantApp());
@@ -543,6 +546,11 @@
}
@DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
+ @DataClass.Generated.Member
public long getFirstInstallTimeMillis() {
return mFirstInstallTimeMillis;
}
@@ -554,10 +562,10 @@
}
@DataClass.Generated(
- time = 1671671043891L,
+ time = 1687938966108L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageStateImpl.java",
- inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
+ inputSignatures = "private int mBooleans\nprivate final long mCeDataInode\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mDisabledComponents\nprivate final @android.content.pm.PackageManager.DistractionRestriction int mDistractionFlags\nprivate final @android.annotation.NonNull android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate final int mEnabledState\nprivate final @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate final @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate final @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate final @android.annotation.NonNull android.content.pm.overlay.OverlayPaths mOverlayPaths\nprivate final @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate final @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate final @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate final @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate final long mFirstInstallTimeMillis\npublic static com.android.server.pm.pkg.PackageUserState copy(com.android.server.pm.pkg.PackageUserState)\nprivate void setBoolean(int,boolean)\nprivate boolean getBoolean(int)\npublic @java.lang.Override boolean isHidden()\npublic @java.lang.Override boolean isInstalled()\npublic @java.lang.Override boolean isInstantApp()\npublic @java.lang.Override boolean isNotLaunched()\npublic @java.lang.Override boolean isStopped()\npublic @java.lang.Override boolean isSuspended()\npublic @java.lang.Override boolean isVirtualPreload()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\nclass UserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserState]\nprivate static final int HIDDEN\nprivate static final int INSTALLED\nprivate static final int INSTANT_APP\nprivate static final int NOT_LAUNCHED\nprivate static final int STOPPED\nprivate static final int SUSPENDED\nprivate static final int VIRTUAL_PRELOAD\nclass Booleans extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genConstructor=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserState.java b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
index 2048d65..f75d214 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserState.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserState.java
@@ -217,4 +217,12 @@
*/
@Nullable
String getSplashScreenTheme();
+
+ /**
+ * @return the min aspect ratio setting of the package which by default is unset
+ * unless it has been set by the user
+ * @hide
+ */
+ @PackageManager.UserMinAspectRatio
+ int getMinAspectRatio();
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
index 73fb672..1fb12a8 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateDefault.java
@@ -136,6 +136,11 @@
}
@Override
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+ }
+
+ @Override
public long getFirstInstallTimeMillis() {
return 0;
}
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index ed4aab9..d911ac1 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -16,6 +16,7 @@
package com.android.server.pm.pkg;
+import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.ComponentName;
@@ -61,6 +62,7 @@
private int mDistractionFlags;
private boolean mInstantApp;
private boolean mVirtualPreload;
+ @PackageManager.EnabledState
private int mEnabledState = PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
@PackageManager.InstallReason
private int mInstallReason = PackageManager.INSTALL_REASON_UNKNOWN;
@@ -81,6 +83,9 @@
@Nullable
private String mSplashScreenTheme;
+ @PackageManager.UserMinAspectRatio
+ private int mMinAspectRatio = PackageManager.USER_MIN_ASPECT_RATIO_UNSET;
+
/**
* Suspending package to suspend params
*/
@@ -90,7 +95,7 @@
@Nullable
private WatchedArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
- private long mFirstInstallTime;
+ private @CurrentTimeMillisLong long mFirstInstallTimeMillis;
// TODO(b/239050028): Remove, enforce notifying parent through PMS commit method
@Nullable
@@ -144,10 +149,11 @@
mHarmfulAppWarning = other.mHarmfulAppWarning;
mLastDisableAppCaller = other.mLastDisableAppCaller;
mSplashScreenTheme = other.mSplashScreenTheme;
+ mMinAspectRatio = other.mMinAspectRatio;
mSuspendParams = other.mSuspendParams == null ? null : other.mSuspendParams.snapshot();
mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null
? null : other.mComponentLabelIconOverrideMap.snapshot();
- mFirstInstallTime = other.mFirstInstallTime;
+ mFirstInstallTimeMillis = other.mFirstInstallTimeMillis;
mSnapshot = new SnapshotCache.Sealed<>();
}
@@ -506,6 +512,19 @@
}
/**
+ * Sets user min aspect ratio override value
+ * @see PackageManager.UserMinAspectRatio
+ */
+ public @NonNull PackageUserStateImpl setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int value) {
+ mMinAspectRatio = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.UserMinAspectRatio.class, null, mMinAspectRatio);
+ onChanged();
+ return this;
+ }
+
+ /**
* Suspending package to suspend params
*/
public @NonNull PackageUserStateImpl setSuspendParams(
@@ -538,8 +557,8 @@
return this;
}
- public @NonNull PackageUserStateImpl setFirstInstallTime(long value) {
- mFirstInstallTime = value;
+ public @NonNull PackageUserStateImpl setFirstInstallTimeMillis(long value) {
+ mFirstInstallTimeMillis = value;
onChanged();
return this;
}
@@ -643,7 +662,7 @@
}
@DataClass.Generated.Member
- public int getEnabledState() {
+ public @PackageManager.EnabledState int getEnabledState() {
return mEnabledState;
}
@@ -677,6 +696,11 @@
return mSplashScreenTheme;
}
+ @DataClass.Generated.Member
+ public @PackageManager.UserMinAspectRatio int getMinAspectRatio() {
+ return mMinAspectRatio;
+ }
+
/**
* Suspending package to suspend params
*/
@@ -691,8 +715,8 @@
}
@DataClass.Generated.Member
- public long getFirstInstallTimeMillis() {
- return mFirstInstallTime;
+ public @CurrentTimeMillisLong long getFirstInstallTimeMillis() {
+ return mFirstInstallTimeMillis;
}
@DataClass.Generated.Member
@@ -764,9 +788,10 @@
&& Objects.equals(mOverlayPaths, that.mOverlayPaths)
&& Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
&& Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
+ && mMinAspectRatio == that.mMinAspectRatio
&& Objects.equals(mSuspendParams, that.mSuspendParams)
&& Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap)
- && mFirstInstallTime == that.mFirstInstallTime
+ && mFirstInstallTimeMillis == that.mFirstInstallTimeMillis
&& watchableEquals(that.mWatchable)
&& snapshotEquals(that.mSnapshot);
}
@@ -796,19 +821,20 @@
_hash = 31 * _hash + Objects.hashCode(mOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme);
+ _hash = 31 * _hash + mMinAspectRatio;
_hash = 31 * _hash + Objects.hashCode(mSuspendParams);
_hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
- _hash = 31 * _hash + Long.hashCode(mFirstInstallTime);
+ _hash = 31 * _hash + Long.hashCode(mFirstInstallTimeMillis);
_hash = 31 * _hash + watchableHashCode();
_hash = 31 * _hash + snapshotHashCode();
return _hash;
}
@DataClass.Generated(
- time = 1668033772891L,
+ time = 1687938397579L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mDisabledComponentsWatched\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArraySet<java.lang.String> mEnabledComponentsWatched\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate @android.content.pm.PackageManager.EnabledState int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.content.pm.PackageManager.UserMinAspectRatio int mMinAspectRatio\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable com.android.server.utils.WatchedArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate @android.annotation.CurrentTimeMillisLong long mFirstInstallTimeMillis\nprivate @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nfinal @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl> makeCache()\nprivate void onChanged()\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageUserStateImpl snapshot()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.utils.WatchedArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponents()\npublic @android.annotation.NonNull @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponents()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(com.android.server.utils.WatchedArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setMinAspectRatio(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTimeMillis(long)\npublic @android.annotation.NonNull @java.lang.Override java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getSharedLibraryOverlayPaths()\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setWatchable(com.android.server.utils.Watchable)\nprivate boolean watchableEquals(com.android.server.utils.Watchable)\nprivate int watchableHashCode()\nprivate boolean snapshotEquals(com.android.server.utils.SnapshotCache<com.android.server.pm.pkg.PackageUserStateImpl>)\nprivate int snapshotHashCode()\nclass PackageUserStateImpl extends com.android.server.utils.WatchableImpl implements [com.android.server.pm.pkg.PackageUserStateInternal, com.android.server.utils.Snappable]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
index 8125b0f..8430cf7 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageStateMutator.java
@@ -439,6 +439,16 @@
}
return null;
}
+
+ @NonNull
+ @Override
+ public PackageUserStateWrite setMinAspectRatio(
+ @PackageManager.UserMinAspectRatio int aspectRatio) {
+ if (mUserState != null) {
+ mUserState.setMinAspectRatio(aspectRatio);
+ }
+ return this;
+ }
}
}
}
diff --git a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
index 11d6d97..0c6c672 100644
--- a/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
+++ b/services/core/java/com/android/server/pm/pkg/mutate/PackageUserStateWrite.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.content.pm.overlay.OverlayPaths;
+import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.SuspendParams;
public interface PackageUserStateWrite {
@@ -68,4 +69,8 @@
@NonNull
PackageUserStateWrite setComponentLabelIcon(@NonNull ComponentName componentName,
@Nullable String nonLocalizedLabel, @Nullable Integer icon);
+
+ /** @see PackageUserStateImpl#setMinAspectRatio(int) */
+ @NonNull
+ PackageUserStateWrite setMinAspectRatio(@PackageManager.UserMinAspectRatio int aspectRatio);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index a792b9c..9402fca 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -708,8 +708,11 @@
finishKeyguardDrawn();
break;
case MSG_WINDOW_MANAGER_DRAWN_COMPLETE:
- if (DEBUG_WAKEUP) Slog.w(TAG, "Setting mWindowManagerDrawComplete");
- finishWindowsDrawn(msg.arg1);
+ final int displayId = msg.arg1;
+ if (DEBUG_WAKEUP) Slog.w(TAG, "All windows drawn on display " + displayId);
+ Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ finishWindowsDrawn(displayId);
break;
case MSG_HIDE_BOOT_MESSAGE:
handleHideBootMessage();
@@ -3618,19 +3621,17 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
- if (mKeyguardDelegate != null && waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
+ if (mKeyguardDelegate != null) {
mPendingKeyguardOccluded = occluded;
mKeyguardOccludedChanged = true;
- } else {
- setKeyguardOccludedLw(occluded);
}
}
@Override
public int applyKeyguardOcclusionChange() {
if (DEBUG_KEYGUARD) Slog.d(TAG, "transition/occluded commit occluded="
- + mPendingKeyguardOccluded);
+ + mPendingKeyguardOccluded + " changed=" + mKeyguardOccludedChanged);
// TODO(b/276433230): Explicitly save before/after for occlude state in each
// Transition so we don't need to update SysUI every time.
@@ -5046,15 +5047,10 @@
// ... eventually calls finishWindowsDrawn which will finalize our screen turn on
// as well as enabling the orientation change logic/sensor.
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for every display");
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- INVALID_DISPLAY, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, INVALID_DISPLAY /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, INVALID_DISPLAY, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, INVALID_DISPLAY);
}
// Called on the DisplayManager's DisplayPowerController thread.
@@ -5134,15 +5130,10 @@
mScreenOnListeners.put(displayId, screenOnListener);
Trace.asyncTraceBegin(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- mWindowManagerInternal.waitForAllWindowsDrawn(() -> {
- if (DEBUG_WAKEUP) Slog.i(TAG, "All windows ready for display: " + displayId);
- mHandler.sendMessage(mHandler.obtainMessage(MSG_WINDOW_MANAGER_DRAWN_COMPLETE,
- displayId, 0));
-
- Trace.asyncTraceEnd(Trace.TRACE_TAG_WINDOW_MANAGER,
- TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, /* cookie= */ 0);
- }, WAITING_FOR_DRAWN_TIMEOUT, displayId);
+ TRACE_WAIT_FOR_ALL_WINDOWS_DRAWN_METHOD, displayId /* cookie */);
+ mWindowManagerInternal.waitForAllWindowsDrawn(mHandler.obtainMessage(
+ MSG_WINDOW_MANAGER_DRAWN_COMPLETE, displayId, 0),
+ WAITING_FOR_DRAWN_TIMEOUT, displayId);
}
}
diff --git a/services/core/java/com/android/server/policy/WindowManagerPolicy.java b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
index 887f946..03a7bd3 100644
--- a/services/core/java/com/android/server/policy/WindowManagerPolicy.java
+++ b/services/core/java/com/android/server/policy/WindowManagerPolicy.java
@@ -169,7 +169,7 @@
*
* @param occluded Whether Keyguard is currently occluded or not.
*/
- void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition);
+ void onKeyguardOccludedChangedLw(boolean occluded);
/**
* Commit any queued changes to keyguard occlude status that had been deferred during the
diff --git a/services/core/java/com/android/server/power/TEST_MAPPING b/services/core/java/com/android/server/power/TEST_MAPPING
index cf1bfc3..fbfe291 100644
--- a/services/core/java/com/android/server/power/TEST_MAPPING
+++ b/services/core/java/com/android/server/power/TEST_MAPPING
@@ -20,6 +20,7 @@
"name": "FrameworksServicesTests",
"options": [
{"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"},
{"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
{"exclude-annotation": "androidx.test.filters.FlakyTest"}
]
@@ -38,7 +39,8 @@
{
"name": "FrameworksServicesTests",
"options": [
- {"include-filter": "com.android.server.power"}
+ {"include-filter": "com.android.server.power"},
+ {"exclude-filter": "com.android.server.power.BatteryStatsTests"}
]
}
]
diff --git a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
index 4a57592a..27329e2 100644
--- a/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
+++ b/services/core/java/com/android/server/power/stats/BatteryStatsImpl.java
@@ -14613,17 +14613,13 @@
// Inform StatsLog of setBatteryState changes.
private void reportChangesToStatsLog(final int status, final int plugType, final int level) {
- if (!mHaveBatteryLevel) {
- return;
- }
-
- if (mBatteryStatus != status) {
+ if (!mHaveBatteryLevel || mBatteryStatus != status) {
FrameworkStatsLog.write(FrameworkStatsLog.CHARGING_STATE_CHANGED, status);
}
- if (mBatteryPlugType != plugType) {
+ if (!mHaveBatteryLevel || mBatteryPlugType != plugType) {
FrameworkStatsLog.write(FrameworkStatsLog.PLUGGED_STATE_CHANGED, plugType);
}
- if (mBatteryLevel != level) {
+ if (!mHaveBatteryLevel || mBatteryLevel != level) {
FrameworkStatsLog.write(FrameworkStatsLog.BATTERY_LEVEL_CHANGED, level);
}
}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
index 6e9a22c..efd8b6d 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerInternal.java
@@ -19,7 +19,6 @@
import android.annotation.Nullable;
import android.app.ITransientNotificationCallback;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
import android.os.Bundle;
import android.os.IBinder;
import android.view.WindowInsets.Type.InsetsType;
@@ -55,13 +54,13 @@
* @param displayId The display to which the IME is bound to.
* @param token The IME token.
* @param vis Bit flags about the IME visibility.
+ * (e.g. {@link android.inputmethodservice.InputMethodService#IME_ACTIVE})
* @param backDisposition Bit flags about the IME back disposition.
+ * (e.g. {@link android.inputmethodservice.InputMethodService#BACK_DISPOSITION_DEFAULT})
* @param showImeSwitcher {@code true} when the IME switcher button should be shown.
*/
- void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
- boolean showImeSwitcher);
+ void setImeWindowStatus(int displayId, IBinder token, int vis,
+ int backDisposition, boolean showImeSwitcher);
/**
* See {@link android.app.StatusBarManager#setIcon(String, int, int, String)}.
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 2c381ca..cc849b6 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -59,7 +59,6 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.fingerprint.IUdfpsRefreshRateRequestCallback;
-import android.inputmethodservice.InputMethodService;
import android.media.INearbyMediaDevicesProvider;
import android.media.MediaRoute2Info;
import android.net.Uri;
@@ -533,9 +532,7 @@
}
@Override
- public void setImeWindowStatus(int displayId, IBinder token,
- @InputMethodService.ImeWindowVisibility int vis,
- @InputMethodService.BackDispositionMode int backDisposition,
+ public void setImeWindowStatus(int displayId, IBinder token, int vis, int backDisposition,
boolean showImeSwitcher) {
StatusBarManagerService.this.setImeWindowStatus(displayId, token, vis, backDisposition,
showImeSwitcher);
@@ -1267,14 +1264,12 @@
}
@Override
- public void setImeWindowStatus(int displayId, final IBinder token,
- @InputMethodService.ImeWindowVisibility final int vis,
- @InputMethodService.BackDispositionMode final int backDisposition,
- final boolean showImeSwitcher) {
+ public void setImeWindowStatus(int displayId, final IBinder token, final int vis,
+ final int backDisposition, final boolean showImeSwitcher) {
enforceStatusBar();
if (SPEW) {
- Slog.d(TAG, "setImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
+ Slog.d(TAG, "swetImeWindowStatus vis=" + vis + " backDisposition=" + backDisposition);
}
synchronized(mLock) {
@@ -1337,9 +1332,7 @@
private String mPackageName = "none";
private int mDisabled1 = 0;
private int mDisabled2 = 0;
- @InputMethodService.ImeWindowVisibility
private int mImeWindowVis = 0;
- @InputMethodService.BackDispositionMode
private int mImeBackDisposition = 0;
private boolean mShowImeSwitcher = false;
private IBinder mImeToken = null;
@@ -1384,8 +1377,7 @@
return mDisabled1 == disabled1 && mDisabled2 == disabled2;
}
- private void setImeWindowState(@InputMethodService.ImeWindowVisibility final int vis,
- @InputMethodService.BackDispositionMode final int backDisposition,
+ private void setImeWindowState(final int vis, final int backDisposition,
final boolean showImeSwitcher, final IBinder token) {
mImeWindowVis = vis;
mImeBackDisposition = backDisposition;
diff --git a/services/core/java/com/android/server/utils/AlarmQueue.java b/services/core/java/com/android/server/utils/AlarmQueue.java
index 09ba195..83605ae 100644
--- a/services/core/java/com/android/server/utils/AlarmQueue.java
+++ b/services/core/java/com/android/server/utils/AlarmQueue.java
@@ -151,6 +151,10 @@
@GuardedBy("mLock")
@ElapsedRealtimeLong
private long mTriggerTimeElapsed = NOT_SCHEDULED;
+ /** The last time an alarm went off (ie. the last time {@link #onAlarm()} was called}). */
+ @GuardedBy("mLock")
+ @ElapsedRealtimeLong
+ private long mLastFireTimeElapsed;
/**
* @param alarmTag The tag to use when scheduling the alarm with AlarmManager.
@@ -284,7 +288,7 @@
/** Sets an alarm with {@link AlarmManager} for the earliest alarm in the queue after now. */
@GuardedBy("mLock")
private void setNextAlarmLocked() {
- setNextAlarmLocked(mInjector.getElapsedRealtime());
+ setNextAlarmLocked(mLastFireTimeElapsed + mMinTimeBetweenAlarmsMs);
}
/**
@@ -334,6 +338,7 @@
final ArraySet<K> expired = new ArraySet<>();
synchronized (mLock) {
final long nowElapsed = mInjector.getElapsedRealtime();
+ mLastFireTimeElapsed = nowElapsed;
while (mAlarmPriorityQueue.size() > 0) {
final Pair<K, Long> alarm = mAlarmPriorityQueue.peek();
if (alarm.second <= nowElapsed) {
diff --git a/services/core/java/com/android/server/vr/VrManagerService.java b/services/core/java/com/android/server/vr/VrManagerService.java
index b296ef2..1ff01a6 100644
--- a/services/core/java/com/android/server/vr/VrManagerService.java
+++ b/services/core/java/com/android/server/vr/VrManagerService.java
@@ -1049,7 +1049,11 @@
for (ComponentName c : possibleServices) {
if (Objects.equals(c.getPackageName(), pkg)) {
- nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ try {
+ nm.setNotificationListenerAccessGrantedForUser(c, userId, true);
+ } catch (Exception e) {
+ Slog.w(TAG, "Could not grant NLS access to package " + pkg, e);
+ }
}
}
}
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index 9b2cdd7..ee7dc50 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -3113,7 +3113,10 @@
if (which == FLAG_SYSTEM && systemIsStatic && systemIsBoth) {
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ " updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)
+ && !isLockscreenLiveWallpaperEnabled()) {
+ which |= FLAG_LOCK;
+ }
}
wallpaper = getWallpaperSafeLocked(userId, which);
@@ -3141,13 +3144,13 @@
}
}
- private void migrateStaticSystemToLockWallpaperLocked(int userId) {
+ private boolean migrateStaticSystemToLockWallpaperLocked(int userId) {
WallpaperData sysWP = mWallpaperMap.get(userId);
if (sysWP == null) {
if (DEBUG) {
Slog.i(TAG, "No system wallpaper? Not tracking for lock-only");
}
- return;
+ return true;
}
// We know a-priori that there is no lock-only wallpaper currently
@@ -3174,9 +3177,12 @@
SELinux.restorecon(lockWP.getWallpaperFile());
mLastLockWallpaper = lockWP;
}
+ return true;
} catch (ErrnoException e) {
- Slog.e(TAG, "Can't migrate system wallpaper: " + e.getMessage());
+ // can happen when migrating default wallpaper (which is not stored in wallpaperFile)
+ Slog.w(TAG, "Couldn't migrate system wallpaper: " + e.getMessage());
clearWallpaperBitmaps(lockWP);
+ return false;
}
}
@@ -3388,7 +3394,9 @@
// therefore it's a shared system+lock image that we need to migrate.
Slog.i(TAG, "Migrating current wallpaper to be lock-only before"
+ "updating system wallpaper");
- migrateStaticSystemToLockWallpaperLocked(userId);
+ if (!migrateStaticSystemToLockWallpaperLocked(userId)) {
+ which |= FLAG_LOCK;
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/AccessibilityController.java b/services/core/java/com/android/server/wm/AccessibilityController.java
index f300113..b3ae2ee 100644
--- a/services/core/java/com/android/server/wm/AccessibilityController.java
+++ b/services/core/java/com/android/server/wm/AccessibilityController.java
@@ -1280,45 +1280,53 @@
}
void drawIfNeeded(SurfaceControl.Transaction t) {
+ // Drawing variables (alpha, dirty rect, and bounds) access is synchronized
+ // using WindowManagerGlobalLock. Grab copies of these values before
+ // drawing on the canvas so that drawing can be performed outside of the lock.
+ int alpha;
+ Rect drawingRect = null;
+ Region drawingBounds = null;
synchronized (mService.mGlobalLock) {
if (!mInvalidated) {
return;
}
mInvalidated = false;
- if (mAlpha > 0) {
- Canvas canvas = null;
- try {
- // Empty dirty rectangle means unspecified.
- if (mDirtyRect.isEmpty()) {
- mBounds.getBounds(mDirtyRect);
- }
- mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
- canvas = mSurface.lockCanvas(mDirtyRect);
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
- }
- } catch (IllegalArgumentException iae) {
- /* ignore */
- } catch (Surface.OutOfResourcesException oore) {
- /* ignore */
- }
- if (canvas == null) {
- return;
- }
- if (DEBUG_VIEWPORT_WINDOW) {
- Slog.i(LOG_TAG, "Bounds: " + mBounds);
- }
- canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
- mPaint.setAlpha(mAlpha);
- Path path = mBounds.getBoundaryPath();
- canvas.drawPath(path, mPaint);
- mSurface.unlockCanvasAndPost(canvas);
- t.show(mSurfaceControl);
- } else {
- t.hide(mSurfaceControl);
+ alpha = mAlpha;
+ if (alpha > 0) {
+ drawingBounds = new Region(mBounds);
+ // Empty dirty rectangle means unspecified.
+ if (mDirtyRect.isEmpty()) {
+ mBounds.getBounds(mDirtyRect);
+ }
+ mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth);
+ drawingRect = new Rect(mDirtyRect);
+ if (DEBUG_VIEWPORT_WINDOW) {
+ Slog.i(LOG_TAG, "ViewportWindow bounds: " + mBounds);
+ Slog.i(LOG_TAG, "ViewportWindow dirty rect: " + mDirtyRect);
+ }
}
}
+
+ // Draw without holding WindowManagerGlobalLock.
+ if (alpha > 0) {
+ Canvas canvas = null;
+ try {
+ canvas = mSurface.lockCanvas(drawingRect);
+ } catch (IllegalArgumentException | OutOfResourcesException e) {
+ /* ignore */
+ }
+ if (canvas == null) {
+ return;
+ }
+ canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
+ mPaint.setAlpha(alpha);
+ canvas.drawPath(drawingBounds.getBoundaryPath(), mPaint);
+ mSurface.unlockCanvasAndPost(canvas);
+ t.show(mSurfaceControl);
+ } else {
+ t.hide(mSurfaceControl);
+ }
}
void releaseSurface() {
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 788bfbc..0994fa4 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -4560,6 +4560,26 @@
}
task.forAllActivities(fromActivity -> {
if (fromActivity == this) return true;
+ // The snapshot starting window could remove itself when receive resized request without
+ // redraw, so transfer it to a different size activity could only cause flicker.
+ // By schedule remove snapshot starting window, the remove process will happen when
+ // transition ready, transition ready means the app window is drawn.
+ final StartingData tmpStartingData = fromActivity.mStartingData;
+ if (tmpStartingData != null && tmpStartingData.mAssociatedTask == null
+ && mTransitionController.isCollecting(fromActivity)
+ && tmpStartingData instanceof SnapshotStartingData) {
+ final Rect fromBounds = fromActivity.getBounds();
+ final Rect myBounds = getBounds();
+ if (!fromBounds.equals(myBounds)) {
+ // Mark as no animation, so these changes won't merge into playing transition.
+ if (mTransitionController.inPlayingTransition(fromActivity)) {
+ mTransitionController.setNoAnimation(this);
+ mTransitionController.setNoAnimation(fromActivity);
+ }
+ fromActivity.removeStartingWindow();
+ return true;
+ }
+ }
return !fromActivity.isVisibleRequested() && transferStartingWindow(fromActivity);
});
}
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
index 32f1f42..a2547fd 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerInternal.java
@@ -23,6 +23,7 @@
import android.app.AppProtoEnums;
import android.app.BackgroundStartPrivileges;
import android.app.IActivityManager;
+import android.app.IAppTask;
import android.app.IApplicationThread;
import android.app.ITaskStackListener;
import android.app.ProfilerInfo;
@@ -308,6 +309,12 @@
public abstract void notifyActiveDreamChanged(@Nullable ComponentName activeDreamComponent);
/**
+ * Starts a dream activity in the DreamService's process.
+ */
+ public abstract IAppTask startDreamActivity(@NonNull Intent intent, int callingUid,
+ int callingPid);
+
+ /**
* Set a uid that is allowed to bypass stopped app switches, launching an app
* whenever it wants.
*
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 10ff3a3..78da5de 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -1467,23 +1467,8 @@
return false;
}
- private void enforceCallerIsDream(String callerPackageName) {
- final long origId = Binder.clearCallingIdentity();
- try {
- if (!canLaunchDreamActivity(callerPackageName)) {
- throw new SecurityException("The dream activity can be started only when the device"
- + " is dreaming and only by the active dream package.");
- }
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
- }
-
- @Override
- public boolean startDreamActivity(@NonNull Intent intent) {
- assertPackageMatchesCallingUid(intent.getPackage());
- enforceCallerIsDream(intent.getPackage());
-
+ private IAppTask startDreamActivityInternal(@NonNull Intent intent, int callingUid,
+ int callingPid) {
final ActivityInfo a = new ActivityInfo();
a.theme = com.android.internal.R.style.Theme_Dream;
a.exported = true;
@@ -1501,7 +1486,7 @@
options.setLaunchActivityType(ACTIVITY_TYPE_DREAM);
synchronized (mGlobalLock) {
- final WindowProcessController process = mProcessMap.getProcess(Binder.getCallingPid());
+ final WindowProcessController process = mProcessMap.getProcess(callingPid);
a.packageName = process.mInfo.packageName;
a.applicationInfo = process.mInfo;
@@ -1509,26 +1494,25 @@
a.uiOptions = process.mInfo.uiOptions;
a.taskAffinity = "android:" + a.packageName + "/dream";
- final int callingUid = Binder.getCallingUid();
- final int callingPid = Binder.getCallingPid();
- final long origId = Binder.clearCallingIdentity();
- try {
- getActivityStartController().obtainStarter(intent, "dream")
- .setCallingUid(callingUid)
- .setCallingPid(callingPid)
- .setCallingPackage(intent.getPackage())
- .setActivityInfo(a)
- .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
- // To start the dream from background, we need to start it from a persistent
- // system process. Here we set the real calling uid to the system server uid
- .setRealCallingUid(Binder.getCallingUid())
- .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
- .execute();
- return true;
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ final ActivityRecord[] outActivity = new ActivityRecord[1];
+ getActivityStartController().obtainStarter(intent, "dream")
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
+ .setCallingPackage(intent.getPackage())
+ .setActivityInfo(a)
+ .setActivityOptions(createSafeActivityOptionsWithBalAllowed(options))
+ .setOutActivity(outActivity)
+ // To start the dream from background, we need to start it from a persistent
+ // system process. Here we set the real calling uid to the system server uid
+ .setRealCallingUid(Binder.getCallingUid())
+ .setBackgroundStartPrivileges(BackgroundStartPrivileges.ALLOW_BAL)
+ .execute();
+
+ final ActivityRecord started = outActivity[0];
+ final IAppTask appTask = started == null ? null :
+ new AppTaskImpl(this, started.getTask().mTaskId, callingUid);
+ return appTask;
}
}
@@ -5926,6 +5910,11 @@
}
@Override
+ public IAppTask startDreamActivity(@NonNull Intent intent, int callingUid, int callingPid) {
+ return startDreamActivityInternal(intent, callingUid, callingPid);
+ }
+
+ @Override
public void setAllowAppSwitches(@NonNull String type, int uid, int userId) {
if (!mAmInternal.isUserRunning(userId, ActivityManager.FLAG_OR_STOPPED)) {
return;
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 12b5f5f..bfd2a10 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -3701,6 +3701,7 @@
mInputMonitor.dump(pw, " ");
pw.println();
mInsetsStateController.dump(prefix, pw);
+ mInsetsPolicy.dump(prefix, pw);
mDwpcHelper.dump(prefix, pw);
pw.println();
}
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index a02fd11..98c15dd2 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -327,8 +327,6 @@
private WindowState mTopFullscreenOpaqueWindowState;
private boolean mTopIsFullscreen;
private int mNavBarOpacityMode = NAV_BAR_OPAQUE_WHEN_FREEFORM_OR_DOCKED;
- private boolean mForceConsumeSystemBars;
- private boolean mForceShowSystemBars;
/**
* Windows that provides gesture insets. If multiple windows provide gesture insets at the same
@@ -1287,20 +1285,6 @@
}
/**
- * @return true if the system bars are forced to be consumed
- */
- public boolean areSystemBarsForcedConsumedLw() {
- return mForceConsumeSystemBars;
- }
-
- /**
- * @return true if the system bars are forced to stay visible
- */
- public boolean areSystemBarsForcedShownLw() {
- return mForceShowSystemBars;
- }
-
- /**
* Computes the frames of display (its logical size, rotation and cutout should already be set)
* used to layout window. This method only changes the given display frames, insets state and
* some temporal states, but doesn't change the window frames used to show on screen.
@@ -1694,7 +1678,8 @@
* @return Whether the top fullscreen app hides the given type of system bar.
*/
boolean topAppHidesSystemBar(@InsetsType int type) {
- if (mTopFullscreenOpaqueWindowState == null || mForceShowSystemBars) {
+ if (mTopFullscreenOpaqueWindowState == null
+ || getInsetsPolicy().areTypesForciblyShowing(type)) {
return false;
}
return !mTopFullscreenOpaqueWindowState.isRequestedVisible(type);
@@ -2371,14 +2356,7 @@
final boolean freeformRootTaskVisible =
defaultTaskDisplayArea.isRootTaskVisible(WINDOWING_MODE_FREEFORM);
- // We need to force showing system bars when adjacent tasks or freeform roots visible.
- mForceShowSystemBars = adjacentTasksVisible || freeformRootTaskVisible;
- // We need to force the consumption of the system bars if they are force shown or if they
- // are controlled by a remote insets controller.
- mForceConsumeSystemBars = mForceShowSystemBars
- || getInsetsPolicy().remoteInsetsControllerControlsSystemBars(win)
- || getInsetsPolicy().forcesShowingNavigationBars(win);
- mDisplayContent.getInsetsPolicy().updateBarControlTarget(win);
+ getInsetsPolicy().updateSystemBars(win, adjacentTasksVisible, freeformRootTaskVisible);
final boolean topAppHidesStatusBar = topAppHidesSystemBar(Type.statusBars());
if (getStatusBar() != null) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 798dc85..4620266 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -64,6 +64,8 @@
import com.android.server.DisplayThread;
import com.android.server.statusbar.StatusBarManagerInternal;
+import java.io.PrintWriter;
+
/**
* Policy that implements who gets control over the windows generating insets.
*/
@@ -114,6 +116,7 @@
private final BarWindow mStatusBar = new BarWindow(StatusBarManager.WINDOW_STATUS_BAR);
private final BarWindow mNavBar = new BarWindow(StatusBarManager.WINDOW_NAVIGATION_BAR);
private @InsetsType int mShowingTransientTypes;
+ private @InsetsType int mForcedShowingTypes;
private boolean mAnimatingShown;
private final boolean mHideNavBarForKeyboard;
@@ -127,7 +130,6 @@
mHideNavBarForKeyboard = r.getBoolean(R.bool.config_hideNavBarForKeyboard);
}
-
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
if (mFocusedWin != focusedWin) {
@@ -514,7 +516,7 @@
component, focusedWin.getRequestedVisibleTypes());
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (mPolicy.areSystemBarsForcedShownLw()) {
+ if (areTypesForciblyShowing(Type.statusBars())) {
// Status bar is forcibly shown. We don't want the client to control the status bar, and
// we will dispatch the real visibility of status bar to the client.
return null;
@@ -567,13 +569,6 @@
return focusedWin;
}
}
- if (forcesShowingNavigationBars(focusedWin)) {
- // When "force show navigation bar" is enabled, it means both force visible is true, and
- // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
- // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
- // still control the navigation bar in this mode.
- return null;
- }
if (remoteInsetsControllerControlsSystemBars(focusedWin)) {
ComponentName component = focusedWin.mActivityRecord != null
? focusedWin.mActivityRecord.mActivityComponent : null;
@@ -581,7 +576,7 @@
component, focusedWin.getRequestedVisibleTypes());
return mDisplayContent.mRemoteInsetsControlTarget;
}
- if (mPolicy.areSystemBarsForcedShownLw()) {
+ if (areTypesForciblyShowing(Type.navigationBars())) {
// Navigation bar is forcibly shown. We don't want the client to control the navigation
// bar, and we will dispatch the real visibility of navigation bar to the client.
return null;
@@ -603,7 +598,32 @@
return focusedWin;
}
- boolean forcesShowingNavigationBars(WindowState win) {
+ boolean areTypesForciblyShowing(@InsetsType int types) {
+ return (mForcedShowingTypes & types) == types;
+ }
+
+ void updateSystemBars(WindowState win, boolean inSplitScreenMode, boolean inFreeformMode) {
+ mForcedShowingTypes = (inSplitScreenMode || inFreeformMode)
+ ? (Type.statusBars() | Type.navigationBars())
+ : forceShowingNavigationBars(win)
+ ? Type.navigationBars()
+ : 0;
+
+ // The client app won't be able to control these types of system bars. Here makes the client
+ // forcibly consume these types to prevent the app content from getting obscured.
+ mStateController.setForcedConsumingTypes(
+ mForcedShowingTypes | (remoteInsetsControllerControlsSystemBars(win)
+ ? (Type.statusBars() | Type.navigationBars())
+ : 0));
+
+ updateBarControlTarget(win);
+ }
+
+ private boolean forceShowingNavigationBars(WindowState win) {
+ // When "force show navigation bar" is enabled, it means both force visible is true, and
+ // we are in 3-button navigation. In this mode, the navigation bar is forcibly shown
+ // when activity type is ACTIVITY_TYPE_STANDARD which means Launcher or Recent could
+ // still control the navigation bar in this mode.
return mPolicy.isForceShowNavigationBarEnabled() && win != null
&& win.getActivityType() == ACTIVITY_TYPE_STANDARD;
}
@@ -696,6 +716,21 @@
wereRevealedFromSwipeOnSystemBar);
}
+ void dump(String prefix, PrintWriter pw) {
+ pw.println(prefix + "InsetsPolicy");
+ prefix = prefix + " ";
+ pw.println(prefix + "status: " + StatusBarManager.windowStateToString(mStatusBar.mState));
+ pw.println(prefix + "nav: " + StatusBarManager.windowStateToString(mNavBar.mState));
+ if (mShowingTransientTypes != 0) {
+ pw.println(prefix + "mShowingTransientTypes="
+ + WindowInsets.Type.toString(mShowingTransientTypes));
+ }
+ if (mForcedShowingTypes != 0) {
+ pw.println(prefix + "mForcedShowingTypes="
+ + WindowInsets.Type.toString(mForcedShowingTypes));
+ }
+ }
+
private class BarWindow {
private final int mId;
diff --git a/services/core/java/com/android/server/wm/InsetsSourceProvider.java b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
index e1c865b..2b8312c 100644
--- a/services/core/java/com/android/server/wm/InsetsSourceProvider.java
+++ b/services/core/java/com/android/server/wm/InsetsSourceProvider.java
@@ -40,6 +40,7 @@
import android.util.SparseArray;
import android.util.proto.ProtoOutputStream;
import android.view.InsetsSource;
+import android.view.InsetsSource.Flags;
import android.view.InsetsSourceControl;
import android.view.SurfaceControl;
import android.view.SurfaceControl.Transaction;
@@ -81,6 +82,8 @@
private final Rect mSourceFrame = new Rect();
private final Rect mLastSourceFrame = new Rect();
private @NonNull Insets mInsetsHint = Insets.NONE;
+ private @Flags int mFlagsFromFrameProvider;
+ private @Flags int mFlagsFromServer;
private final Consumer<Transaction> mSetLeashPositionConsumer = t -> {
if (mControl != null) {
@@ -189,6 +192,16 @@
}
}
+ boolean setFlags(@Flags int flags, @Flags int mask) {
+ mFlagsFromServer = (mFlagsFromServer & ~mask) | (flags & mask);
+ final @Flags int mergedFlags = mFlagsFromFrameProvider | mFlagsFromServer;
+ if (mSource.getFlags() != mergedFlags) {
+ mSource.setFlags(mergedFlags);
+ return true;
+ }
+ return false;
+ }
+
/**
* The source frame can affect the layout of other windows, so this should be called once the
* window container gets laid out.
@@ -217,11 +230,11 @@
mSourceFrame.set(frame);
if (mFrameProvider != null) {
- final int flags = mFrameProvider.apply(
+ mFlagsFromFrameProvider = mFrameProvider.apply(
mWindowContainer.getDisplayContent().mDisplayFrames,
mWindowContainer,
mSourceFrame);
- mSource.setFlags(flags);
+ mSource.setFlags(mFlagsFromFrameProvider | mFlagsFromServer);
}
updateSourceFrameForServerVisibility();
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index addb521..081ebe0 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.InsetsSource.FLAG_FORCE_CONSUMING;
import static android.view.InsetsSource.ID_IME;
import static android.view.WindowInsets.Type.displayCutout;
import static android.view.WindowInsets.Type.ime;
@@ -85,6 +86,8 @@
}
};
+ private @InsetsType int mForcedConsumingTypes;
+
InsetsStateController(DisplayContent displayContent) {
mDisplayContent = displayContent;
}
@@ -122,6 +125,11 @@
provider = id == ID_IME
? new ImeInsetsSourceProvider(source, this, mDisplayContent)
: new InsetsSourceProvider(source, this, mDisplayContent);
+ provider.setFlags(
+ (mForcedConsumingTypes & type) != 0
+ ? FLAG_FORCE_CONSUMING
+ : 0,
+ FLAG_FORCE_CONSUMING);
mProviders.put(id, provider);
return provider;
}
@@ -137,6 +145,24 @@
}
}
+ void setForcedConsumingTypes(@InsetsType int types) {
+ if (mForcedConsumingTypes != types) {
+ mForcedConsumingTypes = types;
+ boolean changed = false;
+ for (int i = mProviders.size() - 1; i >= 0; i--) {
+ final InsetsSourceProvider provider = mProviders.valueAt(i);
+ changed |= provider.setFlags(
+ (types & provider.getSource().getType()) != 0
+ ? FLAG_FORCE_CONSUMING
+ : 0,
+ FLAG_FORCE_CONSUMING);
+ }
+ if (changed) {
+ notifyInsetsChanged();
+ }
+ }
+ }
+
/**
* Called when a layout pass has occurred.
*/
@@ -391,6 +417,10 @@
for (int i = mProviders.size() - 1; i >= 0; i--) {
mProviders.valueAt(i).dump(pw, prefix + " ");
}
+ if (mForcedConsumingTypes != 0) {
+ pw.println(prefix + "mForcedConsumingTypes="
+ + WindowInsets.Type.toString(mForcedConsumingTypes));
+ }
}
void dumpDebug(ProtoOutputStream proto, @WindowTraceLogLevel int logLevel) {
diff --git a/services/core/java/com/android/server/wm/KeyguardController.java b/services/core/java/com/android/server/wm/KeyguardController.java
index ad9c3b2..83fd725 100644
--- a/services/core/java/com/android/server/wm/KeyguardController.java
+++ b/services/core/java/com/android/server/wm/KeyguardController.java
@@ -418,13 +418,17 @@
return;
}
- final boolean waitAppTransition = isKeyguardLocked(displayId);
- mWindowManager.mPolicy.onKeyguardOccludedChangedLw(isDisplayOccluded(DEFAULT_DISPLAY),
- waitAppTransition);
- if (waitAppTransition) {
- mService.deferWindowLayout();
- try {
- if (isDisplayOccluded(DEFAULT_DISPLAY)) {
+ final TransitionController tc = mRootWindowContainer.mTransitionController;
+
+ final boolean occluded = isDisplayOccluded(displayId);
+ final boolean performTransition = isKeyguardLocked(displayId);
+ final boolean executeTransition = performTransition && !tc.isCollecting();
+
+ mWindowManager.mPolicy.onKeyguardOccludedChangedLw(occluded);
+ mService.deferWindowLayout();
+ try {
+ if (isKeyguardLocked(displayId)) {
+ if (occluded) {
mRootWindowContainer.getDefaultDisplay().requestTransitionAndLegacyPrepare(
TRANSIT_KEYGUARD_OCCLUDE,
TRANSIT_FLAG_KEYGUARD_OCCLUDING,
@@ -434,11 +438,19 @@
TRANSIT_KEYGUARD_UNOCCLUDE,
TRANSIT_FLAG_KEYGUARD_UNOCCLUDING);
}
- updateKeyguardSleepToken(DEFAULT_DISPLAY);
- mWindowManager.executeAppTransition();
- } finally {
- mService.continueWindowLayout();
+ } else {
+ if (tc.inTransition()) {
+ tc.mStateValidators.add(mWindowManager.mPolicy::applyKeyguardOcclusionChange);
+ } else {
+ mWindowManager.mPolicy.applyKeyguardOcclusionChange();
+ }
}
+ updateKeyguardSleepToken(displayId);
+ if (performTransition && executeTransition) {
+ mWindowManager.executeAppTransition();
+ }
+ } finally {
+ mService.continueWindowLayout();
}
}
@@ -485,6 +497,9 @@
}
}
+ /**
+ * @return true if Keyguard is occluded or the device is dreaming.
+ */
boolean isDisplayOccluded(int displayId) {
return getDisplayState(displayId).mOccluded;
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 54fec3e..d9a954f 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -1061,8 +1061,10 @@
displayHasContent = true;
} else if (displayContent != null &&
(!mObscureApplicationContentOnSecondaryDisplays
+ || displayContent.isKeyguardAlwaysUnlocked()
|| (obscured && type == TYPE_KEYGUARD_DIALOG))) {
- // Allow full screen keyguard presentation dialogs to be seen.
+ // Allow full screen keyguard presentation dialogs to be seen, or simply ignore the
+ // keyguard if this display is always unlocked.
displayHasContent = true;
}
if ((privateflags & PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE) != 0) {
diff --git a/services/core/java/com/android/server/wm/SafeActivityOptions.java b/services/core/java/com/android/server/wm/SafeActivityOptions.java
index 5860776..c914fa1 100644
--- a/services/core/java/com/android/server/wm/SafeActivityOptions.java
+++ b/services/core/java/com/android/server/wm/SafeActivityOptions.java
@@ -48,6 +48,7 @@
import android.os.UserHandle;
import android.util.Slog;
import android.view.RemoteAnimationAdapter;
+import android.window.RemoteTransition;
import android.window.WindowContainerToken;
import com.android.internal.annotations.VisibleForTesting;
@@ -385,6 +386,18 @@
throw new SecurityException(msg);
}
+ // Check permission for remote transitions
+ final RemoteTransition transition = options.getRemoteTransition();
+ if (transition != null && supervisor.mService.checkPermission(
+ CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS, callingPid, callingUid)
+ != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: starting " + getIntentString(intent)
+ + " from " + callerApp + " (pid=" + callingPid
+ + ", uid=" + callingUid + ") with remoteTransition";
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+
// If launched from bubble is specified, then ensure that the caller is system or sysui.
if (options.getLaunchedFromBubble() && !isSystemOrSystemUI(callingPid, callingUid)) {
final String msg = "Permission Denial: starting " + getIntentString(intent)
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 39772dda..9c23beb 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3473,6 +3473,11 @@
top.mLetterboxUiController.getLetterboxPositionForVerticalReachability();
}
}
+ // User Aspect Ratio Settings is enabled if the app is not in SCM
+ info.topActivityEligibleForUserAspectRatioButton =
+ mWmService.mLetterboxConfiguration.isUserAppAspectRatioSettingsEnabled()
+ && top != null && !info.topActivityInSizeCompat;
+ info.topActivityBoundsLetterboxed = top != null && top.areBoundsLetterboxed();
}
/**
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 79839ef..1b27bb1 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -27,6 +27,7 @@
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
import static android.view.WindowManager.INPUT_CONSUMER_RECENTS_ANIMATION;
+import static android.view.WindowManager.KEYGUARD_VISIBILITY_TRANSIT_FLAGS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_SEAMLESS;
import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
@@ -77,6 +78,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.Trace;
@@ -613,12 +615,8 @@
}
}
if (mParticipants.contains(wc)) return;
- // Wallpaper is like in a static drawn state unless display may have changes, so exclude
- // the case to reduce transition latency waiting for the unchanged wallpaper to redraw.
- final boolean needSync = (!isWallpaper(wc) || mParticipants.contains(wc.mDisplayContent))
- // Transient-hide may be hidden later, so no need to request redraw.
- && !isInTransientHide(wc);
- if (needSync) {
+ // Transient-hide may be hidden later, so no need to request redraw.
+ if (!isInTransientHide(wc)) {
mSyncEngine.addToSyncSet(mSyncId, wc);
}
ChangeInfo info = mChanges.get(wc);
@@ -1101,6 +1099,16 @@
final Task task = ar.getTask();
if (task == null) continue;
boolean visibleAtTransitionEnd = mVisibleAtTransitionEndTokens.contains(ar);
+ // visibleAtTransitionEnd is used to guard against pre-maturely committing
+ // invisible on a window which is actually hidden by a later transition and not this
+ // one. However, for a transient launch, we can't use this mechanism because the
+ // visibility is determined at finish. Instead, use a different heuristic: don't
+ // commit invisible if the window is already in a later transition. That later
+ // transition will then handle the commit.
+ if (isTransientLaunch(ar) && !ar.isVisibleRequested()
+ && mController.inCollectingTransition(ar)) {
+ visibleAtTransitionEnd = true;
+ }
// We need both the expected visibility AND current requested-visibility to be
// false. If it is expected-visible but not currently visible, it means that
// another animation is queued-up to animate this to invisibility, so we can't
@@ -2657,7 +2665,7 @@
}
private void validateKeyguardOcclusion() {
- if ((mFlags & TRANSIT_FLAG_KEYGUARD_LOCKED) != 0) {
+ if ((mFlags & KEYGUARD_VISIBILITY_TRANSIT_FLAGS) != 0) {
mController.mStateValidators.add(
mController.mAtm.mWindowManager.mPolicy::applyKeyguardOcclusionChange);
}
@@ -2686,6 +2694,26 @@
});
}
+ /**
+ * Returns {@code true} if the transition and the corresponding transaction should be applied
+ * on display thread. Currently, this only checks for display rotation change because the order
+ * of dispatching the new display info will be after requesting the windows to sync drawing.
+ * That avoids potential flickering of screen overlays (e.g. cutout, rounded corner). Also,
+ * because the display thread has a higher priority, it is faster to perform the configuration
+ * changes and window hierarchy traversal.
+ */
+ boolean shouldApplyOnDisplayThread() {
+ for (int i = mParticipants.size() - 1; i >= 0; --i) {
+ final DisplayContent dc = mParticipants.valueAt(i).asDisplayContent();
+ if (dc == null) continue;
+ final ChangeInfo changeInfo = mChanges.get(dc);
+ if (changeInfo != null && changeInfo.mRotation != dc.getRotation()) {
+ return Looper.myLooper() != mController.mAtm.mWindowManager.mH.getLooper();
+ }
+ }
+ return false;
+ }
+
/** Applies the new configuration for the changed displays. */
void applyDisplayChangeIfNeeded() {
for (int i = mParticipants.size() - 1; i >= 0; --i) {
diff --git a/services/core/java/com/android/server/wm/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index a539a48..79cb61b 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -588,15 +588,6 @@
/** Sets the sync method for the display change. */
private void setDisplaySyncMethod(@NonNull TransitionRequestInfo.DisplayChange displayChange,
@NonNull Transition displayTransition, @NonNull DisplayContent displayContent) {
- final int startRotation = displayChange.getStartRotation();
- final int endRotation = displayChange.getEndRotation();
- if (startRotation != endRotation && (startRotation + endRotation) % 2 == 0) {
- // 180 degrees rotation change may not change screen size. So the clients may draw
- // some frames before and after the display projection transaction is applied by the
- // remote player. That may cause some buffers to show in different rotation. So use
- // sync method to pause clients drawing until the projection transaction is applied.
- mSyncEngine.setSyncMethod(displayTransition.getSyncId(), BLASTSyncEngine.METHOD_BLAST);
- }
final Rect startBounds = displayChange.getStartAbsBounds();
final Rect endBounds = displayChange.getEndAbsBounds();
if (startBounds == null || endBounds == null) return;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 9e7df00..805e7ff 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@
import android.hardware.display.DisplayManagerInternal;
import android.os.Bundle;
import android.os.IBinder;
+import android.os.Message;
import android.util.Pair;
import android.view.ContentRecordingSession;
import android.view.Display;
@@ -515,12 +516,13 @@
* Invalidate all visible windows on a given display, and report back on the callback when all
* windows have redrawn.
*
- * @param callback reporting callback to be called when all windows have redrawn.
+ * @param message The message will be sent when all windows have redrawn. Note that the message
+ * must be obtained from handler, otherwise it will throw NPE.
* @param timeout calls the callback anyway after the timeout.
* @param displayId waits for the windows on the given display, INVALID_DISPLAY to wait for all
* windows on all displays.
*/
- public abstract void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId);
+ public abstract void waitForAllWindowsDrawn(Message message, long timeout, int displayId);
/**
* Overrides the display size.
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index bb3d109..68a6384 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -601,7 +601,7 @@
* The callbacks to make when the windows all have been drawn for a given
* {@link WindowContainer}.
*/
- final HashMap<WindowContainer, Runnable> mWaitingForDrawnCallbacks = new HashMap<>();
+ final ArrayMap<WindowContainer<?>, Message> mWaitingForDrawnCallbacks = new ArrayMap<>();
/** List of window currently causing non-system overlay windows to be hidden. */
private ArrayList<WindowState> mHidingNonSystemOverlayWindows = new ArrayList<>();
@@ -1789,9 +1789,6 @@
winAnimator.mEnterAnimationPending = true;
winAnimator.mEnteringAnimation = true;
- if (displayPolicy.areSystemBarsForcedConsumedLw()) {
- res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_SYSTEM_BARS;
- }
if (displayContent.isInTouchMode()) {
res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;
}
@@ -2494,9 +2491,6 @@
if (win.mActivityRecord != null) {
win.mActivityRecord.updateReportedVisibilityLocked();
}
- if (displayPolicy.areSystemBarsForcedConsumedLw()) {
- result |= WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_SYSTEM_BARS;
- }
if (!win.isGoneForLayout()) {
win.mResizedWhileGone = false;
}
@@ -5369,8 +5363,6 @@
public static final int CLIENT_FREEZE_TIMEOUT = 30;
public static final int NOTIFY_ACTIVITY_DRAWN = 32;
- public static final int ALL_WINDOWS_DRAWN = 33;
-
public static final int NEW_ANIMATOR_SCALE = 34;
public static final int SHOW_EMULATOR_DISPLAY_OVERLAY = 36;
@@ -5492,7 +5484,7 @@
}
case WAITING_FOR_DRAWN_TIMEOUT: {
- Runnable callback = null;
+ final Message callback;
final WindowContainer<?> container = (WindowContainer<?>) msg.obj;
synchronized (mGlobalLock) {
ProtoLog.w(WM_ERROR, "Timeout waiting for drawn: undrawn=%s",
@@ -5506,7 +5498,7 @@
callback = mWaitingForDrawnCallbacks.remove(container);
}
if (callback != null) {
- callback.run();
+ callback.sendToTarget();
}
break;
}
@@ -5530,17 +5522,6 @@
}
break;
}
- case ALL_WINDOWS_DRAWN: {
- Runnable callback;
- final WindowContainer container = (WindowContainer) msg.obj;
- synchronized (mGlobalLock) {
- callback = mWaitingForDrawnCallbacks.remove(container);
- }
- if (callback != null) {
- callback.run();
- }
- break;
- }
case NEW_ANIMATOR_SCALE: {
float scale = getCurrentAnimatorScale();
ValueAnimator.setDurationScale(scale);
@@ -6098,7 +6079,8 @@
if (mWaitingForDrawnCallbacks.isEmpty()) {
return;
}
- mWaitingForDrawnCallbacks.forEach((container, callback) -> {
+ for (int i = mWaitingForDrawnCallbacks.size() - 1; i >= 0; i--) {
+ final WindowContainer<?> container = mWaitingForDrawnCallbacks.keyAt(i);
for (int j = container.mWaitingForDrawn.size() - 1; j >= 0; j--) {
final WindowState win = (WindowState) container.mWaitingForDrawn.get(j);
ProtoLog.i(WM_DEBUG_SCREEN_ON,
@@ -6124,9 +6106,9 @@
if (container.mWaitingForDrawn.isEmpty()) {
ProtoLog.d(WM_DEBUG_SCREEN_ON, "All windows drawn!");
mH.removeMessages(H.WAITING_FOR_DRAWN_TIMEOUT, container);
- mH.sendMessage(mH.obtainMessage(H.ALL_WINDOWS_DRAWN, container));
+ mWaitingForDrawnCallbacks.removeAt(i).sendToTarget();
}
- });
+ }
}
private void traceStartWaitingForWindowDrawn(WindowState window) {
@@ -7842,13 +7824,14 @@
}
@Override
- public void waitForAllWindowsDrawn(Runnable callback, long timeout, int displayId) {
+ public void waitForAllWindowsDrawn(Message message, long timeout, int displayId) {
+ Objects.requireNonNull(message.getTarget());
final WindowContainer<?> container = displayId == INVALID_DISPLAY
? mRoot : mRoot.getDisplayContent(displayId);
if (container == null) {
// The waiting container doesn't exist, no need to wait to run the callback. Run and
// return;
- callback.run();
+ message.sendToTarget();
return;
}
boolean allWindowsDrawn = false;
@@ -7865,13 +7848,13 @@
}
}
- mWaitingForDrawnCallbacks.put(container, callback);
+ mWaitingForDrawnCallbacks.put(container, message);
mH.sendNewMessageDelayed(H.WAITING_FOR_DRAWN_TIMEOUT, container, timeout);
checkDrawnWindowsLocked();
}
}
if (allWindowsDrawn) {
- callback.run();
+ message.sendToTarget();
}
}
@@ -9081,7 +9064,7 @@
}
@Override
- public boolean getWindowInsets(int displayId, IBinder token, InsetsState outInsetsState) {
+ public void getWindowInsets(int displayId, IBinder token, InsetsState outInsetsState) {
final long origId = Binder.clearCallingIdentity();
try {
synchronized (mGlobalLock) {
@@ -9092,7 +9075,6 @@
}
final WindowToken winToken = dc.getWindowToken(token);
dc.getInsetsPolicy().getInsetsForWindowMetrics(winToken, outInsetsState);
- return dc.getDisplayPolicy().areSystemBarsForcedConsumedLw();
}
} finally {
Binder.restoreCallingIdentity(origId);
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index a2f7ba4..31918f4 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -321,9 +321,18 @@
applyTransaction(wct, -1 /*syncId*/, null /*transition*/, caller);
return transition.getToken();
}
- transition.start();
transition.mLogger.mStartWCT = wct;
- applyTransaction(wct, -1 /*syncId*/, transition, caller);
+ if (transition.shouldApplyOnDisplayThread()) {
+ mService.mH.post(() -> {
+ synchronized (mService.mGlobalLock) {
+ transition.start();
+ applyTransaction(wct, -1 /* syncId */, transition, caller);
+ }
+ });
+ } else {
+ transition.start();
+ applyTransaction(wct, -1 /* syncId */, transition, caller);
+ }
// Since the transition is already provided, it means WMCore is determining the
// "readiness lifecycle" outside the provided transaction, so don't set ready here.
return transition.getToken();
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index cf1e51f..ef447020 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3732,8 +3732,6 @@
final boolean isDragResizeChanged = isDragResizeChanged();
final boolean forceRelayout = syncWithBuffers || isDragResizeChanged;
final DisplayContent displayContent = getDisplayContent();
- final boolean alwaysConsumeSystemBars =
- displayContent.getDisplayPolicy().areSystemBarsForcedConsumedLw();
final int displayId = displayContent.getDisplayId();
if (isDragResizeChanged) {
@@ -3745,7 +3743,7 @@
try {
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
- getCompatInsetsState(), forceRelayout, alwaysConsumeSystemBars, displayId,
+ getCompatInsetsState(), forceRelayout, displayId,
syncWithBuffers ? mSyncSeqId : -1, isDragResizing);
if (drawPending && prevRotation >= 0 && prevRotation != mLastReportedConfiguration
.getMergedConfiguration().windowConfiguration.getRotation()) {
@@ -5665,7 +5663,7 @@
}
if (mIsWallpaper) {
// TODO(b/233286785): Add sync support to wallpaper.
- return false;
+ return true;
}
// In the WindowContainer implementation we immediately mark ready
// since a generic WindowContainer only needs to wait for its
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index c065cb5..e76cbe44 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -971,7 +971,7 @@
void NativeInputManager::notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
const std::set<gui::Uid>& uids) {
static const bool ENABLE_INPUT_DEVICE_USAGE_METRICS =
- sysprop::InputProperties::enable_input_device_usage_metrics().value_or(true);
+ sysprop::InputProperties::enable_input_device_usage_metrics().value_or(false);
if (!ENABLE_INPUT_DEVICE_USAGE_METRICS) return;
mInputManager->getMetricsCollector().notifyDeviceInteraction(deviceId, timestamp, uids);
diff --git a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
index 46c90b4..bafa4a5 100644
--- a/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
+++ b/services/credentials/java/com/android/server/credentials/ProviderRegistryGetSession.java
@@ -126,6 +126,7 @@
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
protected ProviderRegistryGetSession(@NonNull Context context,
@@ -143,6 +144,7 @@
mElementKeys = new HashSet<>(requestOption
.getCredentialRetrievalData()
.getStringArrayList(CredentialOption.SUPPORTED_ELEMENT_KEYS));
+ mStatus = Status.PENDING;
}
private List<Entry> prepareUiCredentialEntries(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 417fc39..fe913b9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -16229,6 +16229,11 @@
}
@Override
+ public ComponentName getDeviceOwnerComponent(boolean callingUserOnly) {
+ return DevicePolicyManagerService.this.getDeviceOwnerComponent(callingUserOnly);
+ }
+
+ @Override
public int getDeviceOwnerUserId() {
return DevicePolicyManagerService.this.getDeviceOwnerUserId();
}
diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
index a6ada4d..869497c 100644
--- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
+++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/DefaultImeVisibilityApplierTest.java
@@ -20,6 +20,7 @@
import static android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE;
import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SOFT_INPUT;
+import static com.android.internal.inputmethod.SoftInputShowHideReason.HIDE_SWITCH_USER;
import static com.android.internal.inputmethod.SoftInputShowHideReason.SHOW_SOFT_INPUT;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME;
import static com.android.server.inputmethod.ImeVisibilityStateComputer.STATE_HIDE_IME_EXPLICIT;
@@ -43,6 +44,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.android.internal.inputmethod.InputBindResult;
import com.android.internal.inputmethod.StartInputFlags;
import com.android.internal.inputmethod.StartInputReason;
@@ -165,6 +167,29 @@
verify(mMockImeTargetVisibilityPolicy).removeImeScreenshot(eq(Display.DEFAULT_DISPLAY));
}
+ @Test
+ public void testApplyImeVisibility_hideImeWhenUnbinding() {
+ mInputMethodManagerService.setAttachedClientForTesting(null);
+ startInputOrWindowGainedFocus(mWindowToken, SOFT_INPUT_STATE_ALWAYS_VISIBLE);
+ ExtendedMockito.spyOn(mVisibilityApplier);
+
+ synchronized (ImfLock.class) {
+ // Simulate the system hides the IME when switching IME services in different users.
+ // (e.g. unbinding the IME from the current user to the profile user)
+ final int displayIdToShowIme = mInputMethodManagerService.getDisplayIdToShowImeLocked();
+ mInputMethodManagerService.hideCurrentInputLocked(mWindowToken, null, 0, null,
+ HIDE_SWITCH_USER);
+ mInputMethodManagerService.onUnbindCurrentMethodByReset();
+
+ // Expects applyImeVisibility() -> hideIme() will be called to notify WM for syncing
+ // the IME hidden state.
+ verify(mVisibilityApplier).applyImeVisibility(eq(mWindowToken), any(),
+ eq(STATE_HIDE_IME));
+ verify(mInputMethodManagerService.mWindowManagerInternal).hideIme(
+ eq(mWindowToken), eq(displayIdToShowIme), eq(null));
+ }
+ }
+
private InputBindResult startInputOrWindowGainedFocus(IBinder windowToken, int softInputMode) {
return mInputMethodManagerService.startInputOrWindowGainedFocus(
StartInputReason.WINDOW_FOCUS_GAIN /* startInputReason */,
diff --git a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
index e33ca77..b7a0cf3 100644
--- a/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
+++ b/services/tests/PackageManagerServiceTests/appenumeration/src/com/android/server/pm/test/appenumeration/CrossUserPackageVisibilityTests.java
@@ -138,6 +138,14 @@
}
@Test
+ public void testGetUserMinAspectRatio_withCrossUserId() {
+ final int crossUserId = UserHandle.myUserId() + 1;
+ assertThrows(SecurityException.class,
+ () -> mIPackageManager.getUserMinAspectRatio(
+ mInstrumentation.getContext().getPackageName(), crossUserId));
+ }
+
+ @Test
public void testIsPackageSignedByKeySet_cannotDetectCrossUserPkg() throws Exception {
final KeySet keySet = mIPackageManager.getSigningKeySet(mContext.getPackageName());
assertThrows(IllegalArgumentException.class,
diff --git a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6 b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
index 30bb647..6feebb8 100644
--- a/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
+++ b/services/tests/PackageManagerServiceTests/server/res/raw/install_app2_cert5_rotated_cert6
Binary files differ
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
index 435f0d7..a805e5c 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerServiceTest.java
@@ -20,6 +20,7 @@
import static com.google.common.truth.Truth.assertWithMessage;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.fail;
import static java.lang.reflect.Modifier.isFinal;
@@ -138,7 +139,7 @@
.setSecondaryCpuAbiString("secondaryCpuAbiString")
.setCpuAbiOverrideString("cpuAbiOverrideString")
.build();
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, 4, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -150,7 +151,7 @@
pri.mBroadcastUsers = null;
final int EXCLUDED_USER_ID = 4;
setting.setInstantApp(true, EXCLUDED_USER_ID);
- pri.populateUsers(new int[] {
+ pri.populateUsers(new int[]{
1, 2, 3, EXCLUDED_USER_ID, 5
}, setting);
Assert.assertNotNull(pri.mBroadcastUsers);
@@ -164,8 +165,8 @@
@Test
public void testPartitions() {
- String[] partitions = { "system", "vendor", "odm", "oem", "product", "system_ext" };
- String[] appdir = { "app", "priv-app" };
+ String[] partitions = {"system", "vendor", "odm", "oem", "product", "system_ext"};
+ String[] appdir = {"app", "priv-app"};
for (int i = 0; i < partitions.length; i++) {
final ScanPartition scanPartition =
PackageManagerService.SYSTEM_PARTITIONS.get(i);
@@ -425,10 +426,10 @@
private String displayName(Method m) {
String r = m.getName();
String p = Arrays.toString(m.getGenericParameterTypes())
- .replaceAll("([a-zA-Z0-9]+\\.)+", "")
- .replace("class ", "")
- .replaceAll("^\\[", "(")
- .replaceAll("\\]$", ")");
+ .replaceAll("([a-zA-Z0-9]+\\.)+", "")
+ .replace("class ", "")
+ .replaceAll("^\\[", "(")
+ .replaceAll("\\]$", ")");
return r + p;
}
@@ -612,4 +613,22 @@
runShellCommand("pm uninstall " + TEST_PKG_NAME);
}
}
+
+ @Test
+ public void testSetUserMinAspectRatio_samePackage_succeeds() throws Exception {
+ mIPackageManager.setUserMinAspectRatio(PACKAGE_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ // Invoking setUserMinAspectRatio on the same package shouldn't get any exception.
+ }
+
+ @Test
+ public void testSetUserMinAspectRatio_differentPackage_fails() {
+ final File testApk = new File(TEST_DATA_PATH, TEST_APP_APK);
+ runShellCommand("pm install " + testApk);
+ assertThrows(SecurityException.class, () -> {
+ mIPackageManager.setUserMinAspectRatio(TEST_PKG_NAME, UserHandle.myUserId(),
+ PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
+ });
+ runShellCommand("pm uninstall " + TEST_PKG_NAME);
+ }
}
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
index 836f858..16fb012 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -971,7 +971,7 @@
origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
- "splashScreenTheme", 1000L);
+ "splashScreenTheme", 1000L, PackageManager.USER_MIN_ASPECT_RATIO_UNSET);
final PersistableBundle appExtras1 = createPersistableBundle(
PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
final PersistableBundle launcherExtras1 = createPersistableBundle(
@@ -1638,7 +1638,8 @@
: oldUserState.getSharedLibraryOverlayPaths() == null)
&& userState.getSplashScreenTheme().equals(
oldUserState.getSplashScreenTheme())
- && userState.getUninstallReason() == oldUserState.getUninstallReason();
+ && userState.getUninstallReason() == oldUserState.getUninstallReason()
+ && userState.getMinAspectRatio() == oldUserState.getMinAspectRatio();
}
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
diff --git a/services/tests/displayservicetests/Android.bp b/services/tests/displayservicetests/Android.bp
index b242ec2..f1ff338 100644
--- a/services/tests/displayservicetests/Android.bp
+++ b/services/tests/displayservicetests/Android.bp
@@ -22,11 +22,20 @@
"src/**/*.java",
],
+ libs: [
+ "android.test.mock",
+ ],
+
static_libs: [
- "services.core",
- "androidx.test.runner",
- "androidx.test.rules",
+ "androidx.test.ext.junit",
+ "display-core-libs",
+ "frameworks-base-testutils",
+ "junit",
+ "junit-params",
+ "platform-compat-test-rules",
"platform-test-annotations",
+ "services.core",
+ "servicestests-utils",
],
defaults: [
@@ -47,3 +56,10 @@
enabled: false,
},
}
+
+java_library {
+ name: "display-core-libs",
+ srcs: [
+ "src/com/android/server/display/TestUtils.java",
+ ],
+}
diff --git a/services/tests/displayservicetests/AndroidManifest.xml b/services/tests/displayservicetests/AndroidManifest.xml
index c2e4174..d2bd10d 100644
--- a/services/tests/displayservicetests/AndroidManifest.xml
+++ b/services/tests/displayservicetests/AndroidManifest.xml
@@ -21,6 +21,16 @@
Insert permissions here. eg:
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
-->
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.CONTROL_DISPLAY_COLOR_TRANSFORMS" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.LOG_COMPAT_CHANGE" />
+ <uses-permission android:name="android.permission.MANAGE_ACTIVITY_TASKS" />
+ <uses-permission android:name="android.permission.PACKAGE_USAGE_STATS" />
+ <uses-permission android:name="android.permission.READ_COMPAT_CHANGE_CONFIG" />
+ <uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:debuggable="true"
android:testOnly="true">
diff --git a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
index 2c4fe53..7333bc7 100644
--- a/services/tests/servicestests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/AmbientBrightnessStatsTrackerTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 5f81869..ee7826f 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.display;
@@ -206,11 +206,11 @@
BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
- final int N = DISPLAY_LEVELS_BACKLIGHT.length;
+ final int n = DISPLAY_LEVELS_BACKLIGHT.length;
final float expectedBrightness =
- (float) DISPLAY_LEVELS_BACKLIGHT[N - 1] / PowerManager.BRIGHTNESS_ON;
+ (float) DISPLAY_LEVELS_BACKLIGHT[n - 1] / PowerManager.BRIGHTNESS_ON;
assertEquals(expectedBrightness,
- strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
+ strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@Test
@@ -270,10 +270,10 @@
// Check that null returns us to the default configuration.
strategy.setBrightnessConfiguration(null);
- final int N = DISPLAY_LEVELS_NITS.length;
- final float expectedBrightness = DISPLAY_LEVELS_NITS[N - 1] / DISPLAY_RANGE_NITS[1];
+ final int n = DISPLAY_LEVELS_NITS.length;
+ final float expectedBrightness = DISPLAY_LEVELS_NITS[n - 1] / DISPLAY_RANGE_NITS[1];
assertEquals(expectedBrightness,
- strategy.getBrightness(LUX_LEVELS[N - 1]), 0.0001f /*tolerance*/);
+ strategy.getBrightness(LUX_LEVELS[n - 1]), 0.0001f /*tolerance*/);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
similarity index 97%
rename from services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
index 46956d7..8faaf59 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessThrottlerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessThrottlerTest.java
@@ -159,7 +159,7 @@
@Test
public void testThermalThrottlingSingleLevel() throws Exception {
final ThrottlingLevel level = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
- 0.25f);
+ 0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(level);
@@ -184,7 +184,7 @@
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status more than high enough to trigger throttling
listener.notifyThrottling(getSkinTemp(level.thermalStatus + 1));
@@ -192,7 +192,7 @@
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to the lower throttling level
listener.notifyThrottling(getSkinTemp(level.thermalStatus));
@@ -200,7 +200,7 @@
assertEquals(level.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(level.thermalStatus - 1));
@@ -208,15 +208,15 @@
assertEquals(PowerManager.BRIGHTNESS_MAX, throttler.getBrightnessCap(), 0f);
assertFalse(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
}
@Test
public void testThermalThrottlingMultiLevel() throws Exception {
final ThrottlingLevel levelLo = new ThrottlingLevel(PowerManager.THERMAL_STATUS_MODERATE,
- 0.62f);
+ 0.62f);
final ThrottlingLevel levelHi = new ThrottlingLevel(PowerManager.THERMAL_STATUS_CRITICAL,
- 0.25f);
+ 0.25f);
List<ThrottlingLevel> levels = new ArrayList<>();
levels.add(levelLo);
@@ -242,7 +242,7 @@
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
@@ -250,7 +250,7 @@
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus));
@@ -258,7 +258,7 @@
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Set status to exceed the highest configured throttling level
listener.notifyThrottling(getSkinTemp(levelHi.thermalStatus + 1));
@@ -266,7 +266,7 @@
assertEquals(levelHi.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to an intermediate throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus + 1));
@@ -274,7 +274,7 @@
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Return to the lowest configured throttling level
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus));
@@ -282,7 +282,7 @@
assertEquals(levelLo.brightness, throttler.getBrightnessCap(), 0f);
assertTrue(throttler.isThrottled());
assertEquals(BrightnessInfo.BRIGHTNESS_MAX_REASON_THERMAL,
- throttler.getBrightnessMaxReason());
+ throttler.getBrightnessMaxReason());
// Cool down
listener.notifyThrottling(getSkinTemp(levelLo.thermalStatus - 1));
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
index 021f2d1..44c7dec 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessTrackerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/BrightnessTrackerTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.display;
@@ -395,8 +395,8 @@
final long currentTime = mInjector.currentTimeMillis();
notifyBrightnessChanged(mTracker, brightness, displayId, new float[] {1000.0f},
new long[] {TimeUnit.NANOSECONDS.toMillis(mInjector.elapsedRealtimeNanos())});
- List<BrightnessChangeEvent> eventsNoPackage
- = mTracker.getEvents(0, false).getList();
+ List<BrightnessChangeEvent> eventsNoPackage =
+ mTracker.getEvents(0, false).getList();
List<BrightnessChangeEvent> events = mTracker.getEvents(0, true).getList();
mTracker.stop();
@@ -1037,9 +1037,9 @@
}
void setBrightnessMode(boolean isBrightnessModeAutomatic) {
- mIsBrightnessModeAutomatic = isBrightnessModeAutomatic;
- mContentObserver.dispatchChange(false, null);
- waitForHandler();
+ mIsBrightnessModeAutomatic = isBrightnessModeAutomatic;
+ mContentObserver.dispatchChange(false, null);
+ waitForHandler();
}
void sendScreenChange(boolean screenOn) {
@@ -1184,8 +1184,8 @@
@Override
public int getNightDisplayColorTemperature(Context context) {
- return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
- mDefaultNightModeColorTemperature);
+ return mSecureIntSettings.getOrDefault(Settings.Secure.NIGHT_DISPLAY_COLOR_TEMPERATURE,
+ mDefaultNightModeColorTemperature);
}
@Override
diff --git a/services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java b/services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/ColorFadeTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/ColorFadeTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java b/services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/DeviceStateToLayoutMapTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/DisplayDeviceConfigTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/DisplayDeviceConfigTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
index 5db9d1f..d16c9c5 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayManagerServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/DisplayManagerServiceTest.java
@@ -142,7 +142,7 @@
private static final float FLOAT_TOLERANCE = 0.01f;
private static final String VIRTUAL_DISPLAY_NAME = "Test Virtual Display";
- private static final String PACKAGE_NAME = "com.android.frameworks.servicestests";
+ private static final String PACKAGE_NAME = "com.android.frameworks.displayservicetests";
private static final long STANDARD_DISPLAY_EVENTS = DisplayManager.EVENT_FLAG_DISPLAY_ADDED
| DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
| DisplayManager.EVENT_FLAG_DISPLAY_REMOVED;
@@ -238,7 +238,7 @@
boolean getHdrOutputConversionSupport() {
return true;
}
- }
+ }
private final DisplayManagerService.Injector mBasicInjector = new BasicInjector();
diff --git a/services/tests/servicestests/src/com/android/server/display/HbmEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/HbmEventTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/HbmEventTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/HbmEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
similarity index 89%
rename from services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index e2a66f0..76e6ec7 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -483,8 +483,10 @@
// Verify Stats HBM_ON_HDR
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 0 /*numberOfHdrLayers*/,
0, 0, 0 /*flags*/, 1.0f /*maxDesiredHdrSdrRatio*/);
@@ -492,8 +494,10 @@
// Verify Stats HBM_OFF
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX + 1);
@@ -501,16 +505,20 @@
// Verify Stats HBM_ON_SUNLIGHT
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.onAmbientLuxChange(1);
advanceTime(TIME_ALLOWED_IN_WINDOW_MILLIS / 2 + 1);
// Verify Stats HBM_OFF
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LUX_DROP));
}
@Test
@@ -527,8 +535,8 @@
// Verify Stats HBM_ON_HDR not report
verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- anyInt());
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ anyInt());
}
@Test
@@ -545,8 +553,8 @@
// Verify Stats HBM_ON_SUNLIGHT not report
verify(mInjectorMock, never()).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- anyInt());
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ anyInt());
}
// Test reporting of thermal throttling when triggered externally through
@@ -565,8 +573,10 @@
BrightnessInfo.BRIGHTNESS_MAX_REASON_NONE);
advanceTime(1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
// Brightness is thermally throttled, HBM brightness denied (NBM brightness granted)
hbmc.onBrightnessChanged(nbmBrightness, hbmBrightness,
@@ -578,8 +588,8 @@
// the HBM transition point.
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_THERMAL_LIMIT));
}
@Test
@@ -592,15 +602,17 @@
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
// Use up all the time in the window.
advanceTime(TIME_WINDOW_MILLIS + 1);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_TIME_LIMIT));
}
@Test
@@ -613,13 +625,17 @@
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_DISPLAY_OFF));
}
@Test
@@ -632,16 +648,18 @@
hbmcOnBrightnessChanged(hbmc, TRANSITION_POINT + 0.01f);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmc.getHdrListener().onHdrInfoChanged(null /*displayToken*/, 1 /*numberOfHdrLayers*/,
DISPLAY_WIDTH, DISPLAY_HEIGHT, 0 /*flags*/, 1.0f /*maxDesiredHdrSdrRatio*/);
advanceTime(0);
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_HDR),
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_HDR_PLAYING));
}
@Test
@@ -657,15 +675,17 @@
assertEquals(HIGH_BRIGHTNESS_MODE_SUNLIGHT, hbmc.getHighBrightnessMode());
// verify HBM_ON_SUNLIGHT
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__STATE__HBM_ON_SUNLIGHT),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_TRANSITION_REASON_UNKNOWN));
hbmcOnBrightnessChanged(hbmc, DEFAULT_MIN);
// verify HBM_SV_OFF due to LOW_REQUESTED_BRIGHTNESS
verify(mInjectorMock).reportHbmStateChange(eq(displayStatsId),
- eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
- eq(FrameworkStatsLog
- .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
+ eq(FrameworkStatsLog.DISPLAY_HBM_STATE_CHANGED__STATE__HBM_OFF),
+ eq(FrameworkStatsLog
+ .DISPLAY_HBM_STATE_CHANGED__REASON__HBM_SV_OFF_LOW_REQUESTED_BRIGHTNESS));
}
private void assertState(HighBrightnessModeController hbmc,
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java b/services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/HighBrightnessModeMetadataTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/LogicalDisplayMapperTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayMapperTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
index 30ff8ba..2065479 100644
--- a/services/tests/servicestests/src/com/android/server/display/LogicalDisplayTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/LogicalDisplayTest.java
@@ -40,6 +40,7 @@
import com.android.server.display.layout.Layout;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import java.io.InputStream;
@@ -121,7 +122,9 @@
assertEquals(expectedPosition, mLogicalDisplay.getDisplayPosition());
}
+ // TODO: b/288880734 - fix test after display tests migration
@Test
+ @Ignore
public void testDisplayInputFlags() {
SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
mLogicalDisplay.configureDisplayLocked(t, mDisplayDevice, false);
diff --git a/services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/NormalBrightnessModeControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
similarity index 99%
rename from services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
index 642f54c..9f91916 100644
--- a/services/tests/servicestests/src/com/android/server/display/PersistentDataStoreTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/PersistentDataStoreTest.java
@@ -11,7 +11,7 @@
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
- * limitations under the License
+ * limitations under the License.
*/
package com.android.server.display;
diff --git a/services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/ScreenOffBrightnessSensorControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java b/services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/utils/SensorUtilsTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/SensorUtilsTest.java
diff --git a/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
new file mode 100644
index 0000000..8b45145
--- /dev/null
+++ b/services/tests/displayservicetests/src/com/android/server/display/TestUtils.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.display;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.input.InputSensorInfo;
+import android.os.Parcel;
+import android.os.SystemClock;
+import android.view.DisplayAddress;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+public final class TestUtils {
+
+ public static SensorEvent createSensorEvent(Sensor sensor, int value) throws Exception {
+ final Constructor<SensorEvent> constructor =
+ SensorEvent.class.getDeclaredConstructor(int.class);
+ constructor.setAccessible(true);
+ final SensorEvent event = constructor.newInstance(1);
+ event.sensor = sensor;
+ event.values[0] = value;
+ event.timestamp = SystemClock.elapsedRealtimeNanos();
+ return event;
+ }
+
+
+ public static void setSensorType(Sensor sensor, int type, String strType) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setType", Integer.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, type);
+ if (strType != null) {
+ Field f = sensor.getClass().getDeclaredField("mStringType");
+ f.setAccessible(true);
+ f.set(sensor, strType);
+ }
+ }
+
+ public static void setMaximumRange(Sensor sensor, float maximumRange) throws Exception {
+ Method setter = Sensor.class.getDeclaredMethod("setRange", Float.TYPE, Float.TYPE);
+ setter.setAccessible(true);
+ setter.invoke(sensor, maximumRange, 1);
+ }
+
+ public static Sensor createSensor(int type, String strType) throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ return sensor;
+ }
+
+ public static Sensor createSensor(int type, String strType, float maximumRange)
+ throws Exception {
+ Constructor<Sensor> constr = Sensor.class.getDeclaredConstructor();
+ constr.setAccessible(true);
+ Sensor sensor = constr.newInstance();
+ setSensorType(sensor, type, strType);
+ setMaximumRange(sensor, maximumRange);
+ return sensor;
+ }
+
+ public static Sensor createSensor(String type, String name) {
+ return new Sensor(new InputSensorInfo(
+ name, "vendor", 0, 0, 0, 1f, 1f, 1, 1, 1, 1,
+ type, "", 0, 0, 0));
+ }
+
+ /**
+ * Create a custom {@link DisplayAddress} to ensure we're not relying on any specific
+ * display-address implementation in our code. Intentionally uses default object (reference)
+ * equality rules.
+ */
+ public static class TestDisplayAddress extends DisplayAddress {
+ @Override
+ public void writeToParcel(Parcel out, int flags) { }
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/BrightnessEventTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessEventTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/BrightnessReasonTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/BrightnessReasonTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/DisplayBrightnessStrategySelectorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/AutomaticBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/BoostBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/DozeBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/FollowerBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/OverrideBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/ScreenOffBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java b/services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/brightness/strategy/TemporaryBrightnessStrategyTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/AppSaturationControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/color/AppSaturationControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/AppSaturationControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/CctEvaluatorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/color/CctEvaluatorTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/CctEvaluatorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
index 618ab1b..c7c09b5 100644
--- a/services/tests/servicestests/src/com/android/server/display/color/ColorDisplayServiceTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/color/ColorDisplayServiceTest.java
@@ -1093,15 +1093,15 @@
@Test
public void compositionColorSpaces_invalidResources() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL,
- // Missing second color mode
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL,
+ // Missing second color mode
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- Display.COLOR_MODE_DISPLAY_P3
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ Display.COLOR_MODE_DISPLAY_P3
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1111,13 +1111,13 @@
@Test
public void compositionColorSpaces_validResources_validColorMode() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_NATURAL);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1127,13 +1127,13 @@
@Test
public void compositionColorSpaces_validResources_invalidColorMode() {
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorModes))
- .thenReturn(new int[] {
- ColorDisplayManager.COLOR_MODE_NATURAL
- });
+ .thenReturn(new int[] {
+ ColorDisplayManager.COLOR_MODE_NATURAL
+ });
when(mResourcesSpy.getIntArray(R.array.config_displayCompositionColorSpaces))
- .thenReturn(new int[] {
- Display.COLOR_MODE_SRGB,
- });
+ .thenReturn(new int[] {
+ Display.COLOR_MODE_SRGB,
+ });
setColorMode(ColorDisplayManager.COLOR_MODE_BOOSTED);
startService();
verify(mDisplayTransformManager).setColorMode(
@@ -1143,7 +1143,7 @@
@Test
public void getColorMode_noAvailableModes_returnsNotSet() {
when(mResourcesSpy.getIntArray(R.array.config_availableColorModes))
- .thenReturn(new int[] {});
+ .thenReturn(new int[] {});
startService();
verify(mDisplayTransformManager, never()).setColorMode(anyInt(), any(), anyInt());
assertThat(mBinderService.getColorMode()).isEqualTo(-1);
diff --git a/services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/DisplayWhiteBalanceTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/GlobalSaturationTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java b/services/tests/displayservicetests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/color/ReduceBrightColorsTintControllerTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/mode/DisplayModeDirectorTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/mode/SkinThermalStatusObserverTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/mode/VotesStorageTest.java b/services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/mode/VotesStorageTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/mode/VotesStorageTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java b/services/tests/displayservicetests/src/com/android/server/display/utils/AmbientFilterTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/utils/AmbientFilterTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/utils/AmbientFilterTest.java
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
rename to services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientFilterStubber.java
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
similarity index 94%
rename from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
index ac97911..f975b6f 100644
--- a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
+++ b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientLuxTest.java
@@ -268,9 +268,9 @@
controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
}
}
@@ -286,9 +286,9 @@
controller.mBrightnessFilter = spy(new AmbientFilterStubber());
for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
}
}
@@ -366,22 +366,22 @@
@Test
public void testSpline_InvalidCombinations() throws Exception {
- setBrightnesses(100.0f, 200.0f);
- setBiases(0.0f, 1.0f);
- setHighLightBrightnesses(150.0f, 250.0f);
- setHighLightBiases(0.0f, 1.0f);
+ setBrightnesses(100.0f, 200.0f);
+ setBiases(0.0f, 1.0f);
+ setHighLightBrightnesses(150.0f, 250.0f);
+ setHighLightBiases(0.0f, 1.0f);
- DisplayWhiteBalanceController controller =
- DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
- final float ambientColorTemperature = 8000.0f;
- setEstimatedColorTemperature(controller, ambientColorTemperature);
- controller.mBrightnessFilter = spy(new AmbientFilterStubber());
+ DisplayWhiteBalanceController controller =
+ DisplayWhiteBalanceFactory.create(mHandler, mSensorManagerMock, mResourcesSpy);
+ final float ambientColorTemperature = 8000.0f;
+ setEstimatedColorTemperature(controller, ambientColorTemperature);
+ controller.mBrightnessFilter = spy(new AmbientFilterStubber());
- for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
- setEstimatedBrightnessAndUpdate(controller, luxOverride);
- assertEquals(controller.mPendingAmbientColorTemperature,
- ambientColorTemperature, 0.001);
- }
+ for (float luxOverride = 0.1f; luxOverride <= 10000; luxOverride *= 10) {
+ setEstimatedBrightnessAndUpdate(controller, luxOverride);
+ assertEquals(controller.mPendingAmbientColorTemperature,
+ ambientColorTemperature, 0.001);
+ }
}
@Test
@@ -486,7 +486,7 @@
private void mockResourcesFloat(int id, float floatValue) {
doAnswer(new Answer<Void>() {
public Void answer(InvocationOnMock invocation) {
- TypedValue value = (TypedValue)invocation.getArgument(1);
+ TypedValue value = (TypedValue) invocation.getArgument(1);
value.type = TypedValue.TYPE_FLOAT;
value.data = Float.floatToIntBits(floatValue);
return null;
diff --git a/services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java b/services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
similarity index 100%
rename from services/tests/servicestests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
rename to services/tests/displayservicetests/src/com/android/server/display/whitebalance/AmbientSensorTest.java
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 32243f0..212a243 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -2221,7 +2221,7 @@
String[] packages = {mPackageName};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
}
@@ -2238,12 +2238,12 @@
doAnswer(inv -> powerState.put(inv.getArgument(0), inv.getArgument(1)))
.when(mMockPowerManager).setPowerMode(anyInt(), anyBoolean());
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
assertTrue(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
assertTrue(powerState.get(Mode.GAME));
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
@@ -2260,13 +2260,13 @@
int somePackageId = DEFAULT_PACKAGE_UID + 1;
when(mMockPackageManager.getPackagesForUid(somePackageId)).thenReturn(packages2);
gameManagerService.mUidObserver.onUidStateChanged(
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
+ somePackageId, ActivityManager.PROCESS_STATE_TOP, 0, 0);
+ gameManagerService.mUidObserver.onUidStateChanged(
DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
somePackageId, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
- gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
- gameManagerService.mUidObserver.onUidStateChanged(
- somePackageId, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, true);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
}
@@ -2277,9 +2277,9 @@
String[] packages = {mPackageName};
when(mMockPackageManager.getPackagesForUid(DEFAULT_PACKAGE_UID)).thenReturn(packages);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TOP, 0, 0);
gameManagerService.mUidObserver.onUidStateChanged(
- DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, 0);
+ DEFAULT_PACKAGE_UID, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, 0);
verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME, false);
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
index f89f73c9..aa0a2fe 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/LocalDisplayAdapterTest.java
@@ -1257,6 +1257,17 @@
public LocalDisplayAdapter.SurfaceControlProxy getSurfaceControlProxy() {
return mSurfaceControlProxy;
}
+
+ // Instead of using DisplayDeviceConfig.create(context, physicalDisplayId, isFirstDisplay)
+ // we should use DisplayDeviceConfig.create(context, isFirstDisplay) for the test to ensure
+ // that real device DisplayDeviceConfig is not loaded for FakeDisplay and we are getting
+ // consistent behaviour. Please also note that context passed to this method, is
+ // mMockContext and values will be loaded from mMockResources.
+ @Override
+ public DisplayDeviceConfig createDisplayDeviceConfig(Context context,
+ long physicalDisplayId, boolean isFirstDisplay) {
+ return DisplayDeviceConfig.create(context, isFirstDisplay);
+ }
}
private class TestListener implements DisplayAdapter.Listener {
diff --git a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
index e7b3e6f..617e4eb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -325,6 +325,83 @@
() -> mUmi.getBootUser(/* waitUntilSet= */ false));
}
+ @Test
+ public void testGetPreviousFullUserToEnterForeground() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ assertWithMessage("getPreviousFullUserToEnterForeground")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(OTHER_USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsCurrentUser() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mockCurrentUser(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip current user")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsNonFullUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags &= ~UserInfo.FLAG_FULL;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip non-full users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsPartialUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.partial = true;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip partial users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsDisabledUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUsers.get(OTHER_USER_ID).info.flags |= UserInfo.FLAG_DISABLED;
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip disabled users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
+ @Test
+ public void testGetPreviousFullUserToEnterForeground_SkipsRemovingUsers() throws Exception {
+ addUser(USER_ID);
+ setLastForegroundTime(USER_ID, 1_000_000L);
+ addUser(OTHER_USER_ID);
+ setLastForegroundTime(OTHER_USER_ID, 2_000_000L);
+
+ mUms.addRemovingUserId(OTHER_USER_ID);
+ assertWithMessage("getPreviousFullUserToEnterForeground should skip removing users")
+ .that(mUms.getPreviousFullUserToEnterForeground())
+ .isEqualTo(USER_ID);
+ }
+
private void mockCurrentUser(@UserIdInt int userId) {
mockGetLocalService(ActivityManagerInternal.class, mActivityManagerInternal);
diff --git a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
index a3a49d70..f3aa427 100644
--- a/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/utils/AlarmQueueTest.java
@@ -259,6 +259,29 @@
}
@Test
+ public void testMinTimeBetweenAlarms_freshAlarm() {
+ final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 5 * MINUTE_IN_MILLIS);
+ final long fixedTimeElapsed = mInjector.getElapsedRealtime();
+
+ InOrder inOrder = inOrder(mAlarmManager);
+
+ final String pkg1 = "com.android.test.1";
+ final String pkg2 = "com.android.test.2";
+ alarmQueue.addAlarm(pkg1, fixedTimeElapsed + MINUTE_IN_MILLIS);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setExact(
+ anyInt(), eq(fixedTimeElapsed + MINUTE_IN_MILLIS), eq(ALARM_TAG), any(), any());
+
+ advanceElapsedClock(MINUTE_IN_MILLIS);
+
+ alarmQueue.onAlarm();
+ // Minimum of 5 minutes between alarms, so the next alarm should be 5 minutes after the
+ // first.
+ alarmQueue.addAlarm(pkg2, fixedTimeElapsed + 2 * MINUTE_IN_MILLIS);
+ inOrder.verify(mAlarmManager, timeout(1000).times(1)).setExact(
+ anyInt(), eq(fixedTimeElapsed + 6 * MINUTE_IN_MILLIS), eq(ALARM_TAG), any(), any());
+ }
+
+ @Test
public void testOnAlarm() {
final AlarmQueue<String> alarmQueue = createAlarmQueue(true, 0);
final long nowElapsed = mInjector.getElapsedRealtime();
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
index e6ef044..5b5c8d4 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/FullScreenMagnificationControllerTest.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -202,6 +203,7 @@
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_0));
assertFalse(mFullScreenMagnificationController.isRegistered(DISPLAY_1));
+ // Once for each display on unregister
verify(mMockThumbnail, times(2)).hideThumbnail();
}
@@ -543,7 +545,11 @@
// The first time is triggered when the thumbnail is just created.
// The second time is triggered when the magnification region changed.
verify(mMockThumbnail, times(2)).setThumbnailBounds(
- any(), anyFloat(), anyFloat(), anyFloat());
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -681,6 +687,9 @@
checkActivatedAndMagnifying(/* activated= */ true, /* magnifying= */ true, displayId);
assertTrue(mFullScreenMagnificationController.resetIfNeeded(displayId, SERVICE_ID_2));
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, displayId);
+
+ // Once on init before it's activated and once for reset
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -783,6 +792,9 @@
mMessageCapturingHandler.sendAllMessages();
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, DISPLAY_0);
checkActivatedAndMagnifying(/* activated= */ false, /* magnifying= */ false, DISPLAY_1);
+
+ // Twice for each display: once on init before it's activated and once for screen off
+ verify(mMockThumbnail, times(4)).hideThumbnail();
}
@Test
@@ -847,6 +859,15 @@
mMessageCapturingHandler.sendAllMessages();
checkActivatedAndMagnifying(
/* activated= */ expectedActivated, /* magnifying= */ false, displayId);
+
+ if (expectedActivated) {
+ verify(mMockThumbnail, times(2)).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
+ }
}
@Test
@@ -950,6 +971,13 @@
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ eq(scale),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -984,6 +1012,13 @@
INITIAL_BOUNDS_LOWER_RIGHT_2X_CENTER, scale);
assertThat(endSpec, closeTo(getMagnificationSpec(scale, expectedOffsets)));
verify(mMockWindowManager).setMagnificationSpec(eq(displayId), argThat(closeTo(endSpec)));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ eq(scale),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -1246,6 +1281,13 @@
callbacks.onImeWindowVisibilityChanged(true);
mMessageCapturingHandler.sendAllMessages();
verify(mRequestObserver).onImeWindowVisibilityChanged(eq(DISPLAY_0), eq(true));
+
+ verify(mMockThumbnail, atLeastOnce()).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
@Test
@@ -1270,6 +1312,15 @@
mFullScreenMagnificationController.onUserContextChanged(DISPLAY_0);
verify(mRequestObserver).onFullScreenMagnificationActivationState(eq(DISPLAY_0), eq(false));
+ verify(mMockThumbnail).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
+
+ // Once on init before it's activated and once for reset
+ verify(mMockThumbnail, times(2)).hideThumbnail();
}
@Test
@@ -1281,6 +1332,12 @@
assertEquals(1.0f, mFullScreenMagnificationController.getScale(DISPLAY_0), 0);
assertTrue(mFullScreenMagnificationController.isActivated(DISPLAY_0));
+ verify(mMockThumbnail).setThumbnailBounds(
+ /* currentBounds= */ any(),
+ /* scale= */ anyFloat(),
+ /* centerX= */ anyFloat(),
+ /* centerY= */ anyFloat()
+ );
}
private void setScaleToMagnifying() {
diff --git a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
index 3baa102..8faddf8 100644
--- a/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
+++ b/services/tests/servicestests/src/com/android/server/accessibility/magnification/MagnificationThumbnailTest.java
@@ -187,6 +187,29 @@
.addView(eq(mMagnificationThumbnail.mThumbnailLayout), any());
verify(mMockWindowManager, never())
.removeView(eq(mMagnificationThumbnail.mThumbnailLayout));
+ verify(mMockWindowManager, never())
+ .updateViewLayout(eq(mMagnificationThumbnail.mThumbnailLayout), any());
+ }
+
+ @Test
+ public void whenVisible_setBoundsUpdatesLayout() throws InterruptedException {
+ runOnMainSync(() -> mMagnificationThumbnail.updateThumbnail(
+ /* scale= */ 2f,
+ /* centerX= */ 5,
+ /* centerY= */ 10
+ ));
+ runOnMainSync(() -> mMagnificationThumbnail.setThumbnailBounds(
+ new Rect(),
+ /* scale= */ 2f,
+ /* centerX= */ 5,
+ /* centerY= */ 10
+ ));
+ idle();
+
+ verify(mMockWindowManager).updateViewLayout(
+ eq(mMagnificationThumbnail.mThumbnailLayout),
+ /* params= */ any()
+ );
}
private static void idle() {
diff --git a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
index dccacb4..24a628e 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -1342,6 +1342,10 @@
Message copy = new Message();
copy.copyFrom(msg);
mMessages.add(copy);
+ if (msg.getCallback() != null) {
+ msg.getCallback().run();
+ msg.setCallback(null);
+ }
return super.sendMessageAtTime(msg, uptimeMillis);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/display/OWNERS b/services/tests/servicestests/src/com/android/server/display/OWNERS
deleted file mode 100644
index 6ce1ee4..0000000
--- a/services/tests/servicestests/src/com/android/server/display/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/display/OWNERS
diff --git a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING b/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
deleted file mode 100644
index 92d8abd..0000000
--- a/services/tests/servicestests/src/com/android/server/display/TEST_MAPPING
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "presubmit": [
- {
- "name": "FrameworksServicesTests",
- "options": [
- {"include-filter": "com.android.server.display"},
- {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
- {"exclude-annotation": "androidx.test.filters.FlakyTest"},
- {"exclude-annotation": "org.junit.Ignore"}
- ]
- }
- ]
-}
diff --git a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
index c42928e..bb8b986 100644
--- a/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/media/projection/MediaProjectionManagerServiceTest.java
@@ -38,6 +38,7 @@
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.app.ActivityManagerInternal;
@@ -49,10 +50,14 @@
import android.content.pm.PackageManager.NameNotFoundException;
import android.media.projection.IMediaProjection;
import android.media.projection.IMediaProjectionCallback;
+import android.media.projection.IMediaProjectionWatcherCallback;
import android.media.projection.ReviewGrantedConsentResult;
+import android.os.Binder;
import android.os.IBinder;
+import android.os.Looper;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.view.ContentRecordingSession;
@@ -86,6 +91,7 @@
private static final int UID = 10;
private static final String PACKAGE_NAME = "test.package";
private final ApplicationInfo mAppInfo = new ApplicationInfo();
+ private final TestLooper mTestLooper = new TestLooper();
private static final ContentRecordingSession DISPLAY_SESSION =
ContentRecordingSession.createDisplaySession(DEFAULT_DISPLAY);
// Callback registered by an app on a MediaProjection instance.
@@ -110,6 +116,14 @@
}
};
+ private final MediaProjectionManagerService.Injector mTestLooperInjector =
+ new MediaProjectionManagerService.Injector() {
+ @Override
+ Looper createCallbackLooper() {
+ return mTestLooper.getLooper();
+ }
+ };
+
private Context mContext;
private MediaProjectionManagerService mService;
private OffsettableClock mClock;
@@ -122,12 +136,15 @@
private WindowManagerInternal mWindowManagerInternal;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private IMediaProjectionWatcherCallback mWatcherCallback;
@Captor
private ArgumentCaptor<ContentRecordingSession> mSessionCaptor;
@Before
public void setup() throws Exception {
MockitoAnnotations.initMocks(this);
+ when(mWatcherCallback.asBinder()).thenReturn(new Binder());
LocalServices.removeServiceForTest(ActivityManagerInternal.class);
LocalServices.addService(ActivityManagerInternal.class, mAmInternal);
@@ -671,6 +688,59 @@
assertThat(mService.isCurrentProjection(projection)).isTrue();
}
+ @Test
+ public void setContentRecordingSession_successful_notifiesListeners()
+ throws Exception {
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+
+ verify(mWatcherCallback).onRecordingSessionSet(
+ projection.getProjectionInfo(),
+ DISPLAY_SESSION
+ );
+ }
+
+ @Test
+ public void setContentRecordingSession_notifiesListenersOnCallbackLooper()
+ throws Exception {
+ mService = new MediaProjectionManagerService(mContext, mTestLooperInjector);
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+ doReturn(true).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+ // Callback not notified yet, as test looper hasn't dispatched the message yet
+ verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
+
+ mTestLooper.dispatchAll();
+ // Message dispatched on test looper. Callback should now be notified.
+ verify(mWatcherCallback).onRecordingSessionSet(
+ projection.getProjectionInfo(),
+ DISPLAY_SESSION
+ );
+ }
+
+ @Test
+ public void setContentRecordingSession_failure_doesNotNotifyListeners()
+ throws Exception {
+ mService.addCallback(mWatcherCallback);
+ MediaProjectionManagerService.MediaProjection projection = startProjectionPreconditions();
+ projection.start(mIMediaProjectionCallback);
+
+ doReturn(false).when(mWindowManagerInternal).setContentRecordingSession(
+ any(ContentRecordingSession.class));
+ mService.setContentRecordingSession(DISPLAY_SESSION);
+
+ verify(mWatcherCallback, never()).onRecordingSessionSet(any(), any());
+ }
+
private void verifySetSessionWithContent(@ContentRecordingSession.RecordContent int content) {
verify(mWindowManagerInternal, atLeastOnce()).setContentRecordingSession(
mSessionCaptor.capture());
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
index fdf94be..39cc653 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceCreateProfileTest.java
@@ -182,7 +182,7 @@
UserInfo secondaryUser = addUser();
UserInfo profile = addProfile(secondaryUser);
// Add the profile it to the users being removed.
- mUserManagerService.addRemovingUserIdLocked(profile.id);
+ mUserManagerService.addRemovingUserId(profile.id);
// We should reuse the badge from the profile being removed.
assertEquals("Badge index not reused while removing a user", 0,
mUserManagerService.getFreeProfileBadgeLU(secondaryUser.id,
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
index 1f4c9f8..b6fd65e 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceIdRecyclingTest.java
@@ -111,7 +111,7 @@
private void removeUser(int userId) {
mUserManagerService.removeUserInfo(userId);
- mUserManagerService.addRemovingUserIdLocked(userId);
+ mUserManagerService.addRemovingUserId(userId);
}
private void assertNoNextIdAvailable(String message) {
diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
index 4af0323..592be2d 100644
--- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java
@@ -70,6 +70,9 @@
LocalServices.removeServiceForTest(UserManagerInternal.class);
mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());
+ // Put the current user to mUsers. UMS can't find userlist.xml, and fallbackToSingleUserLP.
+ mUserManagerService.putUserInfo(
+ new UserInfo(ActivityManager.getCurrentUser(), "Current User", 0));
restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
restrictionsFile.delete();
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 eaf4838..ba07439 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -4150,6 +4150,30 @@
}
@Test
+ public void testSetListenerAccessForUser_grantWithNameTooLong_throws() {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ assertThrows(IllegalArgumentException.class,
+ () -> mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ true, true));
+ }
+
+ @Test
+ public void testSetListenerAccessForUser_revokeWithNameTooLong_okay() throws Exception {
+ UserHandle user = UserHandle.of(mContext.getUserId() + 10);
+ ComponentName c = new ComponentName("com.example.package",
+ com.google.common.base.Strings.repeat("Blah", 150));
+
+ mBinderService.setNotificationListenerAccessGrantedForUser(
+ c, user.getIdentifier(), /* enabled= */ false, true);
+
+ verify(mListeners).setPackageOrComponentEnabled(
+ c.flattenToString(), user.getIdentifier(), true, /* enabled= */ false, true);
+ }
+
+ @Test
public void testSetAssistantAccessForUser() throws Exception {
UserInfo ui = new UserInfo();
ui.id = mContext.getUserId() + 10;
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 3db53eb..2e055e8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -3354,8 +3354,7 @@
// to client if the app didn't request IME visible.
assertFalse(app2.mActivityRecord.mImeInsetsFrozenUntilStartInput);
verify(app2.mClient, atLeastOnce()).resized(any(), anyBoolean(), any(),
- insetsStateCaptor.capture(), anyBoolean(), anyBoolean(), anyInt(), anyInt(),
- anyBoolean());
+ insetsStateCaptor.capture(), anyBoolean(), anyInt(), anyInt(), anyBoolean());
assertFalse(app2.getInsetsState().isSourceOrDefaultVisible(ID_IME, ime()));
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 5369b93..bdd178b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2046,6 +2046,7 @@
// Once transition starts, rotation is applied and transition shows DC rotating.
testPlayer.startTransition();
+ waitUntilHandlersIdle();
assertNotEquals(origRot, dc.getConfiguration().windowConfiguration.getRotation());
assertNotNull(testPlayer.mLastReady);
assertTrue(testPlayer.mController.isPlaying());
diff --git a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
index 28241d3..f332b69 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskTests.java
@@ -563,6 +563,36 @@
assertEquals(freeformBounds, task.getBounds());
}
+ @Test
+ public void testTopActivityEligibleForUserAspectRatioButton() {
+ DisplayContent display = mAtm.mRootWindowContainer.getDefaultDisplay();
+ final Task rootTask = new TaskBuilder(mSupervisor).setCreateActivity(true)
+ .setWindowingMode(WINDOWING_MODE_FULLSCREEN).setDisplay(display).build();
+ final Task task = rootTask.getBottomMostTask();
+ final ActivityRecord root = task.getTopNonFinishingActivity();
+ spyOn(mWm.mLetterboxConfiguration);
+
+ // When device config flag is disabled the button is not enabled
+ doReturn(false).when(mWm.mLetterboxConfiguration)
+ .isUserAppAspectRatioSettingsEnabled();
+ doReturn(false).when(mWm.mLetterboxConfiguration)
+ .isTranslucentLetterboxingEnabled();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+
+ // The flag is enabled
+ doReturn(true).when(mWm.mLetterboxConfiguration)
+ .isUserAppAspectRatioSettingsEnabled();
+ spyOn(root);
+ doReturn(task).when(root).getOrganizedTask();
+ // When the flag is enabled and the top activity is not in size compat mode.
+ doReturn(false).when(root).inSizeCompatMode();
+ assertTrue(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+
+ // When in size compat mode the button is not enabled
+ doReturn(true).when(root).inSizeCompatMode();
+ assertFalse(task.getTaskInfo().topActivityEligibleForUserAspectRatioButton);
+ }
+
/**
* Tests that a task with forced orientation has orientation-consistent bounds within the
* parent.
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
index 3f8acc6..40f86dd 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestIWindow.java
@@ -46,8 +46,7 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfig, InsetsState insetsState, boolean forceLayout,
- boolean alwaysConsumeSystemBars, int displayId, int seqId, boolean dragResizing)
- throws RemoteException {
+ int displayId, int seqId, boolean dragResizing) throws RemoteException {
}
@Override
diff --git a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
index adf3f39..bd111ad 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -233,7 +233,7 @@
}
@Override
- public void onKeyguardOccludedChangedLw(boolean occluded, boolean waitAppTransition) {
+ public void onKeyguardOccludedChangedLw(boolean occluded) {
}
public void setSafeMode(boolean safeMode) {
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
index 197ee92..64330d8 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowManagerServiceTests.java
@@ -81,6 +81,7 @@
import android.platform.test.annotations.Presubmit;
import android.util.DisplayMetrics;
import android.util.MergedConfiguration;
+import android.view.ContentRecordingSession;
import android.view.IWindow;
import android.view.IWindowSessionCallback;
import android.view.InputChannel;
@@ -101,6 +102,7 @@
import com.android.compatibility.common.util.AdoptShellPermissionsRule;
import com.android.internal.os.IResultReceiver;
+import com.android.server.LocalServices;
import org.junit.Rule;
import org.junit.Test;
@@ -761,6 +763,63 @@
}
@Test
+ public void setContentRecordingSession_sessionNull_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+
+ boolean result = wmInternal.setContentRecordingSession(/* incomingSession= */ null);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentDisplay_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
+ DEFAULT_DISPLAY);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentTask_noMatchingTask_returnsFalse() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ IBinder launchCookie = new Binder();
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(launchCookie);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isFalse();
+ }
+
+ @Test
+ public void setContentRecordingSession_sessionContentTask_matchingTask_returnsTrue() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ ActivityRecord activityRecord = createActivityRecord(createTask(mDefaultDisplay));
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(
+ activityRecord.mLaunchCookie);
+
+ boolean result = wmInternal.setContentRecordingSession(session);
+
+ assertThat(result).isTrue();
+ }
+
+ @Test
+ public void setContentRecordingSession_matchingTask_mutatesSessionWithWindowContainerToken() {
+ WindowManagerInternal wmInternal = LocalServices.getService(WindowManagerInternal.class);
+ Task task = createTask(mDefaultDisplay);
+ ActivityRecord activityRecord = createActivityRecord(task);
+ ContentRecordingSession session = ContentRecordingSession.createTaskSession(
+ activityRecord.mLaunchCookie);
+
+ wmInternal.setContentRecordingSession(session);
+
+ assertThat(session.getTokenToRecord()).isEqualTo(
+ task.mRemoteToken.toWindowContainerToken().asBinder());
+ }
+
+ @Test
public void testisLetterboxBackgroundMultiColored() {
assertThat(setupLetterboxConfigurationWithBackgroundType(
LETTERBOX_BACKGROUND_APP_COLOR_BACKGROUND_FLOATING)).isTrue();
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 0ddd31355..c13c2c3 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -778,8 +778,8 @@
doThrow(new RemoteException("test")).when(win.mClient).resized(any() /* frames */,
anyBoolean() /* reportDraw */, any() /* mergedConfig */,
any() /* insetsState */, anyBoolean() /* forceLayout */,
- anyBoolean() /* alwaysConsumeSystemBars */, anyInt() /* displayId */,
- anyInt() /* seqId */, anyBoolean() /* dragResizing */);
+ anyInt() /* displayId */, anyInt() /* seqId */,
+ anyBoolean() /* dragResizing */);
} catch (RemoteException ignored) {
}
win.reportResized();
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
index 13945a1..997015f 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger/SoundTriggerService.java
@@ -407,7 +407,9 @@
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"SoundTriggerSessionLogs for package: "
+ Objects.requireNonNull(originatorIdentity.packageName)
- + "#" + sessionId);
+ + "#" + sessionId
+ + " - " + originatorIdentity.uid
+ + "|" + originatorIdentity.pid);
return new SoundTriggerSessionStub(client,
newSoundTriggerHelper(moduleProperties, eventLogger), eventLogger);
}
@@ -428,7 +430,9 @@
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"SoundTriggerSessionLogs for package: "
+ Objects.requireNonNull(originatorIdentity.packageName) + "#"
- + sessionId);
+ + sessionId
+ + " - " + originatorIdentity.uid
+ + "|" + originatorIdentity.pid);
return new SoundTriggerSessionStub(client,
newSoundTriggerHelper(moduleProperties, eventLogger), eventLogger);
}
@@ -1801,7 +1805,9 @@
ServiceEvent.Type.ATTACH, identity.packageName + "#" + sessionId));
var eventLogger = new EventLogger(SESSION_MAX_EVENT_SIZE,
"LocalSoundTriggerEventLogger for package: " +
- identity.packageName + "#" + sessionId);
+ identity.packageName + "#" + sessionId
+ + " - " + identity.uid
+ + "|" + identity.pid);
return new SessionImpl(newSoundTriggerHelper(underlyingModule, eventLogger, isTrusted),
client, eventLogger, identity);
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
index 2f2cb59..55cbf29 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/DefaultHalFactory.java
@@ -22,7 +22,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
@@ -62,7 +62,7 @@
android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ "/default";
if (ServiceManager.isDeclared(aidlServiceName)) {
- Log.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to default soundtrigger3.ISoundTriggerHw");
return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
() -> {
// This property needs to be defined in an init.rc script and
@@ -72,7 +72,7 @@
}
// Fallback to soundtrigger-V2.x (HIDL).
- Log.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to default soundtrigger-V2.x.ISoundTriggerHw");
ISoundTriggerHw driver = ISoundTriggerHw.getService(true);
return SoundTriggerHw2Compat.create(driver, () -> {
// This property needs to be defined in an init.rc script and
@@ -81,7 +81,7 @@
}, mCaptureStateNotifier);
} else if (mockHal == USE_MOCK_HAL_V2) {
// Use V2 mock.
- Log.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to mock soundtrigger-V2.x.ISoundTriggerHw");
HwBinder.setTrebleTestingOverride(true);
try {
ISoundTriggerHw driver = ISoundTriggerHw.getService("mock", true);
@@ -89,7 +89,7 @@
try {
driver.debug(null, new ArrayList<>(Arrays.asList("reboot")));
} catch (Exception e) {
- Log.e(TAG, "Failed to reboot mock HAL", e);
+ Slog.e(TAG, "Failed to reboot mock HAL", e);
}
}, mCaptureStateNotifier);
} finally {
@@ -100,14 +100,14 @@
final String aidlServiceName =
android.hardware.soundtrigger3.ISoundTriggerHw.class.getCanonicalName()
+ "/mock";
- Log.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
+ Slog.i(TAG, "Connecting to mock soundtrigger3.ISoundTriggerHw");
return new SoundTriggerHw3Compat(ServiceManager.waitForService(aidlServiceName),
() -> {
try {
ServiceManager.waitForService(aidlServiceName).shellCommand(null,
null, null, new String[]{"reboot"}, null, null);
} catch (Exception e) {
- Log.e(TAG, "Failed to reboot mock HAL", e);
+ Slog.e(TAG, "Failed to reboot mock HAL", e);
}
});
} else {
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
index d195fbe..e3d64d4 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/ExternalCaptureStateTracker.java
@@ -17,7 +17,7 @@
package com.android.server.soundtrigger_middleware;
import android.annotation.NonNull;
-import android.util.Log;
+import android.util.Slog;
import java.util.LinkedList;
import java.util.List;
@@ -94,7 +94,7 @@
}
}
} catch (Exception e) {
- Log.e(TAG, "Exception caught while setting capture state", e);
+ Slog.e(TAG, "Exception caught while setting capture state", e);
}
}
@@ -102,7 +102,7 @@
* Called by native code when the remote service died.
*/
private void binderDied() {
- Log.w(TAG, "Audio policy service died");
+ Slog.w(TAG, "Audio policy service died");
mNeedToConnect.release();
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
index c3e0a3c..0f63347 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalEnforcer.java
@@ -27,7 +27,7 @@
import android.media.soundtrigger_middleware.RecognitionEventSys;
import android.os.DeadObjectException;
import android.os.IBinder;
-import android.util.Log;
+import android.util.Slog;
import java.util.HashMap;
import java.util.Map;
@@ -227,10 +227,10 @@
}
if (e.getCause() instanceof DeadObjectException) {
// Server is dead, no need to reboot.
- Log.e(TAG, "HAL died");
+ Slog.e(TAG, "HAL died");
throw new RecoverableException(Status.DEAD_OBJECT);
}
- Log.e(TAG, "Exception caught from HAL, rebooting HAL");
+ Slog.e(TAG, "Exception caught from HAL, rebooting HAL");
reboot();
throw e;
}
@@ -257,14 +257,14 @@
synchronized (mModelStates) {
ModelState state = mModelStates.get(model);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ Slog.wtfStack(TAG, "Unexpected recognition event for model: " + model);
reboot();
return;
}
if (event.recognitionEvent.recognitionStillActive
&& event.recognitionEvent.status != RecognitionStatus.SUCCESS
&& event.recognitionEvent.status != RecognitionStatus.FORCED) {
- Log.wtfStack(TAG,
+ Slog.wtfStack(TAG,
"recognitionStillActive is only allowed when the recognition status "
+ "is SUCCESS");
reboot();
@@ -283,14 +283,14 @@
synchronized (mModelStates) {
ModelState state = mModelStates.get(model);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected recognition event for model: " + model);
+ Slog.wtfStack(TAG, "Unexpected recognition event for model: " + model);
reboot();
return;
}
if (event.phraseRecognitionEvent.common.recognitionStillActive
&& event.phraseRecognitionEvent.common.status != RecognitionStatus.SUCCESS
&& event.phraseRecognitionEvent.common.status != RecognitionStatus.FORCED) {
- Log.wtfStack(TAG,
+ Slog.wtfStack(TAG,
"recognitionStillActive is only allowed when the recognition status "
+ "is SUCCESS");
reboot();
@@ -309,13 +309,13 @@
synchronized (mModelStates) {
ModelState state = mModelStates.get(modelHandle);
if (state == null) {
- Log.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
+ Slog.wtfStack(TAG, "Unexpected unload event for model: " + modelHandle);
reboot();
return;
}
if (state == ModelState.ACTIVE) {
- Log.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
+ Slog.wtfStack(TAG, "Trying to unload an active model: " + modelHandle);
reboot();
return;
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
index 0390f03..5e525e0 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHalWatchdog.java
@@ -23,7 +23,7 @@
import android.media.soundtrigger.RecognitionConfig;
import android.media.soundtrigger.SoundModel;
import android.os.IBinder;
-import android.util.Log;
+import android.util.Slog;
import java.util.Objects;
@@ -172,7 +172,7 @@
Watchdog() {
mTask = mTimer.createTask(() -> {
- Log.e(TAG, "HAL deadline expired. Rebooting.", mException);
+ Slog.e(TAG, "HAL deadline expired. Rebooting.", mException);
reboot();
}, TIMEOUT_MS);
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
index df2e9b4..730e92c 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerHw2Compat.java
@@ -32,7 +32,7 @@
import android.os.RemoteException;
import android.os.SystemClock;
import android.system.OsConstants;
-import android.util.Log;
+import android.util.Slog;
import java.io.IOException;
import java.util.HashMap;
@@ -240,7 +240,7 @@
try {
hidlModel.data.close();
} catch (IOException e) {
- Log.e(TAG, "Failed to close file", e);
+ Slog.e(TAG, "Failed to close file", e);
}
}
}
@@ -276,7 +276,7 @@
try {
hidlModel.common.data.close();
} catch (IOException e) {
- Log.e(TAG, "Failed to close file", e);
+ Slog.e(TAG, "Failed to close file", e);
}
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
index 3b800de..5a064da 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareImpl.java
@@ -20,7 +20,7 @@
import android.media.soundtrigger_middleware.ISoundTriggerCallback;
import android.media.soundtrigger_middleware.ISoundTriggerModule;
import android.media.soundtrigger_middleware.SoundTriggerModuleDescriptor;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.List;
@@ -85,7 +85,7 @@
try {
modules.add(new SoundTriggerModule(halFactory, audioSessionProvider));
} catch (Exception e) {
- Log.e(TAG, "Failed to add a SoundTriggerModule instance", e);
+ Slog.e(TAG, "Failed to add a SoundTriggerModule instance", e);
}
}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
index 7ec2d9f..0b9ed8c 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerMiddlewareValidation.java
@@ -36,7 +36,7 @@
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
-import android.util.Log;
+import android.util.Slog;
import android.util.SparseArray;
import com.android.internal.util.Preconditions;
@@ -150,7 +150,7 @@
e.getMessage());
}
- Log.wtf(TAG, "Unexpected exception", e);
+ Slog.wtf(TAG, "Unexpected exception", e);
throw new ServiceSpecificException(Status.INTERNAL_ERROR, e.getMessage());
}
@@ -701,7 +701,7 @@
try {
mCallback.onRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -719,7 +719,7 @@
try {
mCallback.onPhraseRecognition(modelHandle, event, captureSession);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -734,7 +734,7 @@
try {
mCallback.onModelUnloaded(modelHandle);
} catch (Exception e) {
- Log.w(TAG, "Client callback exception.", e);
+ Slog.w(TAG, "Client callback exception.", e);
}
}
@@ -746,7 +746,7 @@
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ Slog.e(TAG, "Client callback exception.", e);
}
}
@@ -761,7 +761,7 @@
} catch (RemoteException e) {
// Dead client will be handled by binderDied() - no need to handle here.
// In any case, client callbacks are considered best effort.
- Log.e(TAG, "Client callback exception.", e);
+ Slog.e(TAG, "Client callback exception.", e);
}
}
@@ -795,11 +795,11 @@
// Check if state updated unexpectedly to log race conditions.
for (Map.Entry<Integer, ModelState> entry : mLoadedModels.entrySet()) {
if (cachedMap.get(entry.getKey()) != entry.getValue().activityState) {
- Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ Slog.e(TAG, "Unexpected state update in binderDied. Race occurred!");
}
}
if (mLoadedModels.size() != cachedMap.size()) {
- Log.e(TAG, "Unexpected state update in binderDied. Race occurred!");
+ Slog.e(TAG, "Unexpected state update in binderDied. Race occurred!");
}
try {
// Detach
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index e793f31..45a7faf 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -31,7 +31,7 @@
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
-import android.util.Log;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.HashMap;
@@ -136,7 +136,7 @@
@Override
public void binderDied() {
- Log.w(TAG, "Underlying HAL driver died.");
+ Slog.w(TAG, "Underlying HAL driver died.");
List<ISoundTriggerCallback> callbacks;
synchronized (this) {
callbacks = new ArrayList<>(mActiveSessions.size());
@@ -270,7 +270,7 @@
try {
mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
} catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ Slog.e(TAG, "Failed to release session.", ee);
}
throw e;
}
@@ -286,7 +286,7 @@
checkValid();
Model loadedModel = new Model();
int result = loadedModel.load(model, audioSession);
- Log.d(TAG, String.format("loadPhraseModel()->%d", result));
+ Slog.d(TAG, String.format("loadPhraseModel()->%d", result));
return result;
} catch (Exception e) {
// We must do this outside the lock, to avoid possible deadlocks with the remote
@@ -294,7 +294,7 @@
try {
mAudioSessionProvider.releaseSession(audioSession.mSessionHandle);
} catch (Exception ee) {
- Log.e(TAG, "Failed to release session.", ee);
+ Slog.e(TAG, "Failed to release session.", ee);
}
throw e;
}
diff --git a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
index db36975..346622f 100644
--- a/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
+++ b/tests/BinaryTransparencyHostTest/src/android/transparency/test/BinaryTransparencyHostTest.java
@@ -61,8 +61,11 @@
options.setTestMethodName("testCollectAllApexInfo");
// Collect APEX package names from /apex, then pass them as expectation to be verified.
+ // The package names are collected from the find name with deduplication (NB: we used to
+ // deduplicate by dropping directory names with '@', but there's a DCLA case where it only
+ // has one directory with '@'. So we have to keep it and deduplicate the current way).
CommandResult result = getDevice().executeShellV2Command(
- "ls -d /apex/*/ |grep -v @ |grep -v /apex/sharedlibs |cut -d/ -f3");
+ "ls -d /apex/*/ |grep -v /apex/sharedlibs |cut -d/ -f3 |cut -d@ -f1 |sort |uniq");
assertTrue(result.getStatus() == CommandStatus.SUCCESS);
String[] packageNames = result.getStdout().split("\n");
for (var i = 0; i < packageNames.length; i++) {
diff --git a/tests/FlickerTests/Android.bp b/tests/FlickerTests/Android.bp
index 8faedeb..feae3b7 100644
--- a/tests/FlickerTests/Android.bp
+++ b/tests/FlickerTests/Android.bp
@@ -108,6 +108,7 @@
":FlickerTestsAppLaunch-src",
":FlickerTestsQuickswitch-src",
":FlickerTestsRotation-src",
+ ":FlickerTestsNotification-src",
],
}
diff --git a/tests/FlickerTests/AndroidTestTemplate.xml b/tests/FlickerTests/AndroidTestTemplate.xml
index 1ede943..ed63ec0 100644
--- a/tests/FlickerTests/AndroidTestTemplate.xml
+++ b/tests/FlickerTests/AndroidTestTemplate.xml
@@ -85,6 +85,8 @@
value="/data/user/0/com.android.server.wm.flicker.quickswitch/files"/>
<option name="directory-keys"
value="/data/user/0/com.android.server.wm.flicker.rotation/files"/>
+ <option name="directory-keys"
+ value="/data/user/0/com.android.server.wm.flicker.notification/files"/>
<option name="collect-on-run-ended-only" value="true"/>
<option name="clean-up" value="true"/>
</metrics_collector>
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
new file mode 100644
index 0000000..43f4ce9
--- /dev/null
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/activityembedding/open/OpenThirdActivityOverSplitTest.kt
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.activityembedding
+
+import android.platform.test.annotations.Presubmit
+import android.tools.common.datatypes.Rect
+import android.tools.common.traces.component.ComponentNameMatcher
+import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
+import android.tools.device.flicker.legacy.FlickerBuilder
+import android.tools.device.flicker.legacy.LegacyFlickerTest
+import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
+import androidx.test.filters.RequiresDevice
+import com.android.server.wm.flicker.helpers.ActivityEmbeddingAppHelper
+import org.junit.FixMethodOrder
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.MethodSorters
+import org.junit.runners.Parameterized
+
+/**
+ * Test launching a secondary activity over an existing split. By default the new secondary
+ * activity will stack over the previous secondary activity.
+ *
+ * Setup: From Activity A launch a split A|B.
+ *
+ * Transitions: Let B start C, expect C to cover B and end up in split A|C.
+ *
+ * To run this test: `atest FlickerTests:OpenThirdActivityOverSplitTest`
+ */
+@RequiresDevice
+@RunWith(Parameterized::class)
+@Parameterized.UseParametersRunnerFactory(FlickerParametersRunnerFactory::class)
+@FixMethodOrder(MethodSorters.NAME_ASCENDING)
+class OpenThirdActivityOverSplitTest (flicker: LegacyFlickerTest) :
+ ActivityEmbeddingTestBase(flicker) {
+ /** {@inheritDoc} */
+ override val transition: FlickerBuilder.() -> Unit = {
+ setup {
+ tapl.setExpectedRotationCheckEnabled(false)
+ // Launch a split.
+ testApp.launchViaIntent(wmHelper)
+ testApp.launchSecondaryActivity(wmHelper)
+
+ startDisplayBounds =
+ wmHelper.currentState.layerState.physicalDisplayBounds
+ ?: error("Can't get display bounds")
+ }
+ transitions {
+ testApp.launchThirdActivity(wmHelper)
+ }
+ teardown {
+ tapl.goHome()
+ testApp.exit(wmHelper)
+ }
+ }
+
+ /** Main activity remains visible throughout the transition. */
+ @Presubmit
+ @Test
+ fun mainActivityWindowAlwaysVisible() {
+ flicker.assertWm { isAppWindowVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT) }
+ }
+
+ /** Main activity remains visible throughout the transition and takes up half of the screen. */
+ @Presubmit
+ @Test
+ fun mainActivityLayersAlwaysVisible() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ }
+
+ flicker.assertLayersStart {
+ val display = this.entry.displays.firstOrNull { it.isOn && !it.isVirtual }
+ ?: error("No non-virtual and on display found")
+ val mainActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val secondaryActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .region
+ mainActivityRegion
+ .plus(secondaryActivityRegion)
+ .coversExactly(display.layerStackSpace)
+ }
+
+ flicker.assertLayersEnd {
+ val display = this.entry.displays.firstOrNull { it.isOn && !it.isVirtual }
+ ?: error("No non-virtual and on display found")
+ val mainActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.MAIN_ACTIVITY_COMPONENT)
+ val secondaryActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ secondaryActivityRegion.isEmpty()
+ val thirdActivityRegion =
+ this.visibleRegion(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ mainActivityRegion
+ .plus(thirdActivityRegion.region)
+ .coversExactly(display.layerStackSpace)
+ }
+ }
+
+ /** Third activity launches during the transition and covers up secondary activity. */
+ @Presubmit
+ @Test
+ fun thirdActivityWindowLaunchesIntoSplit() {
+ flicker.assertWm {
+ isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isAppWindowVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .isAppWindowInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT) // expectation
+ }
+ }
+
+ /** Third activity launches during the transition and covers up secondary activity. */
+ @Presubmit
+ @Test
+ fun thirdActivityLayerLaunchesIntoSplit() {
+ flicker.assertLayers {
+ isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isInvisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ .isVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .then()
+ .isVisible(ActivityEmbeddingAppHelper.THIRD_ACTIVITY_COMPONENT)
+ .isInvisible(ActivityEmbeddingAppHelper.SECONDARY_ACTIVITY_COMPONENT)
+ }
+ }
+
+ /** Assert the background animation layer is never visible during transition. */
+ @Presubmit
+ @Test
+ fun backgroundLayerNeverVisible() {
+ val backgroundColorLayer = ComponentNameMatcher("", "Animation Background")
+ flicker.assertLayers {
+ isInvisible(backgroundColorLayer)
+ }
+ }
+
+ companion object {
+ /** {@inheritDoc} */
+ private var startDisplayBounds = Rect.EMPTY
+ /**
+ * Creates the test configurations.
+ *
+ * See [LegacyFlickerTestFactory.nonRotationTests] for configuring screen orientation and
+ * navigation modes.
+ */
+ @Parameterized.Parameters(name = "{0}")
+ @JvmStatic
+ fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+ }
+}
\ No newline at end of file
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
index 865d5b4..dbbc771 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
index c108633..566f393 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppBackButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
index ea9710c6..ed930fc 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTest.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.close
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
index d65555a..49ed183 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/close/CloseAppHomeButtonTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.close
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
index 351eb1e..06beec1 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/ActivityEmbeddingAppHelper.kt
@@ -59,6 +59,23 @@
.waitForAndVerify()
}
+ /** Clicks the button to launch a third activity over a secondary activity. */
+ fun launchThirdActivity(wmHelper: WindowManagerStateHelper) {
+ val launchButton =
+ uiDevice.wait(
+ Until.findObject(By.res(getPackage(), "launch_third_activity_button")),
+ FIND_TIMEOUT
+ )
+ require(launchButton != null) { "Can't find launch third activity button on screen." }
+ launchButton.click()
+ wmHelper
+ .StateSyncBuilder()
+ .withActivityState(MAIN_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .withActivityState(SECONDARY_ACTIVITY_COMPONENT, PlatformConsts.STATE_STOPPED)
+ .withActivityState(THIRD_ACTIVITY_COMPONENT, PlatformConsts.STATE_RESUMED)
+ .waitForAndVerify()
+ }
+
/**
* Clicks the button to finishes the secondary activity launched through
* [launchSecondaryActivity], waits for the main activity to resume.
@@ -166,6 +183,9 @@
val SECONDARY_ACTIVITY_COMPONENT =
ActivityOptions.ActivityEmbedding.SecondaryActivity.COMPONENT.toFlickerComponent()
+ val THIRD_ACTIVITY_COMPONENT =
+ ActivityOptions.ActivityEmbedding.ThirdActivity.COMPONENT.toFlickerComponent()
+
val ALWAYS_EXPAND_ACTIVITY_COMPONENT =
ActivityOptions.ActivityEmbedding.AlwaysExpandActivity.COMPONENT.toFlickerComponent()
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
index 2563bfb..4fd4a61 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIconColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
index 33302fa..e39a578 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.Postsubmit
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
index 45fb453..6d0b6f4 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentColdTestCfArm.kt
@@ -17,7 +17,7 @@
package com.android.server.wm.flicker.launch
import android.platform.test.annotations.FlakyTest
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
index 12ee7d0..d2c3807 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTest.kt
@@ -18,7 +18,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
index 1371fd7..3e0958a 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromIntentWarmTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
index 46eb257..2a2a3db 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromLockscreenViaIntentTest.kt
@@ -21,7 +21,7 @@
import android.tools.common.NavBar
import android.tools.common.Rotation
import android.tools.common.traces.component.ComponentNameMatcher
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
index 1497e50..eec6bfd 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTest.kt
@@ -19,7 +19,7 @@
import android.platform.test.annotations.FlakyTest
import android.platform.test.annotations.Presubmit
import android.tools.common.Rotation
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.FlickerBuilder
import android.tools.device.flicker.legacy.LegacyFlickerTest
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
index 9b6c136..ab6a1ea 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenAppFromOverviewTestCfArm.kt
@@ -16,7 +16,7 @@
package com.android.server.wm.flicker.launch
-import android.tools.device.flicker.annotation.FlickerServiceCompatible
+import android.tools.common.flicker.annotation.FlickerServiceCompatible
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
import android.tools.device.flicker.legacy.LegacyFlickerTest
import android.tools.device.flicker.legacy.LegacyFlickerTestFactory
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
index 4a1bd7e..1bdb6e71 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/OpenCameraFromHomeOnDoubleClickPowerButtonTest.kt
@@ -18,6 +18,8 @@
import android.os.SystemClock
import android.platform.test.annotations.Postsubmit
+import android.tools.common.flicker.subject.layers.LayersTraceSubject
+import android.tools.common.traces.component.ComponentNameMatcher
import android.tools.device.apphelpers.CameraAppHelper
import android.tools.device.apphelpers.StandardAppHelper
import android.tools.device.flicker.junit.FlickerParametersRunnerFactory
@@ -137,8 +139,14 @@
@Postsubmit
@Test
- override fun visibleLayersShownMoreThanOneConsecutiveEntry() =
- super.visibleLayersShownMoreThanOneConsecutiveEntry()
+ override fun visibleLayersShownMoreThanOneConsecutiveEntry() {
+ flicker.assertLayers {
+ this.visibleLayersShownMoreThanOneConsecutiveEntry(
+ LayersTraceSubject.VISIBLE_FOR_MORE_THAN_ONE_ENTRY_IGNORE_LAYERS +
+ listOf(CAMERA_BACKGROUND)
+ )
+ }
+ }
@Postsubmit
@Test
@@ -161,5 +169,12 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams() = LegacyFlickerTestFactory.nonRotationTests()
+
+ private val CAMERA_BACKGROUND =
+ ComponentNameMatcher(
+ "Background for SurfaceView" +
+ "[com.google.android.GoogleCamera/" +
+ "com.google.android.apps.camera.legacy.app.activity.main.CameraActivity]"
+ )
}
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
index 6430283..7a2e74b 100644
--- a/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/AndroidManifest.xml
@@ -200,6 +200,13 @@
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
android:exported="false"/>
<activity
+ android:name=".ActivityEmbeddingThirdActivity"
+ android:label="ActivityEmbedding Third"
+ android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
+ android:theme="@style/CutoutShortEdges"
+ android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout"
+ android:exported="false"/>
+ <activity
android:name=".ActivityEmbeddingAlwaysExpandActivity"
android:label="ActivityEmbedding AlwaysExpand"
android:taskAffinity="com.android.server.wm.flicker.testapp.ActivityEmbedding"
diff --git a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
index 239aba5..6731446 100644
--- a/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
+++ b/tests/FlickerTests/test-apps/flickerapp/res/layout/activity_embedding_secondary_activity_layout.xml
@@ -27,4 +27,12 @@
android:layout_height="48dp"
android:text="Finish" />
+ <Button
+ android:id="@+id/launch_third_activity_button"
+ android:layout_width="wrap_content"
+ android:layout_height="48dp"
+ android:layout_centerHorizontal="true"
+ android:onClick="launchThirdActivity"
+ android:text="Launch a third activity" />
+
</LinearLayout>
\ No newline at end of file
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
index 6e78750..dc21027 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingSecondaryActivity.java
@@ -17,6 +17,7 @@
package com.android.server.wm.flicker.testapp;
import android.app.Activity;
+import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
@@ -40,4 +41,9 @@
}
});
}
+
+ public void launchThirdActivity(View view) {
+ startActivity(new Intent().setComponent(
+ ActivityOptions.ActivityEmbedding.ThirdActivity.COMPONENT));
+ }
}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java
new file mode 100644
index 0000000..3bd7281
--- /dev/null
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityEmbeddingThirdActivity.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm.flicker.testapp;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.os.Bundle;
+
+/**
+ * Activity to be used also as a secondary activity to split with
+ * {@link ActivityEmbeddingMainActivity}.
+ */
+public class ActivityEmbeddingThirdActivity extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_embedding_base_layout);
+ findViewById(R.id.root_activity_layout).setBackgroundColor(Color.RED);
+ }
+}
diff --git a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
index 5210618..d84ac42 100644
--- a/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
+++ b/tests/FlickerTests/test-apps/flickerapp/src/com/android/server/wm/flicker/testapp/ActivityOptions.java
@@ -99,6 +99,12 @@
FLICKER_APP_PACKAGE + ".ActivityEmbeddingSecondaryActivity");
}
+ public static class ThirdActivity {
+ public static final String LABEL = "ActivityEmbeddingThirdActivity";
+ public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,
+ FLICKER_APP_PACKAGE + ".ActivityEmbeddingThirdActivity");
+ }
+
public static class AlwaysExpandActivity {
public static final String LABEL = "ActivityEmbeddingAlwaysExpandActivity";
public static final ComponentName COMPONENT = new ComponentName(FLICKER_APP_PACKAGE,