Merge "On strongAuthChanges, update fp & face states"
diff --git a/Android.bp b/Android.bp
index cd55dcf..c0a2abb 100644
--- a/Android.bp
+++ b/Android.bp
@@ -205,7 +205,7 @@
"android.hardware.contexthub-V1.0-java",
"android.hardware.contexthub-V1.1-java",
"android.hardware.contexthub-V1.2-java",
- "android.hardware.contexthub-V1-java",
+ "android.hardware.contexthub-V2-java",
"android.hardware.gnss-V1.0-java",
"android.hardware.gnss-V2.1-java",
"android.hardware.health-V1.0-java-constants",
diff --git a/core/api/current.txt b/core/api/current.txt
index f6ec91b..f26f8399 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -1261,6 +1261,7 @@
field public static final int requireDeviceUnlock = 16843756; // 0x10103ec
field public static final int required = 16843406; // 0x101028e
field public static final int requiredAccountType = 16843734; // 0x10103d6
+ field public static final int requiredDisplayCategory;
field public static final int requiredFeature = 16844116; // 0x1010554
field public static final int requiredForAllUsers = 16843728; // 0x10103d0
field public static final int requiredNotFeature = 16844117; // 0x1010555
@@ -1508,7 +1509,6 @@
field public static final int targetCellWidth = 16844340; // 0x1010634
field public static final int targetClass = 16842799; // 0x101002f
field @Deprecated public static final int targetDescriptions = 16843680; // 0x10103a0
- field public static final int targetDisplayCategory;
field public static final int targetId = 16843740; // 0x10103dc
field public static final int targetName = 16843853; // 0x101044d
field public static final int targetPackage = 16842785; // 0x1010021
@@ -11201,10 +11201,10 @@
field public String parentActivityName;
field public String permission;
field public int persistableMode;
+ field @Nullable public String requiredDisplayCategory;
field public int screenOrientation;
field public int softInputMode;
field public String targetActivity;
- field @Nullable public String targetDisplayCategory;
field public String taskAffinity;
field public int theme;
field public int uiOptions;
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index 7d85faf..002032a 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1316,6 +1316,14 @@
}
+package android.hardware.location {
+
+ public final class ContextHubManager {
+ method @NonNull @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB) public long[] getPreloadedNanoAppIds(@NonNull android.hardware.location.ContextHubInfo);
+ }
+
+}
+
package android.hardware.soundtrigger {
public class KeyphraseEnrollmentInfo {
diff --git a/core/java/android/app/ForegroundServiceTypePolicy.java b/core/java/android/app/ForegroundServiceTypePolicy.java
index 63fdc2e..332aadd 100644
--- a/core/java/android/app/ForegroundServiceTypePolicy.java
+++ b/core/java/android/app/ForegroundServiceTypePolicy.java
@@ -58,6 +58,7 @@
import android.healthconnect.HealthConnectManager;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.permission.PermissionCheckerManager;
import android.util.ArraySet;
import android.util.SparseArray;
@@ -880,11 +881,12 @@
int checkPermission(@NonNull Context context, @NonNull String name, int callerUid,
int callerPid, String packageName, boolean allowWhileInUse) {
// Simple case, check if it's already granted.
- if (PermissionChecker.checkPermissionForPreflight(context, name,
- callerPid, callerUid, packageName) == PERMISSION_GRANTED) {
+ @PackageManager.PermissionResult int result;
+ if ((result = PermissionChecker.checkPermissionForPreflight(context, name,
+ callerPid, callerUid, packageName)) == PERMISSION_GRANTED) {
return PERMISSION_GRANTED;
}
- if (allowWhileInUse) {
+ if (allowWhileInUse && result == PermissionCheckerManager.PERMISSION_SOFT_DENIED) {
// Check its appops
final int opCode = AppOpsManager.permissionToOpCode(name);
final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class);
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index 01b42bf..568185a 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -416,7 +416,7 @@
* @param densityDpi The density of the virtual display in dpi, must be greater than 0.
* @param displayCategories The categories of the virtual display, indicating the type of
* activities allowed to run on the display. Activities can declare their type using
- * {@link android.content.pm.ActivityInfo#targetDisplayCategory}.
+ * {@link android.content.pm.ActivityInfo#requiredDisplayCategory}.
* @param surface The surface to which the content of the virtual display should
* be rendered, or null if there is none initially. The surface can also be set later using
* {@link VirtualDisplay#setSurface(Surface)}.
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 9d82274..65b3ca4 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3889,7 +3889,7 @@
"android.intent.action.USER_INITIALIZE";
/**
- * Sent when a user switch is happening, causing the process's user to be
+ * Sent after a user switch is complete, if the switch caused the process's user to be
* brought to the foreground. This is only sent to receivers registered
* through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver}. It is sent to the user that is going to the
@@ -3901,7 +3901,7 @@
"android.intent.action.USER_FOREGROUND";
/**
- * Sent when a user switch is happening, causing the process's user to be
+ * Sent after a user switch is complete, if the switch caused the process's user to be
* sent to the background. This is only sent to receivers registered
* through {@link Context#registerReceiver(BroadcastReceiver, IntentFilter)
* Context.registerReceiver}. It is sent to the user that is going to the
@@ -4042,6 +4042,11 @@
* the current state of the user.
* @hide
*/
+ /*
+ * This broadcast is sent after the user switch is complete. In case a task needs to be done
+ * while the switch is happening (i.e. while the screen is frozen to hide UI jank), please use
+ * ActivityManagerService.registerUserSwitchObserver method.
+ */
@SystemApi
public static final String ACTION_USER_SWITCHED =
"android.intent.action.USER_SWITCHED";
diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java
index fda4119..dab57fd 100644
--- a/core/java/android/content/pm/ActivityInfo.java
+++ b/core/java/android/content/pm/ActivityInfo.java
@@ -221,21 +221,20 @@
public String launchToken;
/**
- * Specifies the category of the target display the activity is expected to run on. Set from
- * the {@link android.R.attr#targetDisplayCategory} attribute. Upon creation, a virtual display
- * can specify which display categories it supports and one of the category must be present in
- * the activity's manifest to allow this activity to run. The default value is {@code null},
- * which indicates the activity does not belong to a restricted display category and thus can
- * only run on a display that didn't specify any display categories. Each activity can only
- * specify one category it targets to but a virtual display can support multiple restricted
- * categories.
- *
+ * Specifies the required display category of the activity. Set from the
+ * {@link android.R.attr#requiredDisplayCategory} attribute. Upon creation, a display can
+ * specify which display categories it supports and one of the category must be present
+ * in the {@code <activity>} element to allow this activity to run. The default value is
+ * {@code null}, which indicates the activity does not have a required display category and
+ * thus can only run on a display that didn't specify any display categories. Each activity
+ * can only specify one required category but a display can support multiple display categories.
+ * <p>
* This field should be formatted as a Java-language-style free form string(for example,
* com.google.automotive_entertainment), which may contain uppercase or lowercase letters ('A'
* through 'Z'), numbers, and underscores ('_') but may only start with letters.
*/
@Nullable
- public String targetDisplayCategory;
+ public String requiredDisplayCategory;
/**
* Activity can not be resized and always occupies the fullscreen area with all windows fully
@@ -1330,7 +1329,7 @@
mMaxAspectRatio = orig.mMaxAspectRatio;
mMinAspectRatio = orig.mMinAspectRatio;
supportsSizeChanges = orig.supportsSizeChanges;
- targetDisplayCategory = orig.targetDisplayCategory;
+ requiredDisplayCategory = orig.requiredDisplayCategory;
}
/**
@@ -1669,8 +1668,8 @@
if (mKnownActivityEmbeddingCerts != null) {
pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts);
}
- if (targetDisplayCategory != null) {
- pw.println(prefix + "targetDisplayCategory=" + targetDisplayCategory);
+ if (requiredDisplayCategory != null) {
+ pw.println(prefix + "requiredDisplayCategory=" + requiredDisplayCategory);
}
super.dumpBack(pw, prefix, dumpFlags);
}
@@ -1718,7 +1717,7 @@
dest.writeFloat(mMinAspectRatio);
dest.writeBoolean(supportsSizeChanges);
sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags);
- dest.writeString8(targetDisplayCategory);
+ dest.writeString8(requiredDisplayCategory);
}
/**
@@ -1844,7 +1843,7 @@
if (mKnownActivityEmbeddingCerts.isEmpty()) {
mKnownActivityEmbeddingCerts = null;
}
- targetDisplayCategory = source.readString8();
+ requiredDisplayCategory = source.readString8();
}
/**
diff --git a/core/java/android/hardware/location/ContextHubManager.java b/core/java/android/hardware/location/ContextHubManager.java
index b54da6c..ac23af4 100644
--- a/core/java/android/hardware/location/ContextHubManager.java
+++ b/core/java/android/hardware/location/ContextHubManager.java
@@ -24,6 +24,7 @@
import android.annotation.SuppressLint;
import android.annotation.SystemApi;
import android.annotation.SystemService;
+import android.annotation.TestApi;
import android.app.ActivityThread;
import android.app.PendingIntent;
import android.content.Context;
@@ -966,6 +967,34 @@
}
/**
+ * Queries for the list of preloaded nanoapp IDs on the system.
+ *
+ * @param hubInfo The Context Hub to query a list of nanoapp IDs from.
+ *
+ * @return The list of 64-bit IDs of the preloaded nanoapps.
+ *
+ * @throws NullPointerException if hubInfo is null
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ @NonNull public long[] getPreloadedNanoAppIds(@NonNull ContextHubInfo hubInfo) {
+ Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+ long[] nanoappIds = null;
+ try {
+ nanoappIds = mService.getPreloadedNanoAppIds(hubInfo);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ if (nanoappIds == null) {
+ nanoappIds = new long[0];
+ }
+ return nanoappIds;
+ }
+
+ /**
* Unregister a callback for receive messages from the context hub.
*
* @see Callback
diff --git a/core/java/android/hardware/location/IContextHubService.aidl b/core/java/android/hardware/location/IContextHubService.aidl
index ced75c4..490267f 100644
--- a/core/java/android/hardware/location/IContextHubService.aidl
+++ b/core/java/android/hardware/location/IContextHubService.aidl
@@ -109,4 +109,8 @@
// Queries for a list of nanoapps
@EnforcePermission("ACCESS_CONTEXT_HUB")
void queryNanoApps(int contextHubId, in IContextHubTransactionCallback transactionCallback);
+
+ // Queries for a list of preloaded nanoapps
+ @EnforcePermission("ACCESS_CONTEXT_HUB")
+ long[] getPreloadedNanoAppIds(in ContextHubInfo hubInfo);
}
diff --git a/core/java/android/nfc/AvailableNfcAntenna.java b/core/java/android/nfc/AvailableNfcAntenna.java
index 946ba67..6e6512a 100644
--- a/core/java/android/nfc/AvailableNfcAntenna.java
+++ b/core/java/android/nfc/AvailableNfcAntenna.java
@@ -27,13 +27,15 @@
*/
public final class AvailableNfcAntenna implements Parcelable {
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
private final int mLocationX;
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
private final int mLocationY;
@@ -43,16 +45,18 @@
}
/**
- * Location on the antenna on the X axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the X axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
public int getLocationX() {
return mLocationX;
}
/**
- * Location on the antenna on the Y axis in millimeters.
- * 0 is the bottom-left when the user is facing the screen.
+ * Location of the antenna on the Y axis in millimeters.
+ * 0 is the bottom-left when the user is facing the screen
+ * and the device orientation is Portrait.
*/
public int getLocationY() {
return mLocationY;
diff --git a/core/jni/android_hardware_display_DisplayViewport.cpp b/core/jni/android_hardware_display_DisplayViewport.cpp
index 03432e9..7f630cb 100644
--- a/core/jni/android_hardware_display_DisplayViewport.cpp
+++ b/core/jni/android_hardware_display_DisplayViewport.cpp
@@ -61,7 +61,8 @@
viewport->displayId = env->GetIntField(viewportObj, gDisplayViewportClassInfo.displayId);
viewport->isActive = env->GetBooleanField(viewportObj, gDisplayViewportClassInfo.isActive);
- viewport->orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+ jint orientation = env->GetIntField(viewportObj, gDisplayViewportClassInfo.orientation);
+ viewport->orientation = static_cast<ui::Rotation>(orientation);
viewport->deviceWidth = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceWidth);
viewport->deviceHeight = env->GetIntField(viewportObj, gDisplayViewportClassInfo.deviceHeight);
diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp
index 80df0ea..403c583 100644
--- a/core/jni/android_view_MotionEvent.cpp
+++ b/core/jni/android_view_MotionEvent.cpp
@@ -763,7 +763,12 @@
static jint android_view_MotionEvent_nativeGetSurfaceRotation(jlong nativePtr) {
MotionEvent* event = reinterpret_cast<MotionEvent*>(nativePtr);
- return jint(event->getSurfaceRotation());
+ auto rotation = event->getSurfaceRotation();
+ if (rotation) {
+ return static_cast<jint>(rotation.value());
+ } else {
+ return -1;
+ }
}
// ----------------------------------------------------------------------------
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index eb70344..abbff58 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -3165,20 +3165,20 @@
<attr name="canDisplayOnRemoteDevices" format="boolean"/>
<attr name="allowUntrustedActivityEmbedding" />
<attr name="knownActivityEmbeddingCerts" />
- <!-- Specifies the category of the target display the activity is expected to run on. Upon
- creation, a virtual display can specify which display categories it supports and one of
- the category must be present in the activity's manifest to allow this activity to run.
- The default value is {@code null}, which indicates the activity does not belong to a
- restricted display category and thus can only run on a display that didn't specify any
- display categories. Each activity can only specify one category it targets to but a
- virtual display can accommodate multiple restricted categories.
+ <!-- Specifies the required display category of the activity. Upon creation, a display can
+ specify which display categories it supports and one of the categories must be present
+ in the {@code <activity>} element to allow this activity to run. The default value is
+ {@code null}, which indicates the activity does not have a required display category
+ and thus can only run on a display that didn't specify any display categories. Each
+ activity can only specify one required category but a display can accommodate multiple
+ display categories.
<p> This field should be formatted as a Java-language-style free form string(for
example, com.google.automotive_entertainment), which may contain uppercase or lowercase
letters ('A' through 'Z'), numbers, and underscores ('_') but may only start with
letters.
-->
- <attr name="targetDisplayCategory" format="string"/>
+ <attr name="requiredDisplayCategory" format="string"/>
</declare-styleable>
<!-- The <code>activity-alias</code> tag declares a new
diff --git a/core/res/res/values/public-staging.xml b/core/res/res/values/public-staging.xml
index bc5878a..a9bec7a9 100644
--- a/core/res/res/values/public-staging.xml
+++ b/core/res/res/values/public-staging.xml
@@ -116,7 +116,7 @@
<public name="handwritingBoundsOffsetBottom" />
<public name="accessibilityDataPrivate" />
<public name="enableTextStylingShortcuts" />
- <public name="targetDisplayCategory"/>
+ <public name="requiredDisplayCategory"/>
<public name="maxConcurrentSessionsCount" />
</staging-public-group>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
index 17d7f5d..5376ae3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipBoundsState.java
@@ -97,6 +97,8 @@
private int mShelfHeight;
/** Whether the user has resized the PIP manually. */
private boolean mHasUserResizedPip;
+ /** Whether the user has moved the PIP manually. */
+ private boolean mHasUserMovedPip;
/**
* Areas defined by currently visible apps that they prefer to keep clear from overlays such as
* the PiP. Restricted areas may only move the PiP a limited amount from its anchor position.
@@ -279,6 +281,7 @@
if (changed) {
clearReentryState();
setHasUserResizedPip(false);
+ setHasUserMovedPip(false);
}
}
@@ -442,6 +445,16 @@
mHasUserResizedPip = hasUserResizedPip;
}
+ /** Returns whether the user has moved the PIP. */
+ public boolean hasUserMovedPip() {
+ return mHasUserMovedPip;
+ }
+
+ /** Set whether the user has moved the PIP. */
+ public void setHasUserMovedPip(boolean hasUserMovedPip) {
+ mHasUserMovedPip = hasUserMovedPip;
+ }
+
/**
* Registers a callback when the minimal size of PIP that is set by the app changes.
*/
@@ -577,6 +590,8 @@
pw.println(innerPrefix + "mImeHeight=" + mImeHeight);
pw.println(innerPrefix + "mIsShelfShowing=" + mIsShelfShowing);
pw.println(innerPrefix + "mShelfHeight=" + mShelfHeight);
+ pw.println(innerPrefix + "mHasUserMovedPip=" + mHasUserMovedPip);
+ pw.println(innerPrefix + "mHasUserResizedPip=" + mHasUserResizedPip);
if (mPipReentryState == null) {
pw.println(innerPrefix + "mPipReentryState=null");
} else {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
index 84071e0..690505e 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipKeepClearAlgorithm.java
@@ -19,6 +19,7 @@
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Rect;
+import android.os.SystemProperties;
import android.util.ArraySet;
import android.view.Gravity;
@@ -34,6 +35,10 @@
*/
public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithm {
+ private boolean mKeepClearAreaGravityEnabled =
+ SystemProperties.getBoolean(
+ "persist.wm.debug.enable_pip_keep_clear_algorithm_gravity", false);
+
protected int mKeepClearAreasPadding;
public PhonePipKeepClearAlgorithm(Context context) {
@@ -53,31 +58,36 @@
Rect startingBounds = pipBoundsState.getBounds().isEmpty()
? pipBoundsAlgorithm.getEntryDestinationBoundsIgnoringKeepClearAreas()
: pipBoundsState.getBounds();
- float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
- int verticalGravity = Gravity.BOTTOM;
- int horizontalGravity;
- if (snapFraction >= 0.5f && snapFraction < 2.5f) {
- horizontalGravity = Gravity.RIGHT;
- } else {
- horizontalGravity = Gravity.LEFT;
- }
- // push the bounds based on the gravity
Rect insets = new Rect();
pipBoundsAlgorithm.getInsetBounds(insets);
if (pipBoundsState.isImeShowing()) {
insets.bottom -= pipBoundsState.getImeHeight();
}
- Rect pushedBounds = new Rect(startingBounds);
- if (verticalGravity == Gravity.BOTTOM) {
- pushedBounds.offsetTo(pushedBounds.left,
- insets.bottom - pushedBounds.height());
+ Rect pipBounds = new Rect(startingBounds);
+
+ // move PiP towards corner if user hasn't moved it manually or the flag is on
+ if (mKeepClearAreaGravityEnabled
+ || (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip())) {
+ float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
+ int verticalGravity = Gravity.BOTTOM;
+ int horizontalGravity;
+ if (snapFraction >= 0.5f && snapFraction < 2.5f) {
+ horizontalGravity = Gravity.RIGHT;
+ } else {
+ horizontalGravity = Gravity.LEFT;
+ }
+ if (verticalGravity == Gravity.BOTTOM) {
+ pipBounds.offsetTo(pipBounds.left,
+ insets.bottom - pipBounds.height());
+ }
+ if (horizontalGravity == Gravity.RIGHT) {
+ pipBounds.offsetTo(insets.right - pipBounds.width(), pipBounds.top);
+ } else {
+ pipBounds.offsetTo(insets.left, pipBounds.top);
+ }
}
- if (horizontalGravity == Gravity.RIGHT) {
- pushedBounds.offsetTo(insets.right - pushedBounds.width(), pushedBounds.top);
- } else {
- pushedBounds.offsetTo(insets.left, pushedBounds.top);
- }
- return findUnoccludedPosition(pushedBounds, pipBoundsState.getRestrictedKeepClearAreas(),
+
+ return findUnoccludedPosition(pipBounds, pipBoundsState.getRestrictedKeepClearAreas(),
pipBoundsState.getUnrestrictedKeepClearAreas(), insets);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index a9a97be..83bc7c0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -875,6 +875,8 @@
}
if (touchState.isDragging()) {
+ mPipBoundsState.setHasUserMovedPip(true);
+
// Move the pinned stack freely
final PointF lastDelta = touchState.getLastTouchDelta();
float lastX = mStartPosition.x + mDelta.x;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
index 7ecb3f3..9215496 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/WindowDecoration.java
@@ -251,7 +251,9 @@
}
final int captionHeight = loadDimensionPixelSize(resources, params.mCaptionHeightId);
- final int captionWidth = loadDimensionPixelSize(resources, params.mCaptionWidthId);
+ final int captionWidth = params.mCaptionWidthId == Resources.ID_NULL
+ ? taskBounds.width()
+ : loadDimensionPixelSize(resources, params.mCaptionWidthId);
startT.setPosition(
mCaptionContainerSurface,
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
index 15181b1..dd9ab98 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/WindowDecorationTests.java
@@ -113,6 +113,12 @@
mMockSurfaceControlFinishT = createMockSurfaceControlTransaction();
mMockSurfaceControlAddWindowT = createMockSurfaceControlTransaction();
+ mRelayoutParams.mLayoutResId = 0;
+ mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
+ // Caption should have fixed width except in testLayoutResultCalculation_fullWidthCaption()
+ mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
+ mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
+
doReturn(mMockSurfaceControlViewHost).when(mMockSurfaceControlViewHostFactory)
.create(any(), any(), any());
}
@@ -435,6 +441,58 @@
assertThat(additionalWindow.mWindowSurface).isNull();
}
+ @Test
+ public void testLayoutResultCalculation_fullWidthCaption() {
+ final Display defaultDisplay = mock(Display.class);
+ doReturn(defaultDisplay).when(mMockDisplayController)
+ .getDisplay(Display.DEFAULT_DISPLAY);
+
+ final SurfaceControl decorContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder decorContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(decorContainerSurface);
+ mMockSurfaceControlBuilders.add(decorContainerSurfaceBuilder);
+ final SurfaceControl taskBackgroundSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder taskBackgroundSurfaceBuilder =
+ createMockSurfaceControlBuilder(taskBackgroundSurface);
+ mMockSurfaceControlBuilders.add(taskBackgroundSurfaceBuilder);
+ final SurfaceControl captionContainerSurface = mock(SurfaceControl.class);
+ final SurfaceControl.Builder captionContainerSurfaceBuilder =
+ createMockSurfaceControlBuilder(captionContainerSurface);
+ mMockSurfaceControlBuilders.add(captionContainerSurfaceBuilder);
+
+ final SurfaceControl.Transaction t = mock(SurfaceControl.Transaction.class);
+ mMockSurfaceControlTransactions.add(t);
+ final ActivityManager.TaskDescription.Builder taskDescriptionBuilder =
+ new ActivityManager.TaskDescription.Builder()
+ .setBackgroundColor(Color.YELLOW);
+ final ActivityManager.RunningTaskInfo taskInfo = new TestRunningTaskInfoBuilder()
+ .setDisplayId(Display.DEFAULT_DISPLAY)
+ .setTaskDescriptionBuilder(taskDescriptionBuilder)
+ .setBounds(TASK_BOUNDS)
+ .setPositionInParent(TASK_POSITION_IN_PARENT.x, TASK_POSITION_IN_PARENT.y)
+ .setVisible(true)
+ .build();
+ taskInfo.isFocused = true;
+ taskInfo.configuration.densityDpi = DisplayMetrics.DENSITY_DEFAULT * 2;
+ mRelayoutParams.setOutsets(
+ R.dimen.test_window_decor_left_outset,
+ R.dimen.test_window_decor_top_outset,
+ R.dimen.test_window_decor_right_outset,
+ R.dimen.test_window_decor_bottom_outset);
+ final SurfaceControl taskSurface = mock(SurfaceControl.class);
+ final TestWindowDecoration windowDecor = createWindowDecoration(taskInfo, taskSurface);
+
+ mRelayoutParams.mCaptionWidthId = Resources.ID_NULL;
+ windowDecor.relayout(taskInfo);
+
+ verify(captionContainerSurfaceBuilder).setParent(decorContainerSurface);
+ verify(captionContainerSurfaceBuilder).setContainerLayer();
+ verify(mMockSurfaceControlStartT).setPosition(captionContainerSurface, 20, 40);
+ // Width of the captionContainerSurface should match the width of TASK_BOUNDS
+ verify(mMockSurfaceControlStartT).setWindowCrop(captionContainerSurface, 300, 64);
+ verify(mMockSurfaceControlStartT).show(captionContainerSurface);
+ }
+
private TestWindowDecoration createWindowDecoration(
ActivityManager.RunningTaskInfo taskInfo, SurfaceControl testSurface) {
return new TestWindowDecoration(InstrumentationRegistry.getInstrumentation().getContext(),
@@ -490,11 +548,6 @@
@Override
void relayout(ActivityManager.RunningTaskInfo taskInfo) {
- mRelayoutParams.mLayoutResId = 0;
- mRelayoutParams.mCaptionHeightId = R.dimen.test_freeform_decor_caption_height;
- mRelayoutParams.mCaptionWidthId = R.dimen.test_freeform_decor_caption_width;
- mRelayoutParams.mShadowRadiusId = R.dimen.test_window_decor_shadow_radius;
-
relayout(mRelayoutParams, mMockSurfaceControlStartT, mMockSurfaceControlFinishT,
mMockWindowContainerTransaction, mMockView, mRelayoutResult);
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index 88cfed9..fcc0126 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -570,6 +570,7 @@
"renderthread/VulkanSurface.cpp",
"renderthread/RenderProxy.cpp",
"renderthread/RenderThread.cpp",
+ "renderthread/HintSessionWrapper.cpp",
"service/GraphicsStatsService.cpp",
"thread/CommonPool.cpp",
"utils/GLUtils.cpp",
diff --git a/libs/hwui/FrameInfo.h b/libs/hwui/FrameInfo.h
index 564ee4f..b15b6cb 100644
--- a/libs/hwui/FrameInfo.h
+++ b/libs/hwui/FrameInfo.h
@@ -104,6 +104,7 @@
set(FrameInfoIndex::AnimationStart) = vsyncTime;
set(FrameInfoIndex::PerformTraversalsStart) = vsyncTime;
set(FrameInfoIndex::DrawStart) = vsyncTime;
+ set(FrameInfoIndex::FrameStartTime) = vsyncTime;
set(FrameInfoIndex::FrameDeadline) = frameDeadline;
set(FrameInfoIndex::FrameInterval) = frameInterval;
return *this;
diff --git a/libs/hwui/hwui/BlurDrawLooper.cpp b/libs/hwui/hwui/BlurDrawLooper.cpp
index d4b0198..8b52551 100644
--- a/libs/hwui/hwui/BlurDrawLooper.cpp
+++ b/libs/hwui/hwui/BlurDrawLooper.cpp
@@ -15,6 +15,7 @@
*/
#include "BlurDrawLooper.h"
+#include <SkBlurTypes.h>
#include <SkColorSpace.h>
#include <SkMaskFilter.h>
diff --git a/libs/hwui/jni/MaskFilter.cpp b/libs/hwui/jni/MaskFilter.cpp
index 5383032..048ce02 100644
--- a/libs/hwui/jni/MaskFilter.cpp
+++ b/libs/hwui/jni/MaskFilter.cpp
@@ -2,6 +2,7 @@
#include "SkMaskFilter.h"
#include "SkBlurMask.h"
#include "SkBlurMaskFilter.h"
+#include "SkBlurTypes.h"
#include "SkTableMaskFilter.h"
static void ThrowIAE_IfNull(JNIEnv* env, void* ptr) {
diff --git a/libs/hwui/renderthread/CacheManager.cpp b/libs/hwui/renderthread/CacheManager.cpp
index 1d24e71..1c76884 100644
--- a/libs/hwui/renderthread/CacheManager.cpp
+++ b/libs/hwui/renderthread/CacheManager.cpp
@@ -98,7 +98,6 @@
auto& cache = skiapipeline::ShaderCache::get();
cache.initShaderDiskCache(identity, size);
contextOptions->fPersistentCache = &cache;
- contextOptions->fGpuPathRenderers &= ~GpuPathRenderers::kCoverageCounting;
}
void CacheManager::trimMemory(TrimLevel mode) {
diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp
index d09bc47..64839d0 100644
--- a/libs/hwui/renderthread/CanvasContext.cpp
+++ b/libs/hwui/renderthread/CanvasContext.cpp
@@ -71,16 +71,19 @@
} /* namespace */
CanvasContext* CanvasContext::create(RenderThread& thread, bool translucent,
- RenderNode* rootRenderNode, IContextFactory* contextFactory) {
+ RenderNode* rootRenderNode, IContextFactory* contextFactory,
+ int32_t uiThreadId, int32_t renderThreadId) {
auto renderType = Properties::getRenderPipelineType();
switch (renderType) {
case RenderPipelineType::SkiaGL:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaOpenGLPipeline>(thread),
+ uiThreadId, renderThreadId);
case RenderPipelineType::SkiaVulkan:
return new CanvasContext(thread, translucent, rootRenderNode, contextFactory,
- std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread));
+ std::make_unique<skiapipeline::SkiaVulkanPipeline>(thread),
+ uiThreadId, renderThreadId);
default:
LOG_ALWAYS_FATAL("canvas context type %d not supported", (int32_t)renderType);
break;
@@ -110,7 +113,8 @@
CanvasContext::CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory,
- std::unique_ptr<IRenderPipeline> renderPipeline)
+ std::unique_ptr<IRenderPipeline> renderPipeline, pid_t uiThreadId,
+ pid_t renderThreadId)
: mRenderThread(thread)
, mGenerationID(0)
, mOpaque(!translucent)
@@ -118,7 +122,8 @@
, mJankTracker(&thread.globalProfileData())
, mProfiler(mJankTracker.frames(), thread.timeLord().frameIntervalNanos())
, mContentDrawBounds(0, 0, 0, 0)
- , mRenderPipeline(std::move(renderPipeline)) {
+ , mRenderPipeline(std::move(renderPipeline))
+ , mHintSessionWrapper(uiThreadId, renderThreadId) {
mRenderThread.cacheManager().registerCanvasContext(this);
rootRenderNode->makeRoot();
mRenderNodes.emplace_back(rootRenderNode);
@@ -472,16 +477,22 @@
mRenderThread.pushBackFrameCallback(this);
}
-std::optional<nsecs_t> CanvasContext::draw() {
+void CanvasContext::draw() {
if (auto grContext = getGrContext()) {
if (grContext->abandoned()) {
LOG_ALWAYS_FATAL("GrContext is abandoned/device lost at start of CanvasContext::draw");
- return std::nullopt;
+ return;
}
}
SkRect dirty;
mDamageAccumulator.finish(&dirty);
+ // reset syncDelayDuration each time we draw
+ nsecs_t syncDelayDuration = mSyncDelayDuration;
+ nsecs_t idleDuration = mIdleDuration;
+ mSyncDelayDuration = 0;
+ mIdleDuration = 0;
+
if (!Properties::isDrawingEnabled() ||
(dirty.isEmpty() && Properties::skipEmptyFrames && !surfaceRequiresRedraw())) {
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
@@ -498,7 +509,7 @@
std::invoke(func, false /* didProduceBuffer */);
}
mFrameCommitCallbacks.clear();
- return std::nullopt;
+ return;
}
ScopedActiveContext activeContext(this);
@@ -650,10 +661,25 @@
}
}
+ int64_t intendedVsync = mCurrentFrameInfo->get(FrameInfoIndex::IntendedVsync);
+ int64_t frameDeadline = mCurrentFrameInfo->get(FrameInfoIndex::FrameDeadline);
+ int64_t dequeueBufferDuration = mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration);
+
+ mHintSessionWrapper.updateTargetWorkDuration(frameDeadline - intendedVsync);
+
+ if (didDraw) {
+ int64_t frameStartTime = mCurrentFrameInfo->get(FrameInfoIndex::FrameStartTime);
+ int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
+ int64_t actualDuration = frameDuration -
+ (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
+ dequeueBufferDuration - idleDuration;
+ mHintSessionWrapper.reportActualWorkDuration(actualDuration);
+ }
+
+ mLastDequeueBufferDuration = dequeueBufferDuration;
+
mRenderThread.cacheManager().onFrameCompleted();
- return didDraw ? std::make_optional(
- mCurrentFrameInfo->get(FrameInfoIndex::DequeueBufferDuration))
- : std::nullopt;
+ return;
}
void CanvasContext::reportMetricsWithPresentTime() {
@@ -766,6 +792,8 @@
// Called by choreographer to do an RT-driven animation
void CanvasContext::doFrame() {
if (!mRenderPipeline->isSurfaceReady()) return;
+ mIdleDuration =
+ systemTime(SYSTEM_TIME_MONOTONIC) - mRenderThread.timeLord().computeFrameTimeNanos();
prepareAndDraw(nullptr);
}
@@ -974,6 +1002,14 @@
}
}
+void CanvasContext::sendLoadResetHint() {
+ mHintSessionWrapper.sendLoadResetHint();
+}
+
+void CanvasContext::setSyncDelayDuration(nsecs_t duration) {
+ mSyncDelayDuration = duration;
+}
+
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h
index db96cfb..e875c42 100644
--- a/libs/hwui/renderthread/CanvasContext.h
+++ b/libs/hwui/renderthread/CanvasContext.h
@@ -16,22 +16,6 @@
#pragma once
-#include "DamageAccumulator.h"
-#include "FrameInfo.h"
-#include "FrameInfoVisualizer.h"
-#include "FrameMetricsReporter.h"
-#include "IContextFactory.h"
-#include "IRenderPipeline.h"
-#include "JankTracker.h"
-#include "LayerUpdateQueue.h"
-#include "Lighting.h"
-#include "ReliableSurface.h"
-#include "RenderNode.h"
-#include "renderthread/RenderTask.h"
-#include "renderthread/RenderThread.h"
-#include "utils/RingBuffer.h"
-#include "ColorMode.h"
-
#include <SkBitmap.h>
#include <SkRect.h>
#include <SkSize.h>
@@ -46,6 +30,23 @@
#include <utility>
#include <vector>
+#include "ColorMode.h"
+#include "DamageAccumulator.h"
+#include "FrameInfo.h"
+#include "FrameInfoVisualizer.h"
+#include "FrameMetricsReporter.h"
+#include "HintSessionWrapper.h"
+#include "IContextFactory.h"
+#include "IRenderPipeline.h"
+#include "JankTracker.h"
+#include "LayerUpdateQueue.h"
+#include "Lighting.h"
+#include "ReliableSurface.h"
+#include "RenderNode.h"
+#include "renderthread/RenderTask.h"
+#include "renderthread/RenderThread.h"
+#include "utils/RingBuffer.h"
+
namespace android {
namespace uirenderer {
@@ -66,7 +67,8 @@
class CanvasContext : public IFrameCallback {
public:
static CanvasContext* create(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory);
+ IContextFactory* contextFactory, pid_t uiThreadId,
+ pid_t renderThreadId);
virtual ~CanvasContext();
/**
@@ -138,7 +140,7 @@
bool makeCurrent();
void prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t syncQueued, RenderNode* target);
// Returns the DequeueBufferDuration.
- std::optional<nsecs_t> draw();
+ void draw();
void destroy();
// IFrameCallback, Choreographer-driven frame callback entry point
@@ -214,9 +216,14 @@
static CanvasContext* getActiveContext();
+ void sendLoadResetHint();
+
+ void setSyncDelayDuration(nsecs_t duration);
+
private:
CanvasContext(RenderThread& thread, bool translucent, RenderNode* rootRenderNode,
- IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline);
+ IContextFactory* contextFactory, std::unique_ptr<IRenderPipeline> renderPipeline,
+ pid_t uiThreadId, pid_t renderThreadId);
friend class RegisterFrameCallbackTask;
// TODO: Replace with something better for layer & other GL object
@@ -330,6 +337,11 @@
std::function<bool(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback;
std::function<void()> mPrepareSurfaceControlForWebviewCallback;
+
+ HintSessionWrapper mHintSessionWrapper;
+ nsecs_t mLastDequeueBufferDuration = 0;
+ nsecs_t mSyncDelayDuration = 0;
+ nsecs_t mIdleDuration = 0;
};
} /* namespace renderthread */
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index cb30614..1cc82fd 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -16,7 +16,6 @@
#include "DrawFrameTask.h"
-#include <dlfcn.h>
#include <gui/TraceUtils.h>
#include <utils/Log.h>
@@ -28,70 +27,11 @@
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "RenderThread.h"
-#include "thread/CommonPool.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
namespace renderthread {
-namespace {
-
-typedef APerformanceHintManager* (*APH_getManager)();
-typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
- size_t, int64_t);
-typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
-typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
-typedef void (*APH_closeSession)(APerformanceHintSession* session);
-
-bool gAPerformanceHintBindingInitialized = false;
-APH_getManager gAPH_getManagerFn = nullptr;
-APH_createSession gAPH_createSessionFn = nullptr;
-APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
-APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
-APH_sendHint gAPH_sendHintFn = nullptr;
-APH_closeSession gAPH_closeSessionFn = nullptr;
-
-void ensureAPerformanceHintBindingInitialized() {
- if (gAPerformanceHintBindingInitialized) return;
-
- void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
- LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
-
- gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
- LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
- "Failed to find required symbol APerformanceHint_getManager!");
-
- gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
- LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_createSession!");
-
- gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
- handle_, "APerformanceHint_updateTargetWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_updateTargetWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
-
- gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
- handle_, "APerformanceHint_reportActualWorkDuration");
- LOG_ALWAYS_FATAL_IF(
- gAPH_reportActualWorkDurationFn == nullptr,
- "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
-
- gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
- LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
- "Failed to find required symbol APerformanceHint_sendHint!");
-
- gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
- LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
- "Failed to find required symbol APerformanceHint_closeSession!");
-
- gAPerformanceHintBindingInitialized = true;
-}
-
-} // namespace
-
DrawFrameTask::DrawFrameTask()
: mRenderThread(nullptr)
, mContext(nullptr)
@@ -100,13 +40,11 @@
DrawFrameTask::~DrawFrameTask() {}
-void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId) {
+void DrawFrameTask::setContext(RenderThread* thread, CanvasContext* context,
+ RenderNode* targetNode) {
mRenderThread = thread;
mContext = context;
mTargetNode = targetNode;
- mUiThreadId = uiThreadId;
- mRenderThreadId = renderThreadId;
}
void DrawFrameTask::pushLayerUpdate(DeferredLayerUpdater* layer) {
@@ -150,11 +88,11 @@
void DrawFrameTask::run() {
const int64_t vsyncId = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameTimelineVsyncId)];
ATRACE_FORMAT("DrawFrames %" PRId64, vsyncId);
- nsecs_t syncDelayDuration = systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued;
+
+ mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
bool canUnblockUiThread;
bool canDrawThisFrame;
- bool didDraw = false;
{
TreeInfo info(TreeInfo::MODE_FULL, *mContext);
info.forceDrawFrame = mForceDrawFrame;
@@ -175,9 +113,6 @@
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
- int64_t intendedVsync = mFrameInfo[static_cast<int>(FrameInfoIndex::IntendedVsync)];
- int64_t frameDeadline = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameDeadline)];
- int64_t frameStartTime = mFrameInfo[static_cast<int>(FrameInfoIndex::FrameStartTime)];
// From this point on anything in "this" is *UNSAFE TO ACCESS*
if (canUnblockUiThread) {
@@ -188,18 +123,15 @@
if (CC_UNLIKELY(frameCallback)) {
context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
frameNr = context->getFrameNumber()]() {
- auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ auto frameCommitCallback = frameCallback(syncResult, frameNr);
if (frameCommitCallback) {
context->addFrameCommitListener(std::move(frameCommitCallback));
}
});
}
- nsecs_t dequeueBufferDuration = 0;
if (CC_LIKELY(canDrawThisFrame)) {
- std::optional<nsecs_t> drawResult = context->draw();
- didDraw = drawResult.has_value();
- dequeueBufferDuration = drawResult.value_or(0);
+ context->draw();
} else {
// Do a flush in case syncFrameState performed any texture uploads. Since we skipped
// the draw() call, those uploads (or deletes) will end up sitting in the queue.
@@ -218,41 +150,6 @@
if (!canUnblockUiThread) {
unblockUiThread();
}
-
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
-
- constexpr int64_t kSanityCheckLowerBound = 100_us;
- constexpr int64_t kSanityCheckUpperBound = 10_s;
- int64_t targetWorkDuration = frameDeadline - intendedVsync;
- targetWorkDuration = targetWorkDuration * Properties::targetCpuTimePercentage / 100;
- if (targetWorkDuration > kSanityCheckLowerBound &&
- targetWorkDuration < kSanityCheckUpperBound &&
- targetWorkDuration != mLastTargetWorkDuration) {
- mLastTargetWorkDuration = targetWorkDuration;
- mHintSessionWrapper->updateTargetWorkDuration(targetWorkDuration);
- }
-
- if (didDraw) {
- int64_t frameDuration = systemTime(SYSTEM_TIME_MONOTONIC) - frameStartTime;
- int64_t actualDuration = frameDuration -
- (std::min(syncDelayDuration, mLastDequeueBufferDuration)) -
- dequeueBufferDuration;
- if (actualDuration > kSanityCheckLowerBound && actualDuration < kSanityCheckUpperBound) {
- mHintSessionWrapper->reportActualWorkDuration(actualDuration);
- }
- }
-
- mLastDequeueBufferDuration = dequeueBufferDuration;
-}
-
-void DrawFrameTask::sendLoadResetHint() {
- if (!(Properties::useHintManager && Properties::isDrawingEnabled())) return;
- if (!mHintSessionWrapper) mHintSessionWrapper.emplace(mUiThreadId, mRenderThreadId);
- nsecs_t now = systemTime();
- if (now - mLastFrameNotification > kResetHintTimeout) {
- mHintSessionWrapper->sendHint(SessionHint::CPU_LOAD_RESET);
- }
- mLastFrameNotification = now;
}
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
@@ -305,50 +202,6 @@
mSignal.signal();
}
-DrawFrameTask::HintSessionWrapper::HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId) {
- if (!Properties::useHintManager) return;
- if (uiThreadId < 0 || renderThreadId < 0) return;
-
- ensureAPerformanceHintBindingInitialized();
-
- APerformanceHintManager* manager = gAPH_getManagerFn();
- if (!manager) return;
-
- std::vector<int32_t> tids = CommonPool::getThreadIds();
- tids.push_back(uiThreadId);
- tids.push_back(renderThreadId);
-
- // DrawFrameTask code will always set a target duration before reporting actual durations.
- // So this is just a placeholder value that's never used.
- int64_t dummyTargetDurationNanos = 16666667;
- mHintSession =
- gAPH_createSessionFn(manager, tids.data(), tids.size(), dummyTargetDurationNanos);
-}
-
-DrawFrameTask::HintSessionWrapper::~HintSessionWrapper() {
- if (mHintSession) {
- gAPH_closeSessionFn(mHintSession);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::updateTargetWorkDuration(long targetDurationNanos) {
- if (mHintSession) {
- gAPH_updateTargetWorkDurationFn(mHintSession, targetDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
- if (mHintSession) {
- gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
- }
-}
-
-void DrawFrameTask::HintSessionWrapper::sendHint(SessionHint hint) {
- if (mHintSession && Properties::isDrawingEnabled()) {
- gAPH_sendHintFn(mHintSession, static_cast<int>(hint));
- }
-}
-
} /* namespace renderthread */
} /* namespace uirenderer */
} /* namespace android */
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index 7eae41c..fafab24 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -16,7 +16,6 @@
#ifndef DRAWFRAMETASK_H
#define DRAWFRAMETASK_H
-#include <android/performance_hint.h>
#include <utils/Condition.h>
#include <utils/Mutex.h>
#include <utils/StrongPointer.h>
@@ -28,7 +27,6 @@
#include "../Rect.h"
#include "../TreeInfo.h"
#include "RenderTask.h"
-#include "utils/TimeUtils.h"
namespace android {
namespace uirenderer {
@@ -62,8 +60,7 @@
DrawFrameTask();
virtual ~DrawFrameTask();
- void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode,
- int32_t uiThreadId, int32_t renderThreadId);
+ void setContext(RenderThread* thread, CanvasContext* context, RenderNode* targetNode);
void setContentDrawBounds(int left, int top, int right, int bottom) {
mContentDrawBounds.set(left, top, right, bottom);
}
@@ -91,22 +88,7 @@
void forceDrawNextFrame() { mForceDrawFrame = true; }
- void sendLoadResetHint();
-
private:
- class HintSessionWrapper {
- public:
- HintSessionWrapper(int32_t uiThreadId, int32_t renderThreadId);
- ~HintSessionWrapper();
-
- void updateTargetWorkDuration(long targetDurationNanos);
- void reportActualWorkDuration(long actualDurationNanos);
- void sendHint(SessionHint hint);
-
- private:
- APerformanceHintSession* mHintSession = nullptr;
- };
-
void postAndWait();
bool syncFrameState(TreeInfo& info);
void unblockUiThread();
@@ -117,8 +99,6 @@
RenderThread* mRenderThread;
CanvasContext* mContext;
RenderNode* mTargetNode = nullptr;
- int32_t mUiThreadId = -1;
- int32_t mRenderThreadId = -1;
Rect mContentDrawBounds;
/*********************************************
@@ -135,13 +115,6 @@
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
- nsecs_t mLastDequeueBufferDuration = 0;
- nsecs_t mLastTargetWorkDuration = 0;
- std::optional<HintSessionWrapper> mHintSessionWrapper;
-
- nsecs_t mLastFrameNotification = 0;
- nsecs_t kResetHintTimeout = 100_ms;
-
bool mForceDrawFrame = false;
};
diff --git a/libs/hwui/renderthread/HintSessionWrapper.cpp b/libs/hwui/renderthread/HintSessionWrapper.cpp
new file mode 100644
index 0000000..edacef0
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "HintSessionWrapper.h"
+
+#include <dlfcn.h>
+#include <utils/Log.h>
+
+#include <vector>
+
+#include "../Properties.h"
+#include "thread/CommonPool.h"
+
+namespace android {
+namespace uirenderer {
+namespace renderthread {
+
+namespace {
+
+typedef APerformanceHintManager* (*APH_getManager)();
+typedef APerformanceHintSession* (*APH_createSession)(APerformanceHintManager*, const int32_t*,
+ size_t, int64_t);
+typedef void (*APH_closeSession)(APerformanceHintSession* session);
+typedef void (*APH_updateTargetWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_reportActualWorkDuration)(APerformanceHintSession*, int64_t);
+typedef void (*APH_sendHint)(APerformanceHintSession* session, int32_t);
+
+bool gAPerformanceHintBindingInitialized = false;
+APH_getManager gAPH_getManagerFn = nullptr;
+APH_createSession gAPH_createSessionFn = nullptr;
+APH_closeSession gAPH_closeSessionFn = nullptr;
+APH_updateTargetWorkDuration gAPH_updateTargetWorkDurationFn = nullptr;
+APH_reportActualWorkDuration gAPH_reportActualWorkDurationFn = nullptr;
+APH_sendHint gAPH_sendHintFn = nullptr;
+
+void ensureAPerformanceHintBindingInitialized() {
+ if (gAPerformanceHintBindingInitialized) return;
+
+ void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE);
+ LOG_ALWAYS_FATAL_IF(handle_ == nullptr, "Failed to dlopen libandroid.so!");
+
+ gAPH_getManagerFn = (APH_getManager)dlsym(handle_, "APerformanceHint_getManager");
+ LOG_ALWAYS_FATAL_IF(gAPH_getManagerFn == nullptr,
+ "Failed to find required symbol APerformanceHint_getManager!");
+
+ gAPH_createSessionFn = (APH_createSession)dlsym(handle_, "APerformanceHint_createSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_createSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_createSession!");
+
+ gAPH_closeSessionFn = (APH_closeSession)dlsym(handle_, "APerformanceHint_closeSession");
+ LOG_ALWAYS_FATAL_IF(gAPH_closeSessionFn == nullptr,
+ "Failed to find required symbol APerformanceHint_closeSession!");
+
+ gAPH_updateTargetWorkDurationFn = (APH_updateTargetWorkDuration)dlsym(
+ handle_, "APerformanceHint_updateTargetWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_updateTargetWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_updateTargetWorkDuration!");
+
+ gAPH_reportActualWorkDurationFn = (APH_reportActualWorkDuration)dlsym(
+ handle_, "APerformanceHint_reportActualWorkDuration");
+ LOG_ALWAYS_FATAL_IF(
+ gAPH_reportActualWorkDurationFn == nullptr,
+ "Failed to find required symbol APerformanceHint_reportActualWorkDuration!");
+
+ gAPH_sendHintFn = (APH_sendHint)dlsym(handle_, "APerformanceHint_sendHint");
+ LOG_ALWAYS_FATAL_IF(gAPH_sendHintFn == nullptr,
+ "Failed to find required symbol APerformanceHint_sendHint!");
+
+ gAPerformanceHintBindingInitialized = true;
+}
+
+} // namespace
+
+HintSessionWrapper::HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId)
+ : mUiThreadId(uiThreadId), mRenderThreadId(renderThreadId) {}
+
+HintSessionWrapper::~HintSessionWrapper() {
+ if (mHintSession) {
+ gAPH_closeSessionFn(mHintSession);
+ }
+}
+
+bool HintSessionWrapper::useHintSession() {
+ if (!Properties::useHintManager || !Properties::isDrawingEnabled()) return false;
+ if (mHintSession) return true;
+ // If session does not exist, create it;
+ // this defers session creation until we try to actually use it.
+ if (!mSessionValid) return false;
+ return init();
+}
+
+bool HintSessionWrapper::init() {
+ if (mUiThreadId < 0 || mRenderThreadId < 0) return false;
+
+ // Assume that if we return before the end, it broke
+ mSessionValid = false;
+
+ ensureAPerformanceHintBindingInitialized();
+
+ APerformanceHintManager* manager = gAPH_getManagerFn();
+ if (!manager) return false;
+
+ std::vector<pid_t> tids = CommonPool::getThreadIds();
+ tids.push_back(mUiThreadId);
+ tids.push_back(mRenderThreadId);
+
+ // Use a placeholder target value to initialize,
+ // this will always be replaced elsewhere before it gets used
+ int64_t defaultTargetDurationNanos = 16666667;
+ mHintSession =
+ gAPH_createSessionFn(manager, tids.data(), tids.size(), defaultTargetDurationNanos);
+
+ mSessionValid = !!mHintSession;
+ return mSessionValid;
+}
+
+void HintSessionWrapper::updateTargetWorkDuration(long targetWorkDurationNanos) {
+ if (!useHintSession()) return;
+ targetWorkDurationNanos = targetWorkDurationNanos * Properties::targetCpuTimePercentage / 100;
+ if (targetWorkDurationNanos != mLastTargetWorkDuration &&
+ targetWorkDurationNanos > kSanityCheckLowerBound &&
+ targetWorkDurationNanos < kSanityCheckUpperBound) {
+ mLastTargetWorkDuration = targetWorkDurationNanos;
+ gAPH_updateTargetWorkDurationFn(mHintSession, targetWorkDurationNanos);
+ }
+ mLastFrameNotification = systemTime();
+}
+
+void HintSessionWrapper::reportActualWorkDuration(long actualDurationNanos) {
+ if (!useHintSession()) return;
+ if (actualDurationNanos > kSanityCheckLowerBound &&
+ actualDurationNanos < kSanityCheckUpperBound) {
+ gAPH_reportActualWorkDurationFn(mHintSession, actualDurationNanos);
+ }
+}
+
+void HintSessionWrapper::sendLoadResetHint() {
+ if (!useHintSession()) return;
+ nsecs_t now = systemTime();
+ if (now - mLastFrameNotification > kResetHintTimeout) {
+ gAPH_sendHintFn(mHintSession, static_cast<int>(SessionHint::CPU_LOAD_RESET));
+ }
+ mLastFrameNotification = now;
+}
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/HintSessionWrapper.h b/libs/hwui/renderthread/HintSessionWrapper.h
new file mode 100644
index 0000000..fcbc101
--- /dev/null
+++ b/libs/hwui/renderthread/HintSessionWrapper.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <android/performance_hint.h>
+
+#include "utils/TimeUtils.h"
+
+namespace android {
+namespace uirenderer {
+
+namespace renderthread {
+
+class HintSessionWrapper {
+public:
+ HintSessionWrapper(pid_t uiThreadId, pid_t renderThreadId);
+ ~HintSessionWrapper();
+
+ void updateTargetWorkDuration(long targetDurationNanos);
+ void reportActualWorkDuration(long actualDurationNanos);
+ void sendLoadResetHint();
+
+private:
+ bool useHintSession();
+ bool init();
+ APerformanceHintSession* mHintSession = nullptr;
+
+ nsecs_t mLastFrameNotification = 0;
+ nsecs_t mLastTargetWorkDuration = 0;
+
+ pid_t mUiThreadId;
+ pid_t mRenderThreadId;
+
+ bool mSessionValid = true;
+
+ static constexpr nsecs_t kResetHintTimeout = 100_ms;
+ static constexpr int64_t kSanityCheckLowerBound = 100_us;
+ static constexpr int64_t kSanityCheckUpperBound = 10_s;
+};
+
+} /* namespace renderthread */
+} /* namespace uirenderer */
+} /* namespace android */
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 03a2bc9..07f5a78 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -42,11 +42,13 @@
RenderProxy::RenderProxy(bool translucent, RenderNode* rootRenderNode,
IContextFactory* contextFactory)
: mRenderThread(RenderThread::getInstance()), mContext(nullptr) {
- mContext = mRenderThread.queue().runSync([&]() -> CanvasContext* {
- return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory);
+ pid_t uiThreadId = pthread_gettid_np(pthread_self());
+ pid_t renderThreadId = getRenderThreadTid();
+ mContext = mRenderThread.queue().runSync([=, this]() -> CanvasContext* {
+ return CanvasContext::create(mRenderThread, translucent, rootRenderNode, contextFactory,
+ uiThreadId, renderThreadId);
});
- mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode,
- pthread_gettid_np(pthread_self()), getRenderThreadTid());
+ mDrawFrameTask.setContext(&mRenderThread, mContext, rootRenderNode);
}
RenderProxy::~RenderProxy() {
@@ -55,7 +57,7 @@
void RenderProxy::destroyContext() {
if (mContext) {
- mDrawFrameTask.setContext(nullptr, nullptr, nullptr, -1, -1);
+ mDrawFrameTask.setContext(nullptr, nullptr, nullptr);
// This is also a fence as we need to be certain that there are no
// outstanding mDrawFrame tasks posted before it is destroyed
mRenderThread.queue().runSync([this]() { delete mContext; });
@@ -237,7 +239,7 @@
}
void RenderProxy::notifyCallbackPending() {
- mDrawFrameTask.sendLoadResetHint();
+ mRenderThread.queue().post([this]() { mContext->sendLoadResetHint(); });
}
void RenderProxy::dumpProfileInfo(int fd, int dumpFlags) {
diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp
index 1771c35..88420a5 100644
--- a/libs/hwui/tests/unit/CanvasContextTests.cpp
+++ b/libs/hwui/tests/unit/CanvasContextTests.cpp
@@ -36,7 +36,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
ASSERT_FALSE(canvasContext->hasSurface());
diff --git a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
index 3caba2d..596bd37 100644
--- a/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeDrawableTests.cpp
@@ -335,7 +335,7 @@
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -399,7 +399,7 @@
"A");
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -519,7 +519,7 @@
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -619,7 +619,7 @@
// prepareTree is required to find, which receivers have backward projected nodes
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, parent.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, parent.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -635,7 +635,7 @@
static int drawNode(RenderThread& renderThread, const sp<RenderNode>& renderNode) {
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, renderNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
diff --git a/libs/hwui/tests/unit/RenderNodeTests.cpp b/libs/hwui/tests/unit/RenderNodeTests.cpp
index 61bd646..80796f4 100644
--- a/libs/hwui/tests/unit/RenderNodeTests.cpp
+++ b/libs/hwui/tests/unit/RenderNodeTests.cpp
@@ -274,7 +274,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -310,7 +310,7 @@
});
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
canvasContext->setSurface(nullptr);
TreeInfo info(TreeInfo::MODE_RT_ONLY, *canvasContext.get());
DamageAccumulator damageAccumulator;
diff --git a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
index 3d5aca4..f825d7c 100644
--- a/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
+++ b/libs/hwui/tests/unit/SkiaDisplayListTests.cpp
@@ -142,7 +142,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
TreeInfo info(TreeInfo::MODE_FULL, *canvasContext.get());
DamageAccumulator damageAccumulator;
info.damageAccumulator = &damageAccumulator;
@@ -201,7 +201,7 @@
auto rootNode = TestUtils::createNode(0, 0, 200, 400, nullptr);
ContextFactory contextFactory;
std::unique_ptr<CanvasContext> canvasContext(
- CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory));
+ CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0));
// Set up a Surface so that we can position the VectorDrawable offscreen.
test::TestContext testContext;
diff --git a/libs/input/MouseCursorController.cpp b/libs/input/MouseCursorController.cpp
index 0e7b7ff..a835167 100644
--- a/libs/input/MouseCursorController.cpp
+++ b/libs/input/MouseCursorController.cpp
@@ -199,8 +199,7 @@
width = viewport.deviceWidth;
height = viewport.deviceHeight;
- if (viewport.orientation == DISPLAY_ORIENTATION_90 ||
- viewport.orientation == DISPLAY_ORIENTATION_270) {
+ if (viewport.orientation == ui::ROTATION_90 || viewport.orientation == ui::ROTATION_270) {
std::swap(width, height);
}
}
@@ -244,38 +243,42 @@
// Undo the previous rotation.
switch (oldViewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = oldViewport.deviceHeight - y;
y = temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = oldViewport.deviceWidth - x;
y = oldViewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = y;
y = oldViewport.deviceWidth - temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Perform the new rotation.
switch (viewport.orientation) {
- case DISPLAY_ORIENTATION_90:
+ case ui::ROTATION_90:
temp = x;
x = y;
y = viewport.deviceHeight - temp;
break;
- case DISPLAY_ORIENTATION_180:
+ case ui::ROTATION_180:
x = viewport.deviceWidth - x;
y = viewport.deviceHeight - y;
break;
- case DISPLAY_ORIENTATION_270:
+ case ui::ROTATION_270:
temp = x;
x = viewport.deviceWidth - y;
y = temp;
break;
+ case ui::ROTATION_0:
+ break;
}
// Apply offsets to convert from the pixel center to the pixel top-left corner position
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
index 43bfa74..0e2d23b 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/RemoteTransitionAdapter.kt
@@ -195,8 +195,16 @@
val out = ArrayList<RemoteAnimationTarget>()
for (i in info.changes.indices) {
val change = info.changes[i]
- val changeIsWallpaper = change.flags and TransitionInfo.FLAG_IS_WALLPAPER != 0
- if (wallpapers != changeIsWallpaper) continue
+ if (change.hasFlags(TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ // For embedded container, when the parent Task is also in the transition, we
+ // should only animate the parent Task.
+ if (change.parent != null) continue
+ // For embedded container without parent, we should only animate if it fills
+ // the Task. Otherwise we may animate only partial of the Task.
+ if (!change.hasFlags(TransitionInfo.FLAG_FILLS_TASK)) continue
+ }
+ // Check if it is wallpaper
+ if (wallpapers != change.hasFlags(TransitionInfo.FLAG_IS_WALLPAPER)) continue
out.add(createTarget(change, info.changes.size - i, info, t))
if (leashMap != null) {
leashMap[change.leash] = out[out.size - 1].leash
diff --git a/packages/SystemUI/ktfmt_includes.txt b/packages/SystemUI/ktfmt_includes.txt
index a156aab..7290e7e 100644
--- a/packages/SystemUI/ktfmt_includes.txt
+++ b/packages/SystemUI/ktfmt_includes.txt
@@ -753,7 +753,7 @@
-packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherControllerOldImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/shared/ConnectivityPipelineLoggerTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt
--packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
-packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/BatteryStateNotifierTest.kt
diff --git a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
index 79ba7ead..aa655e6 100644
--- a/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
+++ b/packages/SystemUI/res/layout/media_smartspace_recommendations.xml
@@ -41,7 +41,7 @@
android:layout_width="@dimen/qs_media_app_icon_size"
android:layout_height="@dimen/qs_media_app_icon_size"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginTop="@dimen/qs_media_padding"
+ android:layout_marginTop="@dimen/qs_media_rec_icon_top_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 868c003..3fc59e3 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -35,6 +35,11 @@
not appear immediately after user swipes to the side -->
<dimen name="qs_tiles_page_horizontal_margin">20dp</dimen>
+ <!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">27dp</dimen>
+ <dimen name="qs_media_rec_album_size">152dp</dimen>
+ <dimen name="qs_media_rec_album_side_margin">16dp</dimen>
+
<dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
<!-- Roughly the same distance as media on LS to media on QS. We will translate by this value
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 8ee39dd..70d53c7 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -133,6 +133,9 @@
<color name="biometric_dialog_accent">@color/material_dynamic_primary40</color>
<color name="biometric_dialog_error">#ffd93025</color> <!-- red 600 -->
+ <!-- SFPS colors -->
+ <color name="sfps_chevron_fill">@color/material_dynamic_primary90</color>
+
<!-- UDFPS colors -->
<color name="udfps_enroll_icon">#699FF3</color>
<color name="udfps_moving_target_fill">#C2D7F7</color>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index d04e2b1..46e05fe 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1055,6 +1055,7 @@
<dimen name="qs_media_session_collapsed_guideline">144dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
+ <dimen name="qs_media_rec_icon_top_margin">16dp</dimen>
<dimen name="qs_media_rec_album_size">88dp</dimen>
<dimen name="qs_media_rec_album_side_margin">16dp</dimen>
<dimen name="qs_media_rec_album_bottom_margin">8dp</dimen>
diff --git a/packages/SystemUI/src/com/android/systemui/CoreStartable.java b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
index 929ebea..becf5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/CoreStartable.java
+++ b/packages/SystemUI/src/com/android/systemui/CoreStartable.java
@@ -39,10 +39,13 @@
*/
public interface CoreStartable extends Dumpable {
- /** Main entry point for implementations. Called shortly after app startup. */
+ /** Main entry point for implementations. Called shortly after SysUI startup. */
void start();
- /** */
+ /** Called when the device configuration changes. This will not be called before
+ * {@link #start()}, but it could be called before {@link #onBootCompleted()}.
+ *
+ * @see android.app.Application#onConfigurationChanged(Configuration) */
default void onConfigurationChanged(Configuration newConfig) {
}
@@ -50,7 +53,11 @@
default void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
}
- /** Called when the device reports BOOT_COMPLETED. */
+ /** Called immediately after the system broadcasts
+ * {@link android.content.Intent#ACTION_LOCKED_BOOT_COMPLETED} or during SysUI startup if the
+ * property {@code sys.boot_completed} is already set to 1. The latter typically occurs when
+ * starting a new SysUI instance, such as when starting SysUI for a secondary user.
+ * {@link #onBootCompleted()} will never be called before {@link #start()}. */
default void onBootCompleted() {
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index a7519cf..db2239b 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -623,6 +623,10 @@
getFingerprintSensorLocationInNaturalOrientation(),
mCachedDisplayInfo);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFingerprintLocationChanged();
+ }
}
/**
@@ -644,6 +648,10 @@
mCachedDisplayInfo
);
}
+
+ for (final Callback cb : mCallbacks) {
+ cb.onFaceSensorLocationChanged();
+ }
}
/**
@@ -1325,8 +1333,24 @@
default void onBiometricPromptDismissed() {}
/**
- * The location in pixels can change due to resolution changes.
+ * Called when the location of the fingerprint sensor changes. The location in pixels can
+ * change due to resolution changes.
+ */
+ default void onFingerprintLocationChanged() {}
+
+ /**
+ * Called when the location of the under display fingerprint sensor changes. The location in
+ * pixels can change due to resolution changes.
+ *
+ * On devices with UDFPS, this is always called alongside
+ * {@link #onFingerprintLocationChanged}.
*/
default void onUdfpsLocationChanged() {}
+
+ /**
+ * Called when the location of the face unlock sensor (typically the front facing camera)
+ * changes. The location in pixels can change due to resolution changes.
+ */
+ default void onFaceSensorLocationChanged() {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
index 6ac54fe..d561cd7 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthRippleController.kt
@@ -29,6 +29,8 @@
import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.Interpolators
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.CircleReveal
@@ -71,7 +73,8 @@
private val biometricUnlockController: BiometricUnlockController,
private val udfpsControllerProvider: Provider<UdfpsController>,
private val statusBarStateController: StatusBarStateController,
- rippleView: AuthRippleView?
+ private val featureFlags: FeatureFlags,
+ rippleView: AuthRippleView?
) : ViewController<AuthRippleView>(rippleView), KeyguardStateController.Callback,
WakefulnessLifecycle.Observer {
@@ -159,12 +162,17 @@
private fun showUnlockedRipple() {
notificationShadeWindowController.setForcePluginOpen(true, this)
- val lightRevealScrim = centralSurfaces.lightRevealScrim
- if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
- circleReveal?.let {
- lightRevealScrim?.revealAmount = 0f
- lightRevealScrim?.revealEffect = it
- startLightRevealScrimOnKeyguardFadingAway = true
+
+ // This code path is not used if the KeyguardTransitionRepository is managing the light
+ // reveal scrim.
+ if (!featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ val lightRevealScrim = centralSurfaces.lightRevealScrim
+ if (statusBarStateController.isDozing || biometricUnlockController.isWakeAndUnlock) {
+ circleReveal?.let {
+ lightRevealScrim?.revealAmount = 0f
+ lightRevealScrim?.revealEffect = it
+ startLightRevealScrimOnKeyguardFadingAway = true
+ }
}
}
@@ -177,6 +185,10 @@
}
override fun onKeyguardFadingAwayChanged() {
+ if (featureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return
+ }
+
if (keyguardStateController.isKeyguardFadingAway) {
val lightRevealScrim = centralSurfaces.lightRevealScrim
if (startLightRevealScrimOnKeyguardFadingAway && lightRevealScrim != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
index 1c3dd45..17ebdad 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SideFpsController.kt
@@ -135,7 +135,7 @@
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_SYSTEM_ERROR,
+ WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG,
Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS,
PixelFormat.TRANSLUCENT
)
@@ -370,11 +370,15 @@
private fun LottieAnimationView.addOverlayDynamicColor(context: Context) {
fun update() {
val c = context.getColor(R.color.biometric_dialog_accent)
+ val chevronFill = context.getColor(R.color.sfps_chevron_fill)
for (key in listOf(".blue600", ".blue400")) {
addValueCallback(KeyPath(key, "**"), LottieProperty.COLOR_FILTER) {
PorterDuffColorFilter(c, PorterDuff.Mode.SRC_ATOP)
}
}
+ addValueCallback(KeyPath(".black", "**"), LottieProperty.COLOR_FILTER) {
+ PorterDuffColorFilter(chevronFill, PorterDuff.Mode.SRC_ATOP)
+ }
}
if (composition != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 76ed304..8019b56 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -164,6 +164,13 @@
// TODO(b/256513609): Tracking Bug
@JvmField val ACTIVE_UNLOCK_CHIPBAR = releasedFlag(217, "active_unlock_chipbar")
+ /**
+ * Migrates control of the LightRevealScrim's reveal effect and amount from legacy code to the
+ * new KeyguardTransitionRepository.
+ */
+ @JvmField
+ val LIGHT_REVEAL_MIGRATION = unreleasedFlag(218, "light_reveal_migration", teamfood = true)
+
// 300 - power menu
// TODO(b/254512600): Tracking Bug
@JvmField val POWER_MENU_LITE = releasedFlag(300, "power_menu_lite")
@@ -403,8 +410,8 @@
val LEAVE_SHADE_OPEN_FOR_BUGREPORT =
unreleasedFlag(1800, "leave_shade_open_for_bugreport", teamfood = true)
- // 1900 - note task
- @JvmField val NOTE_TASKS = sysPropBooleanFlag(1900, "persist.sysui.debug.note_tasks")
+ // 1900
+ @JvmField val NOTE_TASKS = unreleasedFlag(1900, "keycode_flag")
// 2000 - device controls
@Keep @JvmField val USE_APP_PANELS = unreleasedFlag(2000, "use_app_panels", teamfood = true)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 796f2b4..148792b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -16,8 +16,11 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.common.shared.model.Position
@@ -27,8 +30,8 @@
import com.android.systemui.doze.DozeTransitionCallback
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
-import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.StatusBarState
@@ -88,8 +91,8 @@
* enter to conserve battery when the device is locked and inactive.
*
* Note that it is possible for the system to be transitioning into doze while this flow still
- * returns `false`. In order to account for that, observers should also use the [dozeAmount]
- * flow to check if it's greater than `0`
+ * returns `false`. In order to account for that, observers should also use the
+ * [linearDozeAmount] flow to check if it's greater than `0`
*/
val isDozing: Flow<Boolean>
@@ -111,7 +114,7 @@
* happens during an animation/transition into doze mode. An observer would be wise to account
* for both flows if needed.
*/
- val dozeAmount: Flow<Float>
+ val linearDozeAmount: Flow<Float>
/** Doze state information, as it transitions */
val dozeTransitionModel: Flow<DozeTransitionModel>
@@ -120,11 +123,20 @@
val statusBarState: Flow<StatusBarState>
/** Observable for device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel>
+ val wakefulness: Flow<WakefulnessModel>
/** Observable for biometric unlock modes */
val biometricUnlockState: Flow<BiometricUnlockModel>
+ /** Approximate location on the screen of the fingerprint sensor. */
+ val fingerprintSensorLocation: Flow<Point?>
+
+ /** Approximate location on the screen of the face unlock sensor/front facing camera. */
+ val faceSensorLocation: Flow<Point?>
+
+ /** Source of the most recent biometric unlock, such as fingerprint or face. */
+ val biometricUnlockSource: Flow<BiometricUnlockSource?>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -163,6 +175,7 @@
private val keyguardStateController: KeyguardStateController,
private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
private val dozeTransitionListener: DozeTransitionListener,
+ private val authController: AuthController,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -281,11 +294,11 @@
}
.distinctUntilChanged()
- override val dozeAmount: Flow<Float> = conflatedCallbackFlow {
+ override val linearDozeAmount: Flow<Float> = conflatedCallbackFlow {
val callback =
object : StatusBarStateController.StateListener {
override fun onDozeAmountChanged(linear: Float, eased: Float) {
- trySendWithFailureLogging(eased, TAG, "updated dozeAmount")
+ trySendWithFailureLogging(linear, TAG, "updated dozeAmount")
}
}
@@ -348,58 +361,139 @@
awaitClose { statusBarStateController.removeCallback(callback) }
}
- override val wakefulnessState: Flow<WakefulnessModel> = conflatedCallbackFlow {
- val callback =
- object : WakefulnessLifecycle.Observer {
- override fun onStartedWakingUp() {
- trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_WAKE,
- TAG,
- "Wakefulness: starting to wake"
- )
- }
- override fun onFinishedWakingUp() {
- trySendWithFailureLogging(WakefulnessModel.AWAKE, TAG, "Wakefulness: awake")
- }
- override fun onStartedGoingToSleep() {
- trySendWithFailureLogging(
- WakefulnessModel.STARTING_TO_SLEEP,
- TAG,
- "Wakefulness: starting to sleep"
- )
- }
- override fun onFinishedGoingToSleep() {
- trySendWithFailureLogging(WakefulnessModel.ASLEEP, TAG, "Wakefulness: asleep")
- }
- }
- wakefulnessLifecycle.addObserver(callback)
- trySendWithFailureLogging(
- wakefulnessIntToObject(wakefulnessLifecycle.getWakefulness()),
- TAG,
- "initial wakefulness state"
- )
-
- awaitClose { wakefulnessLifecycle.removeObserver(callback) }
- }
-
override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ fun dispatchUpdate() {
+ trySendWithFailureLogging(
+ biometricModeIntToObject(biometricUnlockController.mode),
+ TAG,
+ "biometric mode"
+ )
+ }
+
val callback =
object : BiometricUnlockController.BiometricModeListener {
override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
- trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode")
+ dispatchUpdate()
+ }
+
+ override fun onResetMode() {
+ dispatchUpdate()
}
}
biometricUnlockController.addBiometricModeListener(callback)
- trySendWithFailureLogging(
- biometricModeIntToObject(biometricUnlockController.getMode()),
- TAG,
- "initial biometric mode"
- )
+ dispatchUpdate()
awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
}
+ override val wakefulness: Flow<WakefulnessModel> = conflatedCallbackFlow {
+ val observer =
+ object : WakefulnessLifecycle.Observer {
+ override fun onStartedWakingUp() {
+ dispatchNewState()
+ }
+
+ override fun onFinishedWakingUp() {
+ dispatchNewState()
+ }
+
+ override fun onPostFinishedWakingUp() {
+ dispatchNewState()
+ }
+
+ override fun onStartedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ override fun onFinishedGoingToSleep() {
+ dispatchNewState()
+ }
+
+ private fun dispatchNewState() {
+ trySendWithFailureLogging(
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
+ TAG,
+ "updated wakefulness state"
+ )
+ }
+ }
+
+ wakefulnessLifecycle.addObserver(observer)
+ trySendWithFailureLogging(
+ WakefulnessModel.fromWakefulnessLifecycle(wakefulnessLifecycle),
+ TAG,
+ "initial wakefulness state"
+ )
+
+ awaitClose { wakefulnessLifecycle.removeObserver(observer) }
+ }
+
+ override val fingerprintSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendFpLocation() {
+ trySendWithFailureLogging(
+ authController.fingerprintSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
+ val callback =
+ object : AuthController.Callback {
+ override fun onFingerprintLocationChanged() {
+ sendFpLocation()
+ }
+ }
+
+ authController.addCallback(callback)
+ sendFpLocation()
+
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val faceSensorLocation: Flow<Point?> = conflatedCallbackFlow {
+ fun sendSensorLocation() {
+ trySendWithFailureLogging(
+ authController.faceSensorLocation,
+ TAG,
+ "AuthController.Callback#onFingerprintLocationChanged"
+ )
+ }
+
+ val callback =
+ object : AuthController.Callback {
+ override fun onFaceSensorLocationChanged() {
+ sendSensorLocation()
+ }
+ }
+
+ authController.addCallback(callback)
+ sendSensorLocation()
+
+ awaitClose { authController.removeCallback(callback) }
+ }
+
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardUpdateMonitorCallback() {
+ override fun onBiometricAuthenticated(
+ userId: Int,
+ biometricSourceType: BiometricSourceType?,
+ isStrongBiometric: Boolean
+ ) {
+ trySendWithFailureLogging(
+ BiometricUnlockSource.fromBiometricSourceType(biometricSourceType),
+ TAG,
+ "onBiometricAuthenticated"
+ )
+ }
+ }
+
+ keyguardUpdateMonitor.registerCallback(callback)
+ trySendWithFailureLogging(null, TAG, "initial value")
+ awaitClose { keyguardUpdateMonitor.removeCallback(callback) }
+ }
+
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.value = animate
}
@@ -423,16 +517,6 @@
}
}
- private fun wakefulnessIntToObject(@Wakefulness value: Int): WakefulnessModel {
- return when (value) {
- 0 -> WakefulnessModel.ASLEEP
- 1 -> WakefulnessModel.STARTING_TO_WAKE
- 2 -> WakefulnessModel.AWAKE
- 3 -> WakefulnessModel.STARTING_TO_SLEEP
- else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
- }
- }
-
private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
return when (value) {
0 -> BiometricUnlockModel.NONE
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
index 0c72520..26f853f 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryModule.kt
@@ -27,4 +27,7 @@
fun keyguardTransitionRepository(
impl: KeyguardTransitionRepositoryImpl
): KeyguardTransitionRepository
+
+ @Binds
+ fun lightRevealScrimRepository(impl: LightRevealScrimRepositoryImpl): LightRevealScrimRepository
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
new file mode 100644
index 0000000..a17481a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepository.kt
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:OptIn(ExperimentalCoroutinesApi::class)
+
+package com.android.systemui.keyguard.data.repository
+
+import android.content.Context
+import android.graphics.Point
+import com.android.systemui.R
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.keyguard.shared.model.WakeSleepReason
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LiftReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.PowerButtonReveal
+import javax.inject.Inject
+import kotlin.math.max
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
+
+val DEFAULT_REVEAL_EFFECT = LiftReveal
+
+/**
+ * Encapsulates state relevant to the light reveal scrim, the view used to reveal/hide screen
+ * contents during transitions between AOD and lockscreen/unlocked.
+ */
+interface LightRevealScrimRepository {
+
+ /**
+ * The reveal effect that should be used for the next lock/unlock. We switch between either the
+ * biometric unlock effect (if wake and unlocking) or the non-biometric effect, and position it
+ * at the current screen position of the appropriate sensor.
+ */
+ val revealEffect: Flow<LightRevealEffect>
+}
+
+@SysUISingleton
+class LightRevealScrimRepositoryImpl
+@Inject
+constructor(
+ keyguardRepository: KeyguardRepository,
+ val context: Context,
+) : LightRevealScrimRepository {
+
+ /** The reveal effect used if the device was locked/unlocked via the power button. */
+ private val powerButtonReveal =
+ PowerButtonReveal(
+ context.resources
+ .getDimensionPixelSize(R.dimen.physical_power_button_center_screen_location_y)
+ .toFloat()
+ )
+
+ /**
+ * Reveal effect to use for a fingerprint unlock. This is reconstructed if the fingerprint
+ * sensor location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val fingerprintRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.fingerprintSensorLocation.map {
+ it?.let { constructCircleRevealFromPoint(it) }
+ }
+
+ /**
+ * Reveal effect to use for a face unlock. This is reconstructed if the face sensor/front camera
+ * location on the screen (in pixels) changes due to configuration changes.
+ */
+ private val faceRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.faceSensorLocation.map { it?.let { constructCircleRevealFromPoint(it) } }
+
+ /**
+ * The reveal effect we'll use for the next biometric unlock animation. We switch between the
+ * fingerprint/face unlock effect flows depending on the biometric unlock source.
+ */
+ private val biometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.biometricUnlockSource.flatMapLatest { source ->
+ when (source) {
+ BiometricUnlockSource.FINGERPRINT_SENSOR -> fingerprintRevealEffect
+ BiometricUnlockSource.FACE_SENSOR -> faceRevealEffect
+ else -> flowOf(null)
+ }
+ }
+
+ /** The reveal effect we'll use for the next non-biometric unlock (tap, power button, etc). */
+ private val nonBiometricRevealEffect: Flow<LightRevealEffect?> =
+ keyguardRepository.wakefulness.map { wakefulnessModel ->
+ val wakingUpFromPowerButton =
+ wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastWakeReason == WakeSleepReason.POWER_BUTTON
+ val sleepingFromPowerButton =
+ !wakefulnessModel.isWakingUpOrAwake &&
+ wakefulnessModel.lastSleepReason == WakeSleepReason.POWER_BUTTON
+
+ if (wakingUpFromPowerButton || sleepingFromPowerButton) {
+ powerButtonReveal
+ } else {
+ LiftReveal
+ }
+ }
+
+ override val revealEffect =
+ combine(
+ keyguardRepository.biometricUnlockState,
+ biometricRevealEffect,
+ nonBiometricRevealEffect
+ ) { biometricUnlockState, biometricReveal, nonBiometricReveal ->
+
+ // Use the biometric reveal for any flavor of wake and unlocking.
+ when (biometricUnlockState) {
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM -> biometricReveal
+ else -> nonBiometricReveal
+ }
+ ?: DEFAULT_REVEAL_EFFECT
+ }
+ .distinctUntilChanged()
+
+ private fun constructCircleRevealFromPoint(point: Point): LightRevealEffect {
+ return with(point) {
+ CircleReveal(
+ x,
+ y,
+ startRadius = 0,
+ endRadius =
+ max(max(x, context.display.width - x), max(y, context.display.height - y)),
+ )
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
index 2dbacd5..9b19353 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodLockscreenTransitionInteractor.kt
@@ -27,7 +27,6 @@
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
index 9e2b724..e34981e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/DreamingToAodTransitionInteractor.kt
@@ -42,7 +42,7 @@
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
index 0e2a54c..483041a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/GoneAodTransitionInteractor.kt
@@ -23,11 +23,10 @@
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionInfo
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.util.kotlin.sample
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
@SysUISingleton
@@ -42,13 +41,13 @@
override fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState
+ keyguardInteractor.wakefulnessModel
.sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
.collect { pair ->
val (wakefulnessState, keyguardState) = pair
if (
keyguardState == KeyguardState.GONE &&
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP
) {
keyguardTransitionRepository.startTransition(
TransitionInfo(
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 7cfd117..6912e1d 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -17,6 +17,7 @@
package com.android.systemui.keyguard.domain.interactor
+import android.graphics.Point
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
@@ -41,7 +42,7 @@
* The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at
* all.
*/
- val dozeAmount: Flow<Float> = repository.dozeAmount
+ val dozeAmount: Flow<Float> = repository.linearDozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
/** Doze transition information. */
@@ -58,7 +59,7 @@
/** Whether the bouncer is showing or not. */
val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
- val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
+ val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness
/** Observable for the [StatusBarState] */
val statusBarState: Flow<StatusBarState> = repository.statusBarState
/**
@@ -67,10 +68,15 @@
*/
val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
+ /** The approximate location on the screen of the fingerprint sensor, if one is available. */
+ val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation
+
+ /** The approximate location on the screen of the face unlock sensor, if one is available. */
+ val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation
+
fun dozeTransitionTo(state: DozeStateModel): Flow<DozeTransitionModel> {
return dozeTransitionModel.filter { it.to == state }
}
-
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
}
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 58a8093..e30e7f6 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
@@ -37,7 +37,7 @@
fun start() {
scope.launch {
- keyguardInteractor.wakefulnessState.collect { logger.v("WakefulnessState", it) }
+ keyguardInteractor.wakefulnessModel.collect { logger.v("WakefulnessModel", it) }
}
scope.launch {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
new file mode 100644
index 0000000..6e25200
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractor.kt
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.LightRevealScrimRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionStep
+import com.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.filter
+import kotlinx.coroutines.flow.map
+
+@ExperimentalCoroutinesApi
+@SysUISingleton
+class LightRevealScrimInteractor
+@Inject
+constructor(
+ transitionRepository: KeyguardTransitionRepository,
+ transitionInteractor: KeyguardTransitionInteractor,
+ lightRevealScrimRepository: LightRevealScrimRepository,
+) {
+
+ /**
+ * Whenever a keyguard transition starts, sample the latest reveal effect from the repository
+ * and use that for the starting transition.
+ *
+ * We can't simply use the nextRevealEffect since the effect may change midway through a
+ * transition, but we don't want to change effects part way through. For example, if we're using
+ * a CircleReveal to animate a biometric unlock, but the biometric unlock mode changes to NONE
+ * from WAKE_AND_UNLOCK before the unlock animation ends, we don't want to end up switching to a
+ * LiftReveal.
+ */
+ val lightRevealEffect: Flow<LightRevealEffect> =
+ transitionInteractor.startedKeyguardTransitionStep.sample(
+ lightRevealScrimRepository.revealEffect
+ )
+
+ /**
+ * The reveal amount to use for the light reveal scrim, which is derived from the keyguard
+ * transition steps.
+ */
+ val revealAmount: Flow<Float> =
+ transitionRepository.transitions
+ // Only listen to transitions that change the reveal amount.
+ .filter { willTransitionAffectRevealAmount(it) }
+ // Use the transition amount as the reveal amount, inverting it if we're transitioning
+ // to a non-revealed (hidden) state.
+ .map { step -> if (willBeRevealedInState(step.to)) step.value else 1f - step.value }
+
+ companion object {
+
+ /**
+ * Whether the transition requires a change in the reveal amount of the light reveal scrim.
+ * If not, we don't care about the transition and don't need to listen to it.
+ */
+ fun willTransitionAffectRevealAmount(transition: TransitionStep): Boolean {
+ return willBeRevealedInState(transition.from) != willBeRevealedInState(transition.to)
+ }
+
+ /**
+ * Whether the light reveal scrim will be fully revealed (revealAmount = 1.0f) in the given
+ * state after the transition is complete. If false, scrim will be fully hidden.
+ */
+ fun willBeRevealedInState(state: KeyguardState): Boolean {
+ return when (state) {
+ KeyguardState.OFF -> false
+ KeyguardState.DOZING -> false
+ KeyguardState.AOD -> false
+ KeyguardState.DREAMING -> true
+ KeyguardState.BOUNCER -> true
+ KeyguardState.LOCKSCREEN -> true
+ KeyguardState.GONE -> true
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 3bb8241..3218f96 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -25,7 +25,7 @@
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
-import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
@@ -58,22 +58,26 @@
keyguardInteractor.isBouncerShowing
.sample(
combine(
- keyguardInteractor.wakefulnessState,
+ keyguardInteractor.wakefulnessModel,
keyguardTransitionInteractor.startedKeyguardTransitionStep,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) = triple
+ ) { wakefulnessModel, transitionStep ->
+ Pair(wakefulnessModel, transitionStep)
+ }
+ ) { bouncerShowing, wakefulnessAndTransition ->
+ Triple(
+ bouncerShowing,
+ wakefulnessAndTransition.first,
+ wakefulnessAndTransition.second
+ )
+ }
+ .collect { (isBouncerShowing, wakefulnessState, lastStartedTransitionStep) ->
if (
!isBouncerShowing && lastStartedTransitionStep.to == KeyguardState.BOUNCER
) {
val to =
if (
- wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
- wakefulnessState == WakefulnessModel.ASLEEP
+ wakefulnessState.state == WakefulnessState.STARTING_TO_SLEEP ||
+ wakefulnessState.state == WakefulnessState.ASLEEP
) {
KeyguardState.AOD
} else {
@@ -100,14 +104,17 @@
combine(
keyguardTransitionInteractor.finishedKeyguardState,
keyguardInteractor.statusBarState,
- ) { a, b ->
- Pair(a, b)
- },
- { a, bc -> Triple(a, bc.first, bc.second) }
- )
- .collect { triple ->
- val (shadeModel, keyguardState, statusBarState) = triple
-
+ ) { finishedKeyguardState, statusBarState ->
+ Pair(finishedKeyguardState, statusBarState)
+ }
+ ) { shadeModel, keyguardStateAndStatusBarState ->
+ Triple(
+ shadeModel,
+ keyguardStateAndStatusBarState.first,
+ keyguardStateAndStatusBarState.second
+ )
+ }
+ .collect { (shadeModel, keyguardState, statusBarState) ->
val id = transitionId
if (id != null) {
// An existing `id` means a transition is started, and calls to
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
new file mode 100644
index 0000000..b403416
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockSource.kt
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.hardware.biometrics.BiometricSourceType
+
+/** Biometric unlock sensor sources, which we use to play sensor-specific animations. */
+enum class BiometricUnlockSource {
+ /** The unlock was initiated by a fingerprint sensor authentication. */
+ FINGERPRINT_SENSOR,
+
+ /** The unlock was initiated by the front-facing camera or a nearby sensor. */
+ FACE_SENSOR;
+
+ companion object {
+ fun fromBiometricSourceType(type: BiometricSourceType?): BiometricUnlockSource? {
+ return when (type) {
+ BiometricSourceType.FINGERPRINT -> FINGERPRINT_SENSOR
+ BiometricSourceType.FACE -> FACE_SENSOR
+ BiometricSourceType.IRIS -> FACE_SENSOR
+ else -> null
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
new file mode 100644
index 0000000..b32597d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakeSleepReason.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import android.os.PowerManager
+
+/** The reason we're waking up or going to sleep, such as pressing the power button. */
+enum class WakeSleepReason {
+ /** The physical power button was pressed to wake up or sleep the device. */
+ POWER_BUTTON,
+
+ /** Something else happened to wake up or sleep the device. */
+ OTHER;
+
+ companion object {
+ fun fromPowerManagerWakeReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.WAKE_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+
+ fun fromPowerManagerSleepReason(reason: Int): WakeSleepReason {
+ return when (reason) {
+ PowerManager.GO_TO_SLEEP_REASON_POWER_BUTTON -> POWER_BUTTON
+ else -> OTHER
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
index 92040f4..03dee00 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessModel.kt
@@ -15,24 +15,34 @@
*/
package com.android.systemui.keyguard.shared.model
-/** Model device wakefulness states. */
-enum class WakefulnessModel {
- /** The device is asleep and not interactive. */
- ASLEEP,
- /** Received a signal that the device is beginning to wake up. */
- STARTING_TO_WAKE,
- /** Device is now fully awake and interactive. */
- AWAKE,
- /** Signal that the device is now going to sleep. */
- STARTING_TO_SLEEP;
+import com.android.systemui.keyguard.WakefulnessLifecycle
+/** Model device wakefulness states. */
+data class WakefulnessModel(
+ val state: WakefulnessState,
+ val isWakingUpOrAwake: Boolean,
+ val lastWakeReason: WakeSleepReason,
+ val lastSleepReason: WakeSleepReason,
+) {
companion object {
fun isSleepingOrStartingToSleep(model: WakefulnessModel): Boolean {
- return model == ASLEEP || model == STARTING_TO_SLEEP
+ return model.state == WakefulnessState.ASLEEP ||
+ model.state == WakefulnessState.STARTING_TO_SLEEP
}
fun isWakingOrStartingToWake(model: WakefulnessModel): Boolean {
- return model == AWAKE || model == STARTING_TO_WAKE
+ return model.state == WakefulnessState.AWAKE ||
+ model.state == WakefulnessState.STARTING_TO_WAKE
+ }
+
+ fun fromWakefulnessLifecycle(wakefulnessLifecycle: WakefulnessLifecycle): WakefulnessModel {
+ return WakefulnessModel(
+ WakefulnessState.fromWakefulnessLifecycleInt(wakefulnessLifecycle.wakefulness),
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_WAKING ||
+ wakefulnessLifecycle.wakefulness == WakefulnessLifecycle.WAKEFULNESS_AWAKE,
+ WakeSleepReason.fromPowerManagerWakeReason(wakefulnessLifecycle.lastWakeReason),
+ WakeSleepReason.fromPowerManagerSleepReason(wakefulnessLifecycle.lastSleepReason),
+ )
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
new file mode 100644
index 0000000..6791d88
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/WakefulnessState.kt
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.model
+
+import com.android.systemui.keyguard.WakefulnessLifecycle
+
+enum class WakefulnessState {
+ /** The device is asleep and not interactive. */
+ ASLEEP,
+ /** Received a signal that the device is beginning to wake up. */
+ STARTING_TO_WAKE,
+ /** Device is now fully awake and interactive. */
+ AWAKE,
+ /** Signal that the device is now going to sleep. */
+ STARTING_TO_SLEEP;
+
+ companion object {
+ fun fromWakefulnessLifecycleInt(
+ @WakefulnessLifecycle.Wakefulness value: Int
+ ): WakefulnessState {
+ return when (value) {
+ WakefulnessLifecycle.WAKEFULNESS_ASLEEP -> ASLEEP
+ WakefulnessLifecycle.WAKEFULNESS_WAKING -> STARTING_TO_WAKE
+ WakefulnessLifecycle.WAKEFULNESS_AWAKE -> AWAKE
+ WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP -> STARTING_TO_SLEEP
+ else -> throw IllegalArgumentException("Invalid Wakefulness value: $value")
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
new file mode 100644
index 0000000..f1da882
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/LightRevealScrimViewBinder.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.binder
+
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.launch
+
+object LightRevealScrimViewBinder {
+ @JvmStatic
+ fun bind(revealScrim: LightRevealScrim, viewModel: LightRevealScrimViewModel) {
+ revealScrim.repeatWhenAttached {
+ repeatOnLifecycle(Lifecycle.State.CREATED) {
+ launch {
+ viewModel.revealAmount.collect { amount -> revealScrim.revealAmount = amount }
+ }
+
+ launch {
+ viewModel.lightRevealEffect.collect { effect ->
+ revealScrim.revealEffect = effect
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
new file mode 100644
index 0000000..a46d441
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/LightRevealScrimViewModel.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.ui.viewmodel
+
+import com.android.systemui.keyguard.domain.interactor.LightRevealScrimInteractor
+import com.android.systemui.statusbar.LightRevealEffect
+import javax.inject.Inject
+import kotlinx.coroutines.flow.Flow
+
+/**
+ * Models UI state for the light reveal scrim, which is used during screen on and off animations to
+ * draw a gradient that reveals/hides the contents of the screen.
+ */
+class LightRevealScrimViewModel @Inject constructor(interactor: LightRevealScrimInteractor) {
+ val lightRevealEffect: Flow<LightRevealEffect> = interactor.lightRevealEffect
+ val revealAmount: Flow<Float> = interactor.revealAmount
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
index 8aaee81..1fdbc99 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaCarouselController.kt
@@ -184,6 +184,7 @@
private val configListener =
object : ConfigurationController.ConfigurationListener {
+
override fun onDensityOrFontScaleChanged() {
// System font changes should only happen when UMO is offscreen or a flicker may
// occur
@@ -199,6 +200,7 @@
override fun onConfigChanged(newConfig: Configuration?) {
if (newConfig == null) return
isRtl = newConfig.layoutDirection == View.LAYOUT_DIRECTION_RTL
+ updatePlayers(recreateMedia = true)
}
override fun onUiModeChanged() {
@@ -635,7 +637,7 @@
val existingSmartspaceMediaKey = MediaPlayerData.smartspaceMediaKey()
existingSmartspaceMediaKey?.let {
val removedPlayer =
- MediaPlayerData.removeMediaPlayer(existingSmartspaceMediaKey, true)
+ removePlayer(existingSmartspaceMediaKey, dismissMediaData = false)
removedPlayer?.run {
debugLogger.logPotentialMemoryLeak(existingSmartspaceMediaKey)
}
@@ -685,7 +687,7 @@
key: String,
dismissMediaData: Boolean = true,
dismissRecommendation: Boolean = true
- ) {
+ ): MediaControlPanel? {
if (key == MediaPlayerData.smartspaceMediaKey()) {
MediaPlayerData.smartspaceMediaData?.let {
logger.logRecommendationRemoved(it.packageName, it.instanceId)
@@ -693,7 +695,7 @@
}
val removed =
MediaPlayerData.removeMediaPlayer(key, dismissMediaData || dismissRecommendation)
- removed?.apply {
+ return removed?.apply {
mediaCarouselScrollHandler.onPrePlayerRemoved(removed)
mediaContent.removeView(removed.mediaViewHolder?.player)
mediaContent.removeView(removed.recommendationViewHolder?.recommendations)
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index 21e64e2..827ac78 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -458,7 +458,9 @@
if (mMediaViewHolder == null) {
return;
}
- Trace.beginSection("MediaControlPanel#bindPlayer<" + key + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "MediaControlPanel#bindPlayer<" + key + ">");
+ }
mKey = key;
mMediaData = data;
MediaSession.Token token = data.getToken();
@@ -1179,8 +1181,10 @@
return;
}
- Trace.beginSection(
- "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP,
+ "MediaControlPanel#bindRecommendation<" + data.getPackageName() + ">");
+ }
mRecommendationData = data;
mSmartspaceId = SmallHash.hash(data.getTargetId());
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
index 966ab4c..afdadeb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/Pluggable.java
@@ -51,7 +51,9 @@
*/
public final void invalidateList(@Nullable String reason) {
if (mListener != null) {
- Trace.beginSection("Pluggable<" + mName + ">.invalidateList");
+ if (Trace.isEnabled()) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "Pluggable<" + mName + ">.invalidateList");
+ }
mListener.onPluggableInvalidated((This) this, reason);
Trace.endSection();
}
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 1eccc98..d7d5ac9 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
@@ -19,6 +19,7 @@
import static android.app.Notification.Action.SEMANTIC_ACTION_MARK_CONVERSATION_AS_PRIORITY;
import static android.service.notification.NotificationListenerService.REASON_CANCEL;
+import static com.android.systemui.statusbar.notification.collection.NotificationEntry.DismissState.PARENT_DISMISSED;
import static com.android.systemui.statusbar.notification.row.NotificationContentView.VISIBLE_TYPE_HEADSUP;
import android.animation.Animator;
@@ -1404,6 +1405,11 @@
mKeepInParentForDismissAnimation = keepInParent;
}
+ /** @return true if the User has dismissed this notif's parent */
+ public boolean isParentDismissed() {
+ return getEntry().getDismissState() == PARENT_DISMISSED;
+ }
+
@Override
public boolean isRemoved() {
return mRemoved;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index f9e9a2d..8a400d5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -359,10 +359,15 @@
@Override
public boolean offerToKeepInParentForAnimation() {
- if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)) {
+ //If the User dismissed the notification's parent, we want to keep it attached until the
+ //dismiss animation is ongoing. Therefore we don't want to remove it in the ShadeViewDiffer.
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION)
+ && mView.isParentDismissed()) {
mView.setKeepInParentForDismissAnimation(true);
return true;
}
+
+ //Otherwise the view system doesn't do the removal, so we rely on the ShadeViewDiffer
return false;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 34e62ce..03057a4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -496,7 +496,7 @@
&& mPendingAuthenticated.userId == KeyguardUpdateMonitor.getCurrentUser();
}
- public int getMode() {
+ public @WakeAndUnlockMode int getMode() {
return mMode;
}
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 1611257..d027ed0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -158,6 +158,8 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.binder.LightRevealScrimViewBinder;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.navigationbar.NavigationBarView;
import com.android.systemui.plugins.DarkIconDispatcher;
@@ -474,6 +476,7 @@
private final OngoingCallController mOngoingCallController;
private final StatusBarSignalPolicy mStatusBarSignalPolicy;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
+ private final Lazy<LightRevealScrimViewModel> mLightRevealScrimViewModelLazy;
/** Controller for the Shade. */
@VisibleForTesting
@@ -740,7 +743,8 @@
DeviceStateManager deviceStateManager,
WiredChargingRippleController wiredChargingRippleController,
IDreamManager dreamManager,
- Lazy<CameraLauncher> cameraLauncherLazy) {
+ Lazy<CameraLauncher> cameraLauncherLazy,
+ Lazy<LightRevealScrimViewModel> lightRevealScrimViewModelLazy) {
mContext = context;
mNotificationsController = notificationsController;
mFragmentService = fragmentService;
@@ -854,6 +858,8 @@
deviceStateManager.registerCallback(mMainExecutor,
new FoldStateListener(mContext, this::onFoldedStateChanged));
wiredChargingRippleController.registerCallbacks();
+
+ mLightRevealScrimViewModelLazy = lightRevealScrimViewModelLazy;
}
@Override
@@ -983,6 +989,12 @@
@Override
public void onKeyguardGoingAwayChanged() {
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ // This code path is not used if the KeyguardTransitionRepository is managing
+ // the lightreveal scrim.
+ return;
+ }
+
// The light reveal scrim should always be fully revealed by the time the keyguard
// is done going away. Double check that this is true.
if (!mKeyguardStateController.isKeyguardGoingAway()) {
@@ -1219,6 +1231,12 @@
mScrimController.attachViews(scrimBehind, notificationsScrim, scrimInFront);
mLightRevealScrim = mNotificationShadeWindowView.findViewById(R.id.light_reveal_scrim);
+
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ LightRevealScrimViewBinder.bind(
+ mLightRevealScrim, mLightRevealScrimViewModelLazy.get());
+ }
+
mLightRevealScrim.setScrimOpaqueChangedListener((opaque) -> {
Runnable updateOpaqueness = () -> {
mNotificationShadeWindowController.setLightRevealScrimOpaque(
@@ -3289,6 +3307,10 @@
return;
}
+ if (mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ return;
+ }
+
final boolean wakingUpFromPowerButton = wakingUp
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)
&& mWakefulnessLifecycle.getLastWakeReason()
@@ -4053,7 +4075,9 @@
return;
}
- mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ if (!mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)) {
+ mLightRevealScrim.setAlpha(mScrimController.getState().getMaxLightRevealScrimAlpha());
+ }
}
@Override
@@ -4234,6 +4258,7 @@
@Override
public void onDozeAmountChanged(float linear, float eased) {
if (mFeatureFlags.isEnabled(Flags.LOCKSCREEN_ANIMATIONS)
+ && !mFeatureFlags.isEnabled(Flags.LIGHT_REVEAL_MIGRATION)
&& !(mLightRevealScrim.getRevealEffect() instanceof CircleReveal)) {
mLightRevealScrim.setRevealAmount(1f - linear);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
index fe30c01..4a5342e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModel.kt
@@ -36,16 +36,20 @@
* [com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository] for
* more details.
*/
+interface AirplaneModeViewModel {
+ /** True if the airplane mode icon is currently visible in the status bar. */
+ val isAirplaneModeIconVisible: StateFlow<Boolean>
+}
+
@SysUISingleton
-class AirplaneModeViewModel
+class AirplaneModeViewModelImpl
@Inject
constructor(
interactor: AirplaneModeInteractor,
logger: ConnectivityPipelineLogger,
@Application private val scope: CoroutineScope,
-) {
- /** True if the airplane mode icon is currently visible in the status bar. */
- val isAirplaneModeIconVisible: StateFlow<Boolean> =
+) : AirplaneModeViewModel {
+ override val isAirplaneModeIconVisible: StateFlow<Boolean> =
combine(interactor.isAirplaneMode, interactor.isForceHidden) {
isAirplaneMode,
isAirplaneIconForceHidden ->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index c961422..fb67f1a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -21,6 +21,8 @@
import com.android.systemui.log.table.TableLogBufferFactory
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepositoryImpl
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryImpl
import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository
@@ -33,6 +35,8 @@
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import dagger.Binds
import dagger.Module
import dagger.Provides
@@ -43,12 +47,18 @@
abstract fun airplaneModeRepository(impl: AirplaneModeRepositoryImpl): AirplaneModeRepository
@Binds
+ abstract fun airplaneModeViewModel(impl: AirplaneModeViewModelImpl): AirplaneModeViewModel
+
+ @Binds
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
@Binds
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
@Binds
+ abstract fun wifiInteractor(impl: WifiInteractorImpl): WifiInteractor
+
+ @Binds
abstract fun mobileConnectionsRepository(
impl: MobileConnectionsRepositoryImpl
): MobileConnectionsRepository
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
index 3a3e611..ec935fe 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt
@@ -34,16 +34,36 @@
* This interactor processes information from our data layer into information that the UI layer can
* use.
*/
-@SysUISingleton
-class WifiInteractor @Inject constructor(
- connectivityRepository: ConnectivityRepository,
- wifiRepository: WifiRepository,
-) {
+interface WifiInteractor {
/**
* The SSID (service set identifier) of the wifi network. Null if we don't have a network, or
* have a network but no valid SSID.
*/
- val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
+ val ssid: Flow<String?>
+
+ /** Our current enabled status. */
+ val isEnabled: Flow<Boolean>
+
+ /** Our current default status. */
+ val isDefault: Flow<Boolean>
+
+ /** Our current wifi network. See [WifiNetworkModel]. */
+ val wifiNetwork: Flow<WifiNetworkModel>
+
+ /** Our current wifi activity. See [WifiActivityModel]. */
+ val activity: StateFlow<WifiActivityModel>
+
+ /** True if we're configured to force-hide the wifi icon and false otherwise. */
+ val isForceHidden: Flow<Boolean>
+}
+
+@SysUISingleton
+class WifiInteractorImpl @Inject constructor(
+ connectivityRepository: ConnectivityRepository,
+ wifiRepository: WifiRepository,
+) : WifiInteractor {
+
+ override val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
when (info) {
is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
@@ -56,20 +76,15 @@
}
}
- /** Our current enabled status. */
- val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
+ override val isEnabled: Flow<Boolean> = wifiRepository.isWifiEnabled
- /** Our current default status. */
- val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
+ override val isDefault: Flow<Boolean> = wifiRepository.isWifiDefault
- /** Our current wifi network. See [WifiNetworkModel]. */
- val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
+ override val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
- /** Our current wifi activity. See [WifiActivityModel]. */
- val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
+ override val activity: StateFlow<WifiActivityModel> = wifiRepository.wifiActivity
- /** True if we're configured to force-hide the wifi icon and false otherwise. */
- val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
+ override val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
it.contains(ConnectivitySlot.WIFI)
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
index 0910ea3..37115ad 100644
--- a/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModel.kt
@@ -19,6 +19,8 @@
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
+import com.android.systemui.R
+import com.android.systemui.common.shared.model.Text
import com.android.systemui.common.ui.drawable.CircularDrawable
import com.android.systemui.power.domain.interactor.PowerInteractor
import com.android.systemui.user.domain.interactor.GuestUserInteractor
@@ -144,7 +146,12 @@
): UserViewModel {
return UserViewModel(
viewKey = model.id,
- name = model.name,
+ name =
+ if (model.isGuest && model.isSelected) {
+ Text.Resource(R.string.guest_exit_quick_settings_button)
+ } else {
+ model.name
+ },
image = CircularDrawable(model.image),
isSelectionMarkerVisible = model.isSelected,
alpha =
diff --git a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
index 5b16ae9..b311318 100644
--- a/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
+++ b/packages/SystemUI/src/com/android/systemui/util/TraceUtils.kt
@@ -22,11 +22,22 @@
* Run a block within a [Trace] section.
* Calls [Trace.beginSection] before and [Trace.endSection] after the passed block.
*/
-inline fun <T> traceSection(tag: String, block: () -> T): T {
- Trace.beginSection(tag)
- try {
- return block()
- } finally {
- Trace.endSection()
+inline fun <T> traceSection(tag: String, block: () -> T): T =
+ if (Trace.isTagEnabled(Trace.TRACE_TAG_APP)) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, tag)
+ try {
+ block()
+ } finally {
+ Trace.traceEnd(Trace.TRACE_TAG_APP)
+ }
+ } else {
+ block()
+ }
+
+class TraceUtils {
+ companion object {
+ inline fun traceRunnable(tag: String, crossinline block: () -> Unit): Runnable {
+ return Runnable { traceSection(tag) { block() } }
+ }
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
index 57ca9c0..9d39a8c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/accessibility/WindowMagnificationControllerTest.java
@@ -34,7 +34,6 @@
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
@@ -88,6 +87,8 @@
import com.android.systemui.util.leak.ReferenceTestUtils;
import com.android.systemui.utils.os.FakeHandler;
+import com.google.common.util.concurrent.AtomicDouble;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -710,7 +711,7 @@
}
@Test
- public void onSingleTap_enabled_scaleIsChanged() {
+ public void onSingleTap_enabled_scaleAnimates() {
mInstrumentation.runOnMainSync(() -> {
mWindowMagnificationController.enableWindowMagnificationInternal(Float.NaN, Float.NaN,
Float.NaN);
@@ -721,14 +722,28 @@
});
final View mirrorView = mWindowManager.getAttachedView();
+
final long timeout = SystemClock.uptimeMillis() + 1000;
- while (SystemClock.uptimeMillis() < timeout) {
- SystemClock.sleep(10);
- if (Float.compare(1.0f, mirrorView.getScaleX()) < 0) {
- return;
+ final AtomicDouble maxScaleX = new AtomicDouble();
+ final Runnable onAnimationFrame = new Runnable() {
+ @Override
+ public void run() {
+ // For some reason the fancy way doesn't compile...
+// maxScaleX.getAndAccumulate(mirrorView.getScaleX(), Math::max);
+ final double oldMax = maxScaleX.get();
+ final double newMax = Math.max(mirrorView.getScaleX(), oldMax);
+ assertTrue(maxScaleX.compareAndSet(oldMax, newMax));
+
+ if (SystemClock.uptimeMillis() < timeout) {
+ mirrorView.postOnAnimation(this);
+ }
}
- }
- fail("MirrorView scale is not changed");
+ };
+ mirrorView.postOnAnimation(onAnimationFrame);
+
+ waitForIdleSync();
+
+ ReferenceTestUtils.waitForCondition(() -> maxScaleX.get() > 1.0);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
index eb8c823..b765ab3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthRippleControllerTest.kt
@@ -26,6 +26,7 @@
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.LightRevealScrim
@@ -77,6 +78,7 @@
@Mock private lateinit var udfpsControllerProvider: Provider<UdfpsController>
@Mock private lateinit var udfpsController: UdfpsController
@Mock private lateinit var statusBarStateController: StatusBarStateController
+ @Mock private lateinit var featureFlags: FeatureFlags
@Mock private lateinit var lightRevealScrim: LightRevealScrim
@Mock private lateinit var fpSensorProp: FingerprintSensorPropertiesInternal
@@ -106,6 +108,7 @@
biometricUnlockController,
udfpsControllerProvider,
statusBarStateController,
+ featureFlags,
rippleView
)
controller.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
index e7d5632..3c40835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/SideFpsControllerTest.kt
@@ -47,6 +47,7 @@
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION
import android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
+import android.view.WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG
import android.view.WindowMetrics
import androidx.test.filters.SmallTest
import com.airbnb.lottie.LottieAnimationView
@@ -423,6 +424,21 @@
}
@Test
+ fun testLayoutParams_isKeyguardDialogType() =
+ testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
+ sideFpsController.overlayOffsets = sensorLocation
+ sideFpsController.updateOverlayParams(windowManager.defaultDisplay, indicatorBounds)
+ overlayController.show(SENSOR_ID, REASON_UNKNOWN)
+ executor.runAllReady()
+
+ verify(windowManager).updateViewLayout(any(), overlayViewParamsCaptor.capture())
+
+ val lpType = overlayViewParamsCaptor.value.type
+
+ assertThat((lpType and TYPE_KEYGUARD_DIALOG) != 0).isTrue()
+ }
+
+ @Test
fun testLayoutParams_hasNoMoveAnimationWindowFlag() =
testWithDisplay(deviceConfig = DeviceConfig.Y_ALIGNED_UNFOLDED) {
sideFpsController.overlayOffsets = sensorLocation
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index 13fc9fc..5deac19 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -16,10 +16,13 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
+import android.hardware.biometrics.BiometricSourceType
import androidx.test.filters.SmallTest
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthController
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
import com.android.systemui.doze.DozeMachine
@@ -27,9 +30,11 @@
import com.android.systemui.doze.DozeTransitionListener
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeStateModel
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
+import com.android.systemui.keyguard.shared.model.WakefulnessState
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -57,9 +62,10 @@
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
- @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
@Mock private lateinit var biometricUnlockController: BiometricUnlockController
@Mock private lateinit var dozeTransitionListener: DozeTransitionListener
+ @Mock private lateinit var authController: AuthController
+ @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor
private lateinit var underTest: KeyguardRepositoryImpl
@@ -76,6 +82,7 @@
keyguardStateController,
keyguardUpdateMonitor,
dozeTransitionListener,
+ authController,
)
}
@@ -198,7 +205,7 @@
fun dozeAmount() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<Float>()
- val job = underTest.dozeAmount.onEach(values::add).launchIn(this)
+ val job = underTest.linearDozeAmount.onEach(values::add).launchIn(this)
val captor = argumentCaptor<StatusBarStateController.StateListener>()
verify(statusBarStateController).addCallback(captor.capture())
@@ -207,7 +214,7 @@
captor.value.onDozeAmountChanged(0.498f, 0.5f)
captor.value.onDozeAmountChanged(0.661f, 0.65f)
- assertThat(values).isEqualTo(listOf(0f, 0.4f, 0.5f, 0.65f))
+ assertThat(values).isEqualTo(listOf(0f, 0.433f, 0.498f, 0.661f))
job.cancel()
verify(statusBarStateController).removeCallback(captor.value)
@@ -217,25 +224,36 @@
fun wakefulness() =
runTest(UnconfinedTestDispatcher()) {
val values = mutableListOf<WakefulnessModel>()
- val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
+ val job = underTest.wakefulness.onEach(values::add).launchIn(this)
val captor = argumentCaptor<WakefulnessLifecycle.Observer>()
verify(wakefulnessLifecycle).addObserver(captor.capture())
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_WAKING)
captor.value.onStartedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_AWAKE)
captor.value.onFinishedWakingUp()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_GOING_TO_SLEEP)
captor.value.onStartedGoingToSleep()
+
+ whenever(wakefulnessLifecycle.wakefulness)
+ .thenReturn(WakefulnessLifecycle.WAKEFULNESS_ASLEEP)
captor.value.onFinishedGoingToSleep()
- assertThat(values)
+ assertThat(values.map { it.state })
.isEqualTo(
listOf(
// Initial value will be ASLEEP
- WakefulnessModel.ASLEEP,
- WakefulnessModel.STARTING_TO_WAKE,
- WakefulnessModel.AWAKE,
- WakefulnessModel.STARTING_TO_SLEEP,
- WakefulnessModel.ASLEEP,
+ WakefulnessState.ASLEEP,
+ WakefulnessState.STARTING_TO_WAKE,
+ WakefulnessState.AWAKE,
+ WakefulnessState.STARTING_TO_SLEEP,
+ WakefulnessState.ASLEEP,
)
)
@@ -329,14 +347,20 @@
val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
verify(biometricUnlockController).addBiometricModeListener(captor.capture())
- captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
- captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
- captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
- captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+ listOf(
+ BiometricUnlockController.MODE_NONE,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockController.MODE_SHOW_BOUNCER,
+ BiometricUnlockController.MODE_ONLY_WAKE,
+ BiometricUnlockController.MODE_UNLOCK_COLLAPSING,
+ BiometricUnlockController.MODE_DISMISS_BOUNCER,
+ BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM,
+ )
+ .forEach {
+ whenever(biometricUnlockController.mode).thenReturn(it)
+ captor.value.onModeChanged(it)
+ }
assertThat(values)
.isEqualTo(
@@ -420,4 +444,104 @@
job.cancel()
verify(dozeTransitionListener).removeCallback(listener)
}
+
+ @Test
+ fun fingerprintSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.fingerprintSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(Point(500, 500), Point(0, 0), null, Point(250, 250))
+ .onEach {
+ whenever(authController.fingerprintSensorLocation).thenReturn(it)
+ captor.value.onFingerprintLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun faceSensorLocation() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Point?>()
+ val job = underTest.faceSensorLocation.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<AuthController.Callback>()
+ verify(authController).addCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The sensor location is expected to be nullable, so anyone
+ // consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ Point(500, 500),
+ Point(0, 0),
+ null,
+ Point(250, 250),
+ )
+ .onEach {
+ whenever(authController.faceSensorLocation).thenReturn(it)
+ captor.value.onFaceSensorLocationChanged()
+ }
+ .also { dispatchedSensorLocations ->
+ assertThat(values).isEqualTo(listOf(null) + dispatchedSensorLocations)
+ }
+
+ job.cancel()
+ }
+
+ @Test
+ fun biometricUnlockSource() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<BiometricUnlockSource?>()
+ val job = underTest.biometricUnlockSource.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<KeyguardUpdateMonitorCallback>()
+ verify(keyguardUpdateMonitor).registerCallback(captor.capture())
+
+ // An initial, null value should be initially emitted so that flows combined with this
+ // one
+ // emit values immediately. The biometric unlock source is expected to be nullable, so
+ // anyone consuming it should handle that properly.
+ assertThat(values).isEqualTo(listOf(null))
+
+ listOf(
+ BiometricSourceType.FINGERPRINT,
+ BiometricSourceType.IRIS,
+ null,
+ BiometricSourceType.FACE,
+ BiometricSourceType.FINGERPRINT,
+ )
+ .onEach { biometricSourceType ->
+ captor.value.onBiometricAuthenticated(0, biometricSourceType, false)
+ }
+
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ null,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ BiometricUnlockSource.FACE_SENSOR,
+ null,
+ BiometricUnlockSource.FACE_SENSOR,
+ BiometricUnlockSource.FINGERPRINT_SENSOR,
+ )
+ )
+
+ job.cancel()
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
new file mode 100644
index 0000000..d2db910
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/LightRevealScrimRepositoryTest.kt
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import android.graphics.Point
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
+import com.android.systemui.statusbar.CircleReveal
+import com.android.systemui.statusbar.LightRevealEffect
+import junit.framework.Assert.assertEquals
+import junit.framework.Assert.assertFalse
+import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.runCurrent
+import kotlinx.coroutines.test.runTest
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimRepositoryTest : SysuiTestCase() {
+ private lateinit var fakeKeyguardRepository: FakeKeyguardRepository
+ private lateinit var underTest: LightRevealScrimRepositoryImpl
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ fakeKeyguardRepository = FakeKeyguardRepository()
+ underTest = LightRevealScrimRepositoryImpl(fakeKeyguardRepository, context)
+ }
+
+ @Test
+ fun `nextRevealEffect - effect switches between default and biometric with no dupes`() =
+ runTest {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = launch { underTest.revealEffect.collect { values.add(it) } }
+
+ // We should initially emit the default reveal effect.
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT })
+
+ // The source and sensor locations are still null, so we should still be using the
+ // default reveal despite a biometric unlock.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a source but still have no sensor locations, so should be sticking with
+ // the default effect.
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // We got a location for the face sensor, but we unlocked with fingerprint.
+ val faceLocation = Point(250, 0)
+ fakeKeyguardRepository.setFaceSensorLocation(faceLocation)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates({ it == DEFAULT_REVEAL_EFFECT },)
+
+ // Now we have fingerprint sensor locations, and wake and unlock via fingerprint.
+ val fingerprintLocation = Point(500, 500)
+ fakeKeyguardRepository.setFingerprintSensorLocation(fingerprintLocation)
+ fakeKeyguardRepository.setBiometricUnlockSource(
+ BiometricUnlockSource.FINGERPRINT_SENSOR
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+
+ // We should now have switched to the circle reveal, at the fingerprint location.
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ )
+
+ // Subsequent wake and unlocks should not emit duplicate, identical CircleReveals.
+ val valuesPrevSize = values.size
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ )
+ fakeKeyguardRepository.setBiometricUnlockState(
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ )
+ assertEquals(valuesPrevSize, values.size)
+
+ // Non-biometric unlock, we should return to the default reveal.
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.NONE)
+
+ runCurrent()
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ )
+
+ // We already have a face location, so switching to face source should update the
+ // CircleReveal.
+ fakeKeyguardRepository.setBiometricUnlockSource(BiometricUnlockSource.FACE_SENSOR)
+ runCurrent()
+ fakeKeyguardRepository.setBiometricUnlockState(BiometricUnlockModel.WAKE_AND_UNLOCK)
+ runCurrent()
+
+ values.assertEffectsMatchPredicates(
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == fingerprintLocation.x &&
+ it.centerY == fingerprintLocation.y
+ },
+ { it == DEFAULT_REVEAL_EFFECT },
+ {
+ it is CircleReveal &&
+ it.centerX == faceLocation.x &&
+ it.centerY == faceLocation.y
+ },
+ )
+
+ job.cancel()
+ }
+
+ /**
+ * Asserts that the list of LightRevealEffects satisfies the list of predicates, in order, with
+ * no leftover elements.
+ */
+ private fun List<LightRevealEffect>.assertEffectsMatchPredicates(
+ vararg predicates: (LightRevealEffect) -> Boolean
+ ) {
+ println(this)
+ assertEquals(predicates.size, this.size)
+
+ assertFalse(
+ zip(predicates) { effect, predicate -> predicate(effect) }.any { matched -> !matched }
+ )
+ }
+}
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
new file mode 100644
index 0000000..3166214
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LightRevealScrimInteractorTest.kt
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.data.repository.FakeLightRevealScrimRepository
+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.android.systemui.statusbar.LightRevealEffect
+import com.android.systemui.statusbar.LightRevealScrim
+import kotlinx.coroutines.flow.launchIn
+import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runTest
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.JUnit4
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(JUnit4::class)
+class LightRevealScrimInteractorTest : SysuiTestCase() {
+ private val fakeKeyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ private val fakeLightRevealScrimRepository = FakeLightRevealScrimRepository()
+
+ private val keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(fakeKeyguardTransitionRepository)
+
+ private lateinit var underTest: LightRevealScrimInteractor
+
+ private val reveal1 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ private val reveal2 =
+ object : LightRevealEffect {
+ override fun setRevealAmountOnScrim(amount: Float, scrim: LightRevealScrim) {}
+ }
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest =
+ LightRevealScrimInteractor(
+ fakeKeyguardTransitionRepository,
+ keyguardTransitionInteractor,
+ fakeLightRevealScrimRepository
+ )
+ }
+
+ @Test
+ fun `lightRevealEffect - does not change during keyguard transition`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<LightRevealEffect>()
+ val job = underTest.lightRevealEffect.onEach(values::add).launchIn(this)
+
+ fakeLightRevealScrimRepository.setRevealEffect(reveal1)
+
+ // The reveal effect shouldn't emit anything until a keyguard transition starts.
+ assertEquals(values.size, 0)
+
+ // Once it starts, it should emit reveal1.
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1))
+
+ // Until the next transition starts, reveal2 should not be emitted.
+ fakeLightRevealScrimRepository.setRevealEffect(reveal2)
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.RUNNING)
+ )
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.FINISHED)
+ )
+ assertEquals(values, listOf(reveal1))
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(transitionState = TransitionState.STARTED)
+ )
+ assertEquals(values, listOf(reveal1, reveal2))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - inverted when appropriate`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.AOD,
+ to = KeyguardState.LOCKSCREEN,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f))
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(
+ from = KeyguardState.LOCKSCREEN,
+ to = KeyguardState.AOD,
+ value = 0.3f
+ )
+ )
+
+ assertEquals(values, listOf(0.3f, 0.7f))
+
+ job.cancel()
+ }
+
+ @Test
+ fun `revealAmount - ignores transitions that do not affect reveal amount`() =
+ runTest(UnconfinedTestDispatcher()) {
+ val values = mutableListOf<Float>()
+ val job = underTest.revealAmount.onEach(values::add).launchIn(this)
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.AOD, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ fakeKeyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.AOD, to = KeyguardState.DOZING, value = 0.3f)
+ )
+
+ assertEquals(values, emptyList<Float>())
+
+ job.cancel()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
new file mode 100644
index 0000000..2d23f3c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowControllerTest.kt
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.notification.row
+
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.internal.logging.MetricsLogger
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.plugins.FalsingManager
+import com.android.systemui.plugins.PluginManager
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.NotificationMediaManager
+import com.android.systemui.statusbar.SmartReplyController
+import com.android.systemui.statusbar.notification.collection.render.GroupExpansionManager
+import com.android.systemui.statusbar.notification.collection.render.GroupMembershipManager
+import com.android.systemui.statusbar.notification.logging.NotificationLogger
+import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
+import com.android.systemui.statusbar.notification.stack.NotificationListContainer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import com.android.systemui.statusbar.policy.HeadsUpManager
+import com.android.systemui.statusbar.policy.SmartReplyConstants
+import com.android.systemui.statusbar.policy.dagger.RemoteInputViewSubcomponent
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.SystemClock
+import com.android.systemui.wmshell.BubblesManager
+import java.util.Optional
+import junit.framework.Assert
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito
+import org.mockito.Mockito.anyBoolean
+import org.mockito.Mockito.never
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper
+class ExpandableNotificationRowControllerTest : SysuiTestCase() {
+
+ private val appName = "MyApp"
+ private val notifKey = "MyNotifKey"
+
+ private val view: ExpandableNotificationRow = mock()
+ private val activableNotificationViewController: ActivatableNotificationViewController = mock()
+ private val rivSubComponentFactory: RemoteInputViewSubcomponent.Factory = mock()
+ private val metricsLogger: MetricsLogger = mock()
+ private val logBufferLogger: NotificationRowLogger = mock()
+ private val listContainer: NotificationListContainer = mock()
+ private val mediaManager: NotificationMediaManager = mock()
+ private val smartReplyConstants: SmartReplyConstants = mock()
+ private val smartReplyController: SmartReplyController = mock()
+ private val pluginManager: PluginManager = mock()
+ private val systemClock: SystemClock = mock()
+ private val keyguardBypassController: KeyguardBypassController = mock()
+ private val groupMembershipManager: GroupMembershipManager = mock()
+ private val groupExpansionManager: GroupExpansionManager = mock()
+ private val rowContentBindStage: RowContentBindStage = mock()
+ private val notifLogger: NotificationLogger = mock()
+ private val headsUpManager: HeadsUpManager = mock()
+ private val onExpandClickListener: ExpandableNotificationRow.OnExpandClickListener = mock()
+ private val statusBarStateController: StatusBarStateController = mock()
+ private val gutsManager: NotificationGutsManager = mock()
+ private val onUserInteractionCallback: OnUserInteractionCallback = mock()
+ private val falsingManager: FalsingManager = mock()
+ private val falsingCollector: FalsingCollector = mock()
+ private val featureFlags: FeatureFlags = mock()
+ private val peopleNotificationIdentifier: PeopleNotificationIdentifier = mock()
+ private val bubblesManager: BubblesManager = mock()
+ private val dragController: ExpandableNotificationRowDragController = mock()
+ private lateinit var controller: ExpandableNotificationRowController
+
+ @Before
+ fun setUp() {
+ allowTestableLooperAsMainThread()
+ controller =
+ ExpandableNotificationRowController(
+ view,
+ activableNotificationViewController,
+ rivSubComponentFactory,
+ metricsLogger,
+ logBufferLogger,
+ listContainer,
+ mediaManager,
+ smartReplyConstants,
+ smartReplyController,
+ pluginManager,
+ systemClock,
+ appName,
+ notifKey,
+ keyguardBypassController,
+ groupMembershipManager,
+ groupExpansionManager,
+ rowContentBindStage,
+ notifLogger,
+ headsUpManager,
+ onExpandClickListener,
+ statusBarStateController,
+ gutsManager,
+ /*allowLongPress=*/ false,
+ onUserInteractionCallback,
+ falsingManager,
+ falsingCollector,
+ featureFlags,
+ peopleNotificationIdentifier,
+ Optional.of(bubblesManager),
+ dragController
+ )
+ }
+
+ @After
+ fun tearDown() {
+ disallowTestableLooperAsMainThread()
+ }
+
+ @Test
+ fun offerKeepInParent_parentDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+ whenever(view.isParentDismissed).thenReturn(true)
+
+ Assert.assertTrue(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view).setKeepInParentForDismissAnimation(true)
+ }
+
+ @Test
+ fun offerKeepInParent_parentNotDismissed() {
+ whenever(featureFlags.isEnabled(Flags.NOTIFICATION_GROUP_DISMISSAL_ANIMATION))
+ .thenReturn(true)
+
+ Assert.assertFalse(controller.offerToKeepInParentForAnimation())
+ Mockito.verify(view, never()).setKeepInParentForDismissAnimation(anyBoolean())
+ }
+
+ @Test
+ fun removeFromParent_keptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+ whenever(view.keepInParentForDismissAnimation()).thenReturn(true)
+
+ Assert.assertTrue(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verify(parentView).removeChildNotification(view)
+ }
+
+ @Test
+ fun removeFromParent_notKeptForAnimation() {
+ val parentView: ExpandableNotificationRow = mock()
+ whenever(view.notificationParent).thenReturn(parentView)
+
+ Assert.assertFalse(controller.removeFromParentIfKeptForAnimation())
+ Mockito.verifyNoMoreInteractions(parentView)
+ }
+}
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 ed84e42..521e518 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
@@ -106,6 +106,7 @@
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
+import com.android.systemui.keyguard.ui.viewmodel.LightRevealScrimViewModel;
import com.android.systemui.navigationbar.NavigationBarController;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;
import com.android.systemui.plugins.PluginDependencyProvider;
@@ -212,6 +213,7 @@
@Mock private NotificationPanelView mNotificationPanelView;
@Mock private IStatusBarService mBarService;
@Mock private IDreamManager mDreamManager;
+ @Mock private LightRevealScrimViewModel mLightRevealScrimViewModel;
@Mock private ScrimController mScrimController;
@Mock private DozeScrimController mDozeScrimController;
@Mock private Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
@@ -497,7 +499,8 @@
mDeviceStateManager,
mWiredChargingRippleController,
mDreamManager,
- mCameraLauncherLazy) {
+ mCameraLauncherLazy,
+ () -> mLightRevealScrimViewModel) {
@Override
protected ViewRootImpl getViewRootImpl() {
return mViewRootImpl;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
similarity index 95%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
index 76016a1..5a6bb30 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/airplane/ui/viewmodel/AirplaneModeViewModelImplTest.kt
@@ -38,9 +38,9 @@
@SmallTest
@OptIn(ExperimentalCoroutinesApi::class)
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-class AirplaneModeViewModelTest : SysuiTestCase() {
+class AirplaneModeViewModelImplTest : SysuiTestCase() {
- private lateinit var underTest: AirplaneModeViewModel
+ private lateinit var underTest: AirplaneModeViewModelImpl
@Mock private lateinit var logger: ConnectivityPipelineLogger
private lateinit var airplaneModeRepository: FakeAirplaneModeRepository
@@ -57,7 +57,7 @@
scope = CoroutineScope(IMMEDIATE)
underTest =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
interactor,
logger,
scope,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
similarity index 97%
rename from packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
rename to packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
index 71b8bab..b38497a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorImplTest.kt
@@ -35,8 +35,9 @@
import org.junit.Test
@OptIn(ExperimentalCoroutinesApi::class)
+@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
@SmallTest
-class WifiInteractorTest : SysuiTestCase() {
+class WifiInteractorImplTest : SysuiTestCase() {
private lateinit var underTest: WifiInteractor
@@ -47,7 +48,7 @@
fun setUp() {
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
- underTest = WifiInteractor(connectivityRepository, wifiRepository)
+ underTest = WifiInteractorImpl(connectivityRepository, wifiRepository)
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 37457b3..5c16e129 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -32,12 +32,14 @@
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWifiViewModel
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
@@ -86,9 +88,9 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(Dispatchers.Unconfined)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index a1afcd7..3001b81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -30,6 +30,7 @@
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -37,6 +38,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel.Companion.NO_INTERNET
import com.google.common.truth.Truth.assertThat
@@ -81,10 +83,10 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
airplaneModeViewModel =
- AirplaneModeViewModel(
+ AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 7d2c560..6a6b2a8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModelImpl
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
@@ -30,6 +31,7 @@
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
+import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractorImpl
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiActivityModel
import com.google.common.truth.Truth.assertThat
@@ -73,9 +75,9 @@
connectivityRepository = FakeConnectivityRepository()
wifiRepository = FakeWifiRepository()
wifiRepository.setIsWifiEnabled(true)
- interactor = WifiInteractor(connectivityRepository, wifiRepository)
+ interactor = WifiInteractorImpl(connectivityRepository, wifiRepository)
scope = CoroutineScope(IMMEDIATE)
- airplaneModeViewModel = AirplaneModeViewModel(
+ airplaneModeViewModel = AirplaneModeViewModelImpl(
AirplaneModeInteractor(
airplaneModeRepository,
connectivityRepository,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
index 4b6bdac..784a26b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/user/ui/viewmodel/UserSwitcherViewModelTest.kt
@@ -47,18 +47,14 @@
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
-import kotlinx.coroutines.Job
-import kotlinx.coroutines.SupervisorJob
-import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.flow.toList
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestDispatcher
-import kotlinx.coroutines.test.TestResult
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
+import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -89,7 +85,6 @@
private lateinit var testDispatcher: TestDispatcher
private lateinit var testScope: TestScope
- private lateinit var injectedScope: CoroutineScope
@Before
fun setUp() {
@@ -104,7 +99,6 @@
testDispatcher = UnconfinedTestDispatcher()
testScope = TestScope(testDispatcher)
- injectedScope = CoroutineScope(testScope.coroutineContext + SupervisorJob())
userRepository = FakeUserRepository()
runBlocking {
userRepository.setSettings(
@@ -118,14 +112,14 @@
powerRepository = FakePowerRepository()
val refreshUsersScheduler =
RefreshUsersScheduler(
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
repository = userRepository,
)
val guestUserInteractor =
GuestUserInteractor(
applicationContext = context,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
mainDispatcher = testDispatcher,
backgroundDispatcher = testDispatcher,
manager = manager,
@@ -154,7 +148,7 @@
set(Flags.FULL_SCREEN_USER_SWITCHER, false)
},
manager = manager,
- applicationScope = injectedScope,
+ applicationScope = testScope.backgroundScope,
telephonyInteractor =
TelephonyInteractor(
repository = FakeTelephonyRepository(),
@@ -175,7 +169,7 @@
}
@Test
- fun users() = selfCancelingTest {
+ fun users() = testScope.runTest {
val userInfos =
listOf(
UserInfo(
@@ -210,26 +204,26 @@
assertUserViewModel(
viewModel = userViewModels.last()[0],
viewKey = 0,
- name = "zero",
+ name = Text.Loaded("zero"),
isSelectionMarkerVisible = true,
)
assertUserViewModel(
viewModel = userViewModels.last()[1],
viewKey = 1,
- name = "one",
+ name = Text.Loaded("one"),
isSelectionMarkerVisible = false,
)
assertUserViewModel(
viewModel = userViewModels.last()[2],
viewKey = 2,
- name = "two",
+ name = Text.Loaded("two"),
isSelectionMarkerVisible = false,
)
job.cancel()
}
@Test
- fun `maximumUserColumns - few users`() = selfCancelingTest {
+ fun `maximumUserColumns - few users`() = testScope.runTest {
setUsers(count = 2)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -240,7 +234,7 @@
}
@Test
- fun `maximumUserColumns - many users`() = selfCancelingTest {
+ fun `maximumUserColumns - many users`() = testScope.runTest {
setUsers(count = 5)
val values = mutableListOf<Int>()
val job = launch(testDispatcher) { underTest.maximumUserColumns.toList(values) }
@@ -250,7 +244,7 @@
}
@Test
- fun `isOpenMenuButtonVisible - has actions - true`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - has actions - true`() = testScope.runTest {
setUsers(2)
val isVisible = mutableListOf<Boolean>()
@@ -261,7 +255,7 @@
}
@Test
- fun `isOpenMenuButtonVisible - no actions - false`() = selfCancelingTest {
+ fun `isOpenMenuButtonVisible - no actions - false`() = testScope.runTest {
val userInfos = setUsers(2)
userRepository.setSelectedUserInfo(userInfos[1])
keyguardRepository.setKeyguardShowing(true)
@@ -275,7 +269,7 @@
}
@Test
- fun menu() = selfCancelingTest {
+ fun menu() = testScope.runTest {
val isMenuVisible = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isMenuVisible.toList(isMenuVisible) }
assertThat(isMenuVisible.last()).isFalse()
@@ -290,7 +284,7 @@
}
@Test
- fun `menu actions`() = selfCancelingTest {
+ fun `menu actions`() = testScope.runTest {
setUsers(2)
val actions = mutableListOf<List<UserActionViewModel>>()
val job = launch(testDispatcher) { underTest.menu.toList(actions) }
@@ -309,7 +303,7 @@
}
@Test
- fun `isFinishRequested - finishes when user is switched`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when user is switched`() = testScope.runTest {
val userInfos = setUsers(count = 2)
val isFinishRequested = mutableListOf<Boolean>()
val job = launch(testDispatcher) { underTest.isFinishRequested.toList(isFinishRequested) }
@@ -323,7 +317,7 @@
}
@Test
- fun `isFinishRequested - finishes when the screen turns off`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when the screen turns off`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -338,7 +332,7 @@
}
@Test
- fun `isFinishRequested - finishes when cancel button is clicked`() = selfCancelingTest {
+ fun `isFinishRequested - finishes when cancel button is clicked`() = testScope.runTest {
setUsers(count = 2)
powerRepository.setInteractive(true)
val isFinishRequested = mutableListOf<Boolean>()
@@ -356,6 +350,93 @@
job.cancel()
}
+ @Test
+ fun `guest selected -- name is exit guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[1])
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = false,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Resource(
+ com.android.settingslib.R.string.guest_exit_quick_settings_button
+ ),
+ isSelectionMarkerVisible = true,
+ )
+ job.cancel()
+ }
+
+ @Test
+ fun `guest not selected -- name is guest`() = testScope.runTest {
+ val userInfos =
+ listOf(
+ UserInfo(
+ /* id= */ 0,
+ /* name= */ "zero",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_PRIMARY or UserInfo.FLAG_ADMIN or UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_SYSTEM,
+ ),
+ UserInfo(
+ /* id= */ 1,
+ /* name= */ "one",
+ /* iconPath= */ "",
+ /* flags= */ UserInfo.FLAG_FULL,
+ UserManager.USER_TYPE_FULL_GUEST,
+ ),
+ )
+
+ userRepository.setUserInfos(userInfos)
+ userRepository.setSelectedUserInfo(userInfos[0])
+ runCurrent()
+
+ val userViewModels = mutableListOf<List<UserViewModel>>()
+ val job = launch(testDispatcher) { underTest.users.toList(userViewModels) }
+
+ assertThat(userViewModels.last()).hasSize(2)
+ assertUserViewModel(
+ viewModel = userViewModels.last()[0],
+ viewKey = 0,
+ name = Text.Loaded("zero"),
+ isSelectionMarkerVisible = true,
+ )
+ assertUserViewModel(
+ viewModel = userViewModels.last()[1],
+ viewKey = 1,
+ name = Text.Loaded("one"),
+ isSelectionMarkerVisible = false,
+ )
+ job.cancel()
+ }
+
private suspend fun setUsers(count: Int): List<UserInfo> {
val userInfos =
(0 until count).map { index ->
@@ -384,26 +465,18 @@
private fun assertUserViewModel(
viewModel: UserViewModel?,
viewKey: Int,
- name: String,
+ name: Text,
isSelectionMarkerVisible: Boolean,
) {
checkNotNull(viewModel)
assertThat(viewModel.viewKey).isEqualTo(viewKey)
- assertThat(viewModel.name).isEqualTo(Text.Loaded(name))
+ assertThat(viewModel.name).isEqualTo(name)
assertThat(viewModel.isSelectionMarkerVisible).isEqualTo(isSelectionMarkerVisible)
assertThat(viewModel.alpha)
.isEqualTo(LegacyUserUiHelper.USER_SWITCHER_USER_VIEW_SELECTABLE_ALPHA)
assertThat(viewModel.onClicked).isNotNull()
}
- private fun selfCancelingTest(
- block: suspend TestScope.() -> Unit,
- ): TestResult =
- testScope.runTest {
- block()
- injectedScope.coroutineContext[Job.Key]?.cancelAndJoin()
- }
-
companion object {
private const val SUPERVISED_USER_CREATION_PACKAGE = "com.some.package"
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
index fa3cc99..bf2235a 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/SysuiTestCase.java
@@ -136,6 +136,8 @@
InstrumentationRegistry.getArguments());
if (TestableLooper.get(this) != null) {
TestableLooper.get(this).processAllMessages();
+ // Must remove static reference to this test object to prevent leak (b/261039202)
+ TestableLooper.remove(this);
}
disallowTestableLooperAsMainThread();
mContext.cleanUpReceivers(this.getClass().getSimpleName());
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
index 52e0c982..ad086ff 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/broadcast/FakeBroadcastDispatcher.kt
@@ -22,12 +22,12 @@
import android.os.Handler
import android.os.Looper
import android.os.UserHandle
-import android.util.ArraySet
import android.util.Log
import com.android.systemui.SysuiTestableContext
import com.android.systemui.broadcast.logging.BroadcastDispatcherLogger
import com.android.systemui.dump.DumpManager
import com.android.systemui.settings.UserTracker
+import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.Executor
class FakeBroadcastDispatcher(
@@ -50,7 +50,7 @@
PendingRemovalStore(logger)
) {
- val registeredReceivers = ArraySet<BroadcastReceiver>()
+ val registeredReceivers: MutableSet<BroadcastReceiver> = ConcurrentHashMap.newKeySet()
override fun registerReceiverWithHandler(
receiver: BroadcastReceiver,
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 3601667..5c2a915 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -17,11 +17,15 @@
package com.android.systemui.keyguard.data.repository
+import android.graphics.Point
import com.android.systemui.common.shared.model.Position
import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.BiometricUnlockSource
import com.android.systemui.keyguard.shared.model.DozeTransitionModel
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 kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -49,7 +53,7 @@
override val isDreaming: Flow<Boolean> = _isDreaming
private val _dozeAmount = MutableStateFlow(0f)
- override val dozeAmount: Flow<Float> = _dozeAmount
+ override val linearDozeAmount: Flow<Float> = _dozeAmount
private val _statusBarState = MutableStateFlow(StatusBarState.SHADE)
override val statusBarState: Flow<StatusBarState> = _statusBarState
@@ -57,8 +61,16 @@
private val _dozeTransitionModel = MutableStateFlow(DozeTransitionModel())
override val dozeTransitionModel: Flow<DozeTransitionModel> = _dozeTransitionModel
- private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
- override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _wakefulnessModel =
+ MutableStateFlow(
+ WakefulnessModel(
+ WakefulnessState.ASLEEP,
+ false,
+ WakeSleepReason.OTHER,
+ WakeSleepReason.OTHER
+ )
+ )
+ override val wakefulness: Flow<WakefulnessModel> = _wakefulnessModel
private val _isUdfpsSupported = MutableStateFlow(false)
@@ -71,6 +83,15 @@
private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
+ private val _fingerprintSensorLocation = MutableStateFlow<Point?>(null)
+ override val fingerprintSensorLocation: Flow<Point?> = _fingerprintSensorLocation
+
+ private val _faceSensorLocation = MutableStateFlow<Point?>(null)
+ override val faceSensorLocation: Flow<Point?> = _faceSensorLocation
+
+ private val _biometricUnlockSource = MutableStateFlow<BiometricUnlockSource?>(null)
+ override val biometricUnlockSource: Flow<BiometricUnlockSource?> = _biometricUnlockSource
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
@@ -99,6 +120,22 @@
_dozeAmount.value = dozeAmount
}
+ fun setBiometricUnlockState(state: BiometricUnlockModel) {
+ _biometricUnlockState.tryEmit(state)
+ }
+
+ fun setBiometricUnlockSource(source: BiometricUnlockSource?) {
+ _biometricUnlockSource.tryEmit(source)
+ }
+
+ fun setFaceSensorLocation(location: Point?) {
+ _faceSensorLocation.tryEmit(location)
+ }
+
+ fun setFingerprintSensorLocation(location: Point?) {
+ _fingerprintSensorLocation.tryEmit(location)
+ }
+
override fun isUdfpsSupported(): Boolean {
return _isUdfpsSupported.value
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
new file mode 100644
index 0000000..7c22604
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeLightRevealScrimRepository.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.keyguard.data.repository
+
+import com.android.systemui.statusbar.LightRevealEffect
+import kotlinx.coroutines.flow.MutableStateFlow
+
+/** Fake implementation of [LightRevealScrimRepository] */
+class FakeLightRevealScrimRepository : LightRevealScrimRepository {
+
+ private val _revealEffect: MutableStateFlow<LightRevealEffect> =
+ MutableStateFlow(DEFAULT_REVEAL_EFFECT)
+ override val revealEffect = _revealEffect
+
+ fun setRevealEffect(effect: LightRevealEffect) {
+ _revealEffect.tryEmit(effect)
+ }
+}
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 2814196..f1ba5ff 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -355,10 +355,10 @@
private boolean activityMatchesDisplayCategory(ActivityInfo activityInfo) {
if (mDisplayCategories.isEmpty()) {
- return activityInfo.targetDisplayCategory == null;
+ return activityInfo.requiredDisplayCategory == null;
}
- return activityInfo.targetDisplayCategory != null
- && mDisplayCategories.contains(activityInfo.targetDisplayCategory);
+ return activityInfo.requiredDisplayCategory != null
+ && mDisplayCategories.contains(activityInfo.requiredDisplayCategory);
}
@@ -375,9 +375,9 @@
}
if (!activityMatchesDisplayCategory(activityInfo)) {
Slog.d(TAG, String.format(
- "The activity's target display category: %s is not found on virtual display"
- + " with the following allowed display categories: %s",
- activityInfo.targetDisplayCategory, mDisplayCategories.toString()));
+ "The activity's required display category: %s is not found on virtual display"
+ + " with the following categories: %s",
+ activityInfo.requiredDisplayCategory, mDisplayCategories.toString()));
return false;
}
final UserHandle activityUser =
diff --git a/services/core/java/com/android/server/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 551ffff..84c033c 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -341,7 +341,8 @@
// non-proto tombstones, even though proto tombstones do not support including the counter
// of events dropped since rate limiting activated yet.
DropboxRateLimiter.RateLimitResult rateLimitResult =
- sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE, processName);
+ sDropboxRateLimiter.shouldRateLimit(
+ proto ? TAG_TOMBSTONE_PROTO : TAG_TOMBSTONE, processName);
if (rateLimitResult.shouldRateLimit()) return;
HashMap<String, Long> timestamps = readTimestamps();
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index 2ea49b3..af0bd63 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -1769,7 +1769,7 @@
if (foreground) {
t.traceBegin("moveUserToForeground");
- moveUserToForeground(uss, oldUserId, userId);
+ moveUserToForeground(uss, userId);
t.traceEnd();
} else {
t.traceBegin("finishUserBoot");
@@ -1983,21 +1983,25 @@
/** Called on handler thread */
@VisibleForTesting
- void dispatchUserSwitchComplete(@UserIdInt int userId) {
+ void dispatchUserSwitchComplete(@UserIdInt int oldUserId, @UserIdInt int newUserId) {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
- t.traceBegin("dispatchUserSwitchComplete-" + userId);
+ t.traceBegin("dispatchUserSwitchComplete-" + newUserId);
mInjector.getWindowManager().setSwitchingUser(false);
final int observerCount = mUserSwitchObservers.beginBroadcast();
for (int i = 0; i < observerCount; i++) {
try {
- t.traceBegin("onUserSwitchComplete-" + userId + " #" + i + " "
+ t.traceBegin("onUserSwitchComplete-" + newUserId + " #" + i + " "
+ mUserSwitchObservers.getBroadcastCookie(i));
- mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(userId);
+ mUserSwitchObservers.getBroadcastItem(i).onUserSwitchComplete(newUserId);
t.traceEnd();
} catch (RemoteException e) {
+ // Ignore
}
}
mUserSwitchObservers.finishBroadcast();
+ t.traceBegin("sendUserSwitchBroadcasts-" + oldUserId + "-" + newUserId);
+ sendUserSwitchBroadcasts(oldUserId, newUserId);
+ t.traceEnd();
t.traceEnd();
}
@@ -2159,7 +2163,8 @@
// Do the keyguard dismiss and unfreeze later
mHandler.removeMessages(COMPLETE_USER_SWITCH_MSG);
- mHandler.sendMessage(mHandler.obtainMessage(COMPLETE_USER_SWITCH_MSG, newUserId, 0));
+ mHandler.sendMessage(mHandler.obtainMessage(
+ COMPLETE_USER_SWITCH_MSG, oldUserId, newUserId));
uss.switching = false;
stopGuestOrEphemeralUserIfBackground(oldUserId);
@@ -2169,7 +2174,7 @@
}
@VisibleForTesting
- void completeUserSwitch(int newUserId) {
+ void completeUserSwitch(int oldUserId, int newUserId) {
final boolean isUserSwitchUiEnabled = isUserSwitchUiEnabled();
final Runnable runnable = () -> {
if (isUserSwitchUiEnabled) {
@@ -2177,7 +2182,7 @@
}
mHandler.removeMessages(REPORT_USER_SWITCH_COMPLETE_MSG);
mHandler.sendMessage(mHandler.obtainMessage(
- REPORT_USER_SWITCH_COMPLETE_MSG, newUserId, 0));
+ REPORT_USER_SWITCH_COMPLETE_MSG, oldUserId, newUserId));
};
// If there is no challenge set, dismiss the keyguard right away
@@ -2202,7 +2207,7 @@
t.traceEnd();
}
- private void moveUserToForeground(UserState uss, int oldUserId, int newUserId) {
+ private void moveUserToForeground(UserState uss, int newUserId) {
boolean homeInFront = mInjector.taskSupervisorSwitchUser(newUserId, uss);
if (homeInFront) {
mInjector.startHomeActivity(newUserId, "moveUserToForeground");
@@ -2210,7 +2215,6 @@
mInjector.taskSupervisorResumeFocusedStackTopActivity();
}
EventLogTags.writeAmSwitchUser(newUserId);
- sendUserSwitchBroadcasts(oldUserId, newUserId);
}
void sendUserSwitchBroadcasts(int oldUserId, int newUserId) {
@@ -3080,9 +3084,8 @@
dispatchForegroundProfileChanged(msg.arg1);
break;
case REPORT_USER_SWITCH_COMPLETE_MSG:
- dispatchUserSwitchComplete(msg.arg1);
-
- logUserLifecycleEvent(msg.arg1, USER_LIFECYCLE_EVENT_SWITCH_USER,
+ dispatchUserSwitchComplete(msg.arg1, msg.arg2);
+ logUserLifecycleEvent(msg.arg2, USER_LIFECYCLE_EVENT_SWITCH_USER,
USER_LIFECYCLE_EVENT_STATE_FINISH);
break;
case REPORT_LOCKED_BOOT_COMPLETE_MSG:
@@ -3100,7 +3103,7 @@
logAndClearSessionId(msg.arg1);
break;
case COMPLETE_USER_SWITCH_MSG:
- completeUserSwitch(msg.arg1);
+ completeUserSwitch(msg.arg1, msg.arg2);
break;
}
return false;
diff --git a/services/core/java/com/android/server/display/DisplayModeDirector.java b/services/core/java/com/android/server/display/DisplayModeDirector.java
index 40e7c50..1c510cc 100644
--- a/services/core/java/com/android/server/display/DisplayModeDirector.java
+++ b/services/core/java/com/android/server/display/DisplayModeDirector.java
@@ -49,7 +49,7 @@
import android.provider.DeviceConfig;
import android.provider.DeviceConfigInterface;
import android.provider.Settings;
-import android.sysprop.DisplayProperties;
+import android.sysprop.SurfaceFlingerProperties;
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -138,8 +138,7 @@
private boolean mAlwaysRespectAppRequest;
- // TODO(b/241447632): remove the flag once SF changes are ready
- private final boolean mRenderFrameRateIsPhysicalRefreshRate;
+ private final boolean mSupportsFrameRateOverride;
/**
* The allowed refresh rate switching type. This is used by SurfaceFlinger.
@@ -176,7 +175,7 @@
mHbmObserver = new HbmObserver(injector, ballotBox, BackgroundThread.getHandler(),
mDeviceConfigDisplaySettings);
mAlwaysRespectAppRequest = false;
- mRenderFrameRateIsPhysicalRefreshRate = injector.renderFrameRateIsPhysicalRefreshRate();
+ mSupportsFrameRateOverride = injector.supportsFrameRateOverride();
}
/**
@@ -238,21 +237,6 @@
}
}
- if (mRenderFrameRateIsPhysicalRefreshRate) {
- for (int i = 0; i < votes.size(); i++) {
-
- Vote vote = votes.valueAt(i);
- vote.refreshRateRanges.physical.min = Math.max(vote.refreshRateRanges.physical.min,
- vote.refreshRateRanges.render.min);
- vote.refreshRateRanges.physical.max = Math.min(vote.refreshRateRanges.physical.max,
- vote.refreshRateRanges.render.max);
- vote.refreshRateRanges.render.min = Math.max(vote.refreshRateRanges.physical.min,
- vote.refreshRateRanges.render.min);
- vote.refreshRateRanges.render.max = Math.min(vote.refreshRateRanges.physical.max,
- vote.refreshRateRanges.render.max);
- }
- }
-
return votes;
}
@@ -541,8 +525,7 @@
}
}
- if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE
- || mRenderFrameRateIsPhysicalRefreshRate) {
+ if (mModeSwitchingType == DisplayManager.SWITCHING_TYPE_NONE) {
primarySummary.minRenderFrameRate = primarySummary.minPhysicalRefreshRate;
primarySummary.maxRenderFrameRate = primarySummary.maxPhysicalRefreshRate;
appRequestSummary.minRenderFrameRate = appRequestSummary.minPhysicalRefreshRate;
@@ -612,6 +595,22 @@
continue;
}
+ // The physical refresh rate must be in the render frame rate range, unless
+ // frame rate override is supported.
+ if (!mSupportsFrameRateOverride) {
+ if (physicalRefreshRate < (summary.minRenderFrameRate - FLOAT_TOLERANCE)
+ || physicalRefreshRate > (summary.maxRenderFrameRate + FLOAT_TOLERANCE)) {
+ if (mLoggingEnabled) {
+ Slog.w(TAG, "Discarding mode " + mode.getModeId()
+ + ", outside render rate bounds"
+ + ": minPhysicalRefreshRate=" + summary.minPhysicalRefreshRate
+ + ", maxPhysicalRefreshRate=" + summary.maxPhysicalRefreshRate
+ + ", modeRefreshRate=" + physicalRefreshRate);
+ }
+ continue;
+ }
+ }
+
// Check whether the render frame rate range is achievable by the mode's physical
// refresh rate, meaning that if a divisor of the physical refresh rate is in range
// of the render frame rate.
@@ -2979,7 +2978,7 @@
IThermalService getThermalService();
- boolean renderFrameRateIsPhysicalRefreshRate();
+ boolean supportsFrameRateOverride();
}
@VisibleForTesting
@@ -3034,9 +3033,11 @@
}
@Override
- public boolean renderFrameRateIsPhysicalRefreshRate() {
- return DisplayProperties
- .debug_render_frame_rate_is_physical_refresh_rate().orElse(true);
+ public boolean supportsFrameRateOverride() {
+ return SurfaceFlingerProperties.enable_frame_rate_override().orElse(false)
+ && !SurfaceFlingerProperties.frame_rate_override_for_native_rates()
+ .orElse(true)
+ && SurfaceFlingerProperties.frame_rate_override_global().orElse(false);
}
private DisplayManager getDisplayManager() {
diff --git a/services/core/java/com/android/server/location/contexthub/ContextHubService.java b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
index 90245b5e..7f6c2d6 100644
--- a/services/core/java/com/android/server/location/contexthub/ContextHubService.java
+++ b/services/core/java/com/android/server/location/contexthub/ContextHubService.java
@@ -73,6 +73,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledThreadPoolExecutor;
@@ -147,7 +148,6 @@
private final ScheduledThreadPoolExecutor mDailyMetricTimer =
new ScheduledThreadPoolExecutor(1);
-
// The period of the recurring time
private static final int PERIOD_METRIC_QUERY_DAYS = 1;
@@ -363,11 +363,13 @@
*/
private void initDefaultClientMap() {
HashMap<Integer, IContextHubClient> defaultClientMap = new HashMap<>();
- for (int contextHubId : mContextHubIdToInfoMap.keySet()) {
+ for (Map.Entry<Integer, ContextHubInfo> entry: mContextHubIdToInfoMap.entrySet()) {
+ int contextHubId = entry.getKey();
+ ContextHubInfo contextHubInfo = entry.getValue();
+
mLastRestartTimestampMap.put(contextHubId,
new AtomicLong(SystemClock.elapsedRealtimeNanos()));
- ContextHubInfo contextHubInfo = mContextHubIdToInfoMap.get(contextHubId);
IContextHubClient client = mClientManager.registerClient(
contextHubInfo, createDefaultClientCallback(contextHubId),
/* attributionTag= */ null, mTransactionManager, mContext.getPackageName());
@@ -1133,6 +1135,26 @@
mTransactionManager.addTransaction(transaction);
}
+ @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_CONTEXT_HUB)
+ /**
+ * Queries for a list of preloaded nanoapp IDs from the specified Context Hub.
+ *
+ * @param hubInfo The Context Hub to query a list of nanoapps from.
+ * @return The list of 64-bit IDs of the preloaded nanoapps.
+ * @throws NullPointerException if hubInfo is null
+ */
+ @Override
+ public long[] getPreloadedNanoAppIds(ContextHubInfo hubInfo) throws RemoteException {
+ super.getPreloadedNanoAppIds_enforcePermission();
+ Objects.requireNonNull(hubInfo, "hubInfo cannot be null");
+
+ long[] nanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+ if (nanoappIds == null) {
+ return new long[0];
+ }
+ return nanoappIds;
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
@@ -1160,6 +1182,10 @@
mNanoAppStateManager.foreachNanoAppInstanceInfo((info) -> pw.println(info));
pw.println("");
+ pw.println("=================== PRELOADED NANOAPPS ====================");
+ dumpPreloadedNanoapps(pw);
+
+ pw.println("");
pw.println("=================== CLIENTS ====================");
pw.println(mClientManager);
@@ -1201,6 +1227,21 @@
proto.flush();
}
+ /**
+ * Dumps preloaded nanoapps to the console
+ */
+ private void dumpPreloadedNanoapps(PrintWriter pw) {
+ if (mContextHubWrapper == null) {
+ return;
+ }
+
+ long[] preloadedNanoappIds = mContextHubWrapper.getPreloadedNanoappIds();
+ for (long preloadedNanoappId: preloadedNanoappIds) {
+ pw.print("ID: 0x");
+ pw.println(Long.toHexString(preloadedNanoappId));
+ }
+ }
+
private void checkPermissions() {
ContextHubServiceUtil.checkPermissions(mContext);
}
diff --git a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 48152b4..f55ae6e 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -359,6 +359,14 @@
public abstract int queryNanoapps(int contextHubId) throws RemoteException;
/**
+ * Provides the list of preloaded nanoapp IDs on the system. The output of this API must
+ * not change.
+ *
+ * @return The list of preloaded nanoapp IDs
+ */
+ public abstract long[] getPreloadedNanoappIds();
+
+ /**
* Registers a callback with the Context Hub.
*
* @param contextHubId The ID of the Context Hub to register the callback with.
@@ -683,6 +691,20 @@
}
}
+ public long[] getPreloadedNanoappIds() {
+ android.hardware.contexthub.IContextHub hub = getHub();
+ if (hub == null) {
+ return null;
+ }
+
+ try {
+ return hub.getPreloadedNanoappIds();
+ } catch (RemoteException e) {
+ Log.e(TAG, "Exception while getting preloaded nanoapp IDs: " + e.getMessage());
+ return null;
+ }
+ }
+
public void registerExistingCallback(int contextHubId) {
android.hardware.contexthub.IContextHub hub = getHub();
if (hub == null) {
@@ -863,6 +885,10 @@
mHub.queryApps(contextHubId));
}
+ public long[] getPreloadedNanoappIds() {
+ return new long[0];
+ }
+
public void registerCallback(int contextHubId, ICallback callback) throws RemoteException {
mHidlCallbackMap.put(contextHubId,
new ContextHubWrapperHidlCallback(contextHubId, callback));
diff --git a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
index 558202b..a3fa25d 100644
--- a/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
+++ b/services/core/java/com/android/server/pm/parsing/PackageInfoUtils.java
@@ -533,7 +533,7 @@
ai.metaData = null;
}
ai.applicationInfo = applicationInfo;
- ai.targetDisplayCategory = a.getTargetDisplayCategory();
+ ai.requiredDisplayCategory = a.getRequiredDisplayCategory();
ai.setKnownActivityEmbeddingCerts(a.getKnownActivityEmbeddingCerts());
assignFieldsComponentInfoParsedMainComponent(ai, a, pkgSetting, userId);
return ai;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
index e019215..1826f7a 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivity.java
@@ -98,8 +98,8 @@
boolean isSupportsSizeChanges();
/**
- * Gets the category of the target display this activity is supposed to run on.
+ * Gets the required category of the display this activity is supposed to run on.
*/
@Nullable
- String getTargetDisplayCategory();
+ String getRequiredDisplayCategory();
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
index 278e547..68d5428 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java
@@ -97,7 +97,7 @@
private ActivityInfo.WindowLayout windowLayout;
@Nullable
- private String mTargetDisplayCategory;
+ private String mRequiredDisplayCategory;
public ParsedActivityImpl(ParsedActivityImpl other) {
super(other);
@@ -125,7 +125,7 @@
this.colorMode = other.colorMode;
this.windowLayout = other.windowLayout;
this.mKnownActivityEmbeddingCerts = other.mKnownActivityEmbeddingCerts;
- this.mTargetDisplayCategory = other.mTargetDisplayCategory;
+ this.mRequiredDisplayCategory = other.mRequiredDisplayCategory;
}
/**
@@ -193,7 +193,7 @@
alias.requestedVrComponent = target.getRequestedVrComponent();
alias.setDirectBootAware(target.isDirectBootAware());
alias.setProcessName(target.getProcessName());
- alias.setTargetDisplayCategory(target.getTargetDisplayCategory());
+ alias.setRequiredDisplayCategory(target.getRequiredDisplayCategory());
return alias;
// Not all attributes from the target ParsedActivity are copied to the alias.
@@ -321,7 +321,7 @@
dest.writeBoolean(false);
}
sForStringSet.parcel(this.mKnownActivityEmbeddingCerts, dest, flags);
- dest.writeString8(this.mTargetDisplayCategory);
+ dest.writeString8(this.mRequiredDisplayCategory);
}
public ParsedActivityImpl() {
@@ -356,7 +356,7 @@
windowLayout = new ActivityInfo.WindowLayout(in);
}
this.mKnownActivityEmbeddingCerts = sForStringSet.unparcel(in);
- this.mTargetDisplayCategory = in.readString8();
+ this.mRequiredDisplayCategory = in.readString8();
}
@NonNull
@@ -414,7 +414,7 @@
int rotationAnimation,
int colorMode,
@Nullable ActivityInfo.WindowLayout windowLayout,
- @Nullable String targetDisplayCategory) {
+ @Nullable String requiredDisplayCategory) {
this.theme = theme;
this.uiOptions = uiOptions;
this.targetActivity = targetActivity;
@@ -439,7 +439,7 @@
this.rotationAnimation = rotationAnimation;
this.colorMode = colorMode;
this.windowLayout = windowLayout;
- this.mTargetDisplayCategory = targetDisplayCategory;
+ this.mRequiredDisplayCategory = requiredDisplayCategory;
// onConstructed(); // You can define this method to get a callback
}
@@ -560,8 +560,8 @@
}
@DataClass.Generated.Member
- public @Nullable String getTargetDisplayCategory() {
- return mTargetDisplayCategory;
+ public @Nullable String getRequiredDisplayCategory() {
+ return mRequiredDisplayCategory;
}
@DataClass.Generated.Member
@@ -691,16 +691,16 @@
}
@DataClass.Generated.Member
- public @NonNull ParsedActivityImpl setTargetDisplayCategory(@NonNull String value) {
- mTargetDisplayCategory = value;
+ public @NonNull ParsedActivityImpl setRequiredDisplayCategory(@NonNull String value) {
+ mRequiredDisplayCategory = value;
return this;
}
@DataClass.Generated(
- time = 1664805688714L,
+ time = 1669437519576L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedActivityImpl.java",
- inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mTargetDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
+ inputSignatures = "private int theme\nprivate int uiOptions\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String targetActivity\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String parentActivityName\nprivate @android.annotation.Nullable java.lang.String taskAffinity\nprivate int privateFlags\nprivate @android.annotation.Nullable @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) java.lang.String permission\nprivate @android.annotation.Nullable java.util.Set<java.lang.String> mKnownActivityEmbeddingCerts\nprivate int launchMode\nprivate int documentLaunchMode\nprivate int maxRecents\nprivate int configChanges\nprivate int softInputMode\nprivate int persistableMode\nprivate int lockTaskLaunchMode\nprivate int screenOrientation\nprivate int resizeMode\nprivate float maxAspectRatio\nprivate float minAspectRatio\nprivate boolean supportsSizeChanges\nprivate @android.annotation.Nullable java.lang.String requestedVrComponent\nprivate int rotationAnimation\nprivate int colorMode\nprivate @android.annotation.Nullable android.content.pm.ActivityInfo.WindowLayout windowLayout\nprivate @android.annotation.Nullable java.lang.String mRequiredDisplayCategory\npublic static final @android.annotation.NonNull android.os.Parcelable.Creator<com.android.server.pm.pkg.component.ParsedActivityImpl> CREATOR\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAppDetailsActivity(java.lang.String,java.lang.String,int,java.lang.String,boolean)\nstatic @android.annotation.NonNull com.android.server.pm.pkg.component.ParsedActivityImpl makeAlias(java.lang.String,com.android.server.pm.pkg.component.ParsedActivity)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMaxAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setMinAspectRatio(int,float)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setTargetActivity(java.lang.String)\npublic com.android.server.pm.pkg.component.ParsedActivityImpl setPermission(java.lang.String)\npublic @android.annotation.NonNull @java.lang.Override java.util.Set<java.lang.String> getKnownActivityEmbeddingCerts()\npublic void setKnownActivityEmbeddingCerts(java.util.Set<java.lang.String>)\npublic java.lang.String toString()\npublic @java.lang.Override int describeContents()\npublic @java.lang.Override void writeToParcel(android.os.Parcel,int)\nclass ParsedActivityImpl extends com.android.server.pm.pkg.component.ParsedMainComponentImpl implements [com.android.server.pm.pkg.component.ParsedActivity, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genSetters=true, genBuilder=false, genParcelable=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
index 305062b..ea791e1 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedActivityUtils.java
@@ -220,17 +220,17 @@
pkg.setVisibleToInstantApps(true);
}
- String targetDisplayCategory = sa.getNonConfigurationString(
- R.styleable.AndroidManifestActivity_targetDisplayCategory, 0);
+ String requiredDisplayCategory = sa.getNonConfigurationString(
+ R.styleable.AndroidManifestActivity_requiredDisplayCategory, 0);
- if (targetDisplayCategory != null
- && FrameworkParsingPackageUtils.validateName(targetDisplayCategory,
+ if (requiredDisplayCategory != null
+ && FrameworkParsingPackageUtils.validateName(requiredDisplayCategory,
false /* requireSeparator */, false /* requireFilename */) != null) {
- return input.error("targetDisplayCategory attribute can only consists of "
- + "alphanumeric characters, '_', and '.'");
+ return input.error("requiredDisplayCategory attribute can only consist "
+ + "of alphanumeric characters, '_', and '.'");
}
- activity.setTargetDisplayCategory(targetDisplayCategory);
+ activity.setRequiredDisplayCategory(requiredDisplayCategory);
return parseActivityOrAlias(activity, pkg, tag, parser, res, sa, receiver,
false /*isAlias*/, visibleToEphemeral, input,
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 19409b1..73759d3 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -4927,7 +4927,8 @@
// animation on the keyguard but seeing the IME window that originally on the app
// which behinds the keyguard.
final WindowState imeInputTarget = getImeInputTarget();
- if (imeInputTarget != null && !(imeInputTarget.isDrawn() || imeInputTarget.isVisible())) {
+ if (imeInputTarget != null
+ && !(imeInputTarget.isDrawn() || imeInputTarget.isVisibleRequested())) {
return false;
}
return mDisplayContent.forAllImeWindows(callback, traverseTopToBottom);
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 5d0551b..969056e 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -310,7 +310,7 @@
const InputDeviceIdentifier& identifier) override;
std::string getDeviceAlias(const InputDeviceIdentifier& identifier) override;
TouchAffineTransformation getTouchAffineTransformation(const std::string& inputDeviceDescriptor,
- int32_t surfaceRotation) override;
+ ui::Rotation surfaceRotation) override;
TouchAffineTransformation getTouchAffineTransformation(JNIEnv* env, jfloatArray matrixArr);
void notifyStylusGestureStarted(int32_t deviceId, nsecs_t eventTime) override;
@@ -1153,7 +1153,7 @@
}
TouchAffineTransformation NativeInputManager::getTouchAffineTransformation(
- const std::string& inputDeviceDescriptor, int32_t surfaceRotation) {
+ const std::string& inputDeviceDescriptor, ui::Rotation surfaceRotation) {
JNIEnv* env = jniEnv();
ScopedLocalRef<jstring> descriptorObj(env, env->NewStringUTF(inputDeviceDescriptor.c_str()));
diff --git a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
index 59f27ec..c8e2676 100644
--- a/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
+++ b/services/tests/PackageManagerServiceTests/server/src/com/android/server/PackageParserTest.java
@@ -334,7 +334,7 @@
}
@Test
- public void testParseActivityTargetDisplayCategoryValid() throws Exception {
+ public void testParseActivityRequiredDisplayCategoryValid() throws Exception {
final File testFile = extractFile(TEST_APP4_APK);
String actualDisplayCategory = null;
try {
@@ -342,7 +342,7 @@
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
- actualDisplayCategory = activity.getTargetDisplayCategory();
+ actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
} finally {
@@ -352,7 +352,7 @@
}
@Test
- public void testParseActivityTargetDisplayCategoryInvalid() throws Exception {
+ public void testParseActivityRequiredDisplayCategoryInvalid() throws Exception {
final File testFile = extractFile(TEST_APP6_APK);
String actualDisplayCategory = null;
try {
@@ -360,12 +360,12 @@
final List<ParsedActivity> activities = pkg.getActivities();
for (ParsedActivity activity : activities) {
if ((PACKAGE_NAME + ".MyActivity").equals(activity.getName())) {
- actualDisplayCategory = activity.getTargetDisplayCategory();
+ actualDisplayCategory = activity.getRequiredDisplayCategory();
}
}
} catch (PackageManagerException e) {
assertThat(e.getMessage()).contains(
- "targetDisplayCategory attribute can only consists"
+ "requiredDisplayCategory attribute can only consist"
+ " of alphanumeric characters, '_', and '.'");
} finally {
testFile.delete();
diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
index 4ceae96..0e2e35f 100644
--- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
+++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/ParsedActivityTest.kt
@@ -54,7 +54,7 @@
ParsedActivity::getTheme,
ParsedActivity::getUiOptions,
ParsedActivity::isSupportsSizeChanges,
- ParsedActivity::getTargetDisplayCategory
+ ParsedActivity::getRequiredDisplayCategory
)
override fun mainComponentSubclassExtraParams() = listOf(
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 e8b8253..537d04f 100644
--- a/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/UserControllerTest.java
@@ -113,6 +113,8 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
/**
* Tests for {@link UserController}.
@@ -146,9 +148,11 @@
private static final List<String> START_FOREGROUND_USER_ACTIONS = newArrayList(
Intent.ACTION_USER_STARTED,
- Intent.ACTION_USER_SWITCHED,
Intent.ACTION_USER_STARTING);
+ private static final List<String> START_FOREGROUND_USER_DEFERRED_ACTIONS = newArrayList(
+ Intent.ACTION_USER_SWITCHED);
+
private static final List<String> START_BACKGROUND_USER_ACTIONS = newArrayList(
Intent.ACTION_USER_STARTED,
Intent.ACTION_LOCKED_BOOT_COMPLETED,
@@ -397,11 +401,11 @@
private void continueAndCompleteUserSwitch(UserState userState, int oldUserId, int newUserId) {
mUserController.continueUserSwitch(userState, oldUserId, newUserId);
mInjector.mHandler.removeMessages(UserController.COMPLETE_USER_SWITCH_MSG);
- mUserController.completeUserSwitch(newUserId);
+ mUserController.completeUserSwitch(oldUserId, newUserId);
}
@Test
- public void testContinueUserSwitch() throws RemoteException {
+ public void testContinueUserSwitch() {
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
// Start user -- this will update state of mUserController
@@ -416,12 +420,12 @@
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(0)).dismissKeyguard(any(), anyString());
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@Test
- public void testContinueUserSwitchDismissKeyguard() throws RemoteException {
+ public void testContinueUserSwitchDismissKeyguard() {
when(mInjector.mKeyguardManagerMock.isDeviceSecure(anyInt())).thenReturn(false);
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ true,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
@@ -437,12 +441,12 @@
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector, times(1)).dismissKeyguard(any(), anyString());
verify(mInjector.getWindowManager(), times(1)).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
verifySystemUserVisibilityChangesNeverNotified();
}
@Test
- public void testContinueUserSwitchUIDisabled() throws RemoteException {
+ public void testContinueUserSwitchUIDisabled() {
mUserController.setInitialConfig(/* userSwitchUiEnabled= */ false,
/* maxRunningUsers= */ 3, /* delayUserDataLocking= */ false);
@@ -457,11 +461,11 @@
// Verify that continueUserSwitch worked as expected
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), never()).stopFreezingScreen();
- continueUserSwitchAssertions(TEST_USER_ID, false);
+ continueUserSwitchAssertions(oldUserId, TEST_USER_ID, false);
}
- private void continueUserSwitchAssertions(int expectedUserId, boolean backgroundUserStopping)
- throws RemoteException {
+ private void continueUserSwitchAssertions(int expectedOldUserId, int expectedNewUserId,
+ boolean backgroundUserStopping) {
Set<Integer> expectedCodes = new LinkedHashSet<>();
expectedCodes.add(COMPLETE_USER_SWITCH_MSG);
expectedCodes.add(REPORT_USER_SWITCH_COMPLETE_MSG);
@@ -473,7 +477,8 @@
assertEquals("Unexpected message sent", expectedCodes, actualCodes);
Message msg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_COMPLETE_MSG);
assertNotNull(msg);
- assertEquals("Unexpected userId", expectedUserId, msg.arg1);
+ assertEquals("Unexpected oldUserId", expectedOldUserId, msg.arg1);
+ assertEquals("Unexpected newUserId", expectedNewUserId, msg.arg2);
}
@Test
@@ -486,16 +491,21 @@
mUserController.startUser(TEST_USER_ID, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
assertNotNull(reportMsg);
+ int oldUserId = reportMsg.arg1;
int newUserId = reportMsg.arg2;
mInjector.mHandler.clearAllRecordedMessages();
// Mockito can't reset only interactions, so just verify that this hasn't been
// called with 'false' until after dispatchUserSwitchComplete.
verify(mInjector.getWindowManager(), never()).setSwitchingUser(false);
// Call dispatchUserSwitchComplete
- mUserController.dispatchUserSwitchComplete(newUserId);
+ mUserController.dispatchUserSwitchComplete(oldUserId, newUserId);
verify(observer, times(1)).onUserSwitchComplete(anyInt());
verify(observer).onUserSwitchComplete(TEST_USER_ID);
verify(mInjector.getWindowManager(), times(1)).setSwitchingUser(false);
+ startUserAssertions(Stream.concat(
+ START_FOREGROUND_USER_ACTIONS.stream(),
+ START_FOREGROUND_USER_DEFERRED_ACTIONS.stream()
+ ).collect(Collectors.toList()), Collections.emptySet());
}
@Test
@@ -902,8 +912,7 @@
}
private void addForegroundUserAndContinueUserSwitch(int newUserId, int expectedOldUserId,
- int expectedNumberOfCalls, boolean expectOldUserStopping)
- throws RemoteException {
+ int expectedNumberOfCalls, boolean expectOldUserStopping) {
// Start user -- this will update state of mUserController
mUserController.startUser(newUserId, true);
Message reportMsg = mInjector.mHandler.getMessageForCode(REPORT_USER_SWITCH_MSG);
@@ -918,7 +927,7 @@
continueAndCompleteUserSwitch(userState, oldUserId, newUserId);
verify(mInjector.getWindowManager(), times(expectedNumberOfCalls))
.stopFreezingScreen();
- continueUserSwitchAssertions(newUserId, expectOldUserStopping);
+ continueUserSwitchAssertions(oldUserId, newUserId, expectOldUserStopping);
}
private void setUpUser(@UserIdInt int userId, @UserInfoFlag int flags) {
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 3e8a070..3daf0f8 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -212,14 +212,14 @@
private ArrayList<ActivityInfo> getActivityInfoList(
String packageName, String name, boolean displayOnRemoveDevices,
- String targetDisplayCategory) {
+ String requiredDisplayCategory) {
ActivityInfo activityInfo = new ActivityInfo();
activityInfo.packageName = packageName;
activityInfo.name = name;
activityInfo.flags = displayOnRemoveDevices
? FLAG_CAN_DISPLAY_ON_REMOTE_DEVICES : FLAG_CANNOT_DISPLAY_ON_REMOTE_DEVICES;
activityInfo.applicationInfo = mApplicationInfoMock;
- activityInfo.targetDisplayCategory = targetDisplayCategory;
+ activityInfo.requiredDisplayCategory = requiredDisplayCategory;
return new ArrayList<>(Arrays.asList(activityInfo));
}
diff --git a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
index 71f3d15..d1ae9ff 100644
--- a/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/DisplayModeDirectorTest.java
@@ -200,12 +200,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDisplayModeVoting(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDisplayModeVoting() {
// With no votes present, DisplayModeDirector should allow any refresh rate.
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
DesiredDisplayModeSpecs modeSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
@@ -242,12 +237,7 @@
.isEqualTo((float) (minFps + i));
assertThat(modeSpecs.primary.physical.max)
.isEqualTo((float) (maxFps - i));
- if (frameRateIsRefreshRate) {
- assertThat(modeSpecs.primary.render.min)
- .isEqualTo((float) (minFps + i));
- } else {
- assertThat(modeSpecs.primary.render.min).isZero();
- }
+ assertThat(modeSpecs.primary.render.min).isZero();
assertThat(modeSpecs.primary.render.max)
.isEqualTo((float) (maxFps - i));
if (priority >= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF) {
@@ -255,12 +245,7 @@
.isEqualTo((float) (minFps + i));
assertThat(modeSpecs.appRequest.physical.max)
.isEqualTo((float) (maxFps - i));
- if (frameRateIsRefreshRate) {
- assertThat(modeSpecs.appRequest.render.min).isEqualTo(
- (float) (minFps + i));
- } else {
- assertThat(modeSpecs.appRequest.render.min).isZero();
- }
+ assertThat(modeSpecs.appRequest.render.min).isZero();
assertThat(modeSpecs.appRequest.render.max).isEqualTo(
(float) (maxFps - i));
} else {
@@ -292,12 +277,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithFloatingPointErrors(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithFloatingPointErrors() {
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -318,12 +298,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle(
- boolean frameRateIsRefreshRate) {
+ public void testFlickerHasLowerPriorityThanUserAndRangeIsSingle() {
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
< Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE
@@ -332,7 +307,6 @@
assertTrue(Vote.PRIORITY_FLICKER_REFRESH_RATE_SWITCH
> Vote.PRIORITY_LOW_POWER_MODE);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -408,17 +382,12 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testLPMHasHigherPriorityThanUser(boolean frameRateIsRefreshRate) {
+ public void testLPMHasHigherPriorityThanUser() {
assertTrue(Vote.PRIORITY_LOW_POWER_MODE
> Vote.PRIORITY_APP_REQUEST_BASE_MODE_REFRESH_RATE);
assertTrue(Vote.PRIORITY_LOW_POWER_MODE
> Vote.PRIORITY_APP_REQUEST_SIZE);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -443,11 +412,7 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -462,11 +427,7 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
@@ -481,11 +442,7 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(2);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -500,21 +457,13 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.baseModeId).isEqualTo(4);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(90);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestRefreshRateRange(boolean frameRateIsRefreshRate) {
+ public void testAppRequestRefreshRateRange() {
// Confirm that the app request range doesn't include flicker or min refresh rate settings,
// but does include everything else.
assertTrue(
@@ -525,7 +474,6 @@
assertTrue(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -582,12 +530,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testSpecsFromRefreshRateSettings(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testSpecsFromRefreshRateSettings() {
// Confirm that, with varying settings for min, peak, and default refresh rate,
// DesiredDisplayModeSpecs is calculated correctly.
float[] refreshRates = {30.f, 60.f, 90.f, 120.f, 150.f};
@@ -607,27 +550,12 @@
RefreshRateRanges frameRateAll = new RefreshRateRanges(rangeAll, rangeAll);
RefreshRateRanges frameRate90toInf = new RefreshRateRanges(range90toInf, range90toInf);
- RefreshRateRanges frameRate0to60;
- RefreshRateRanges frameRate0to90;
- RefreshRateRanges frameRate0to120;
- RefreshRateRanges frameRate60to90;
- RefreshRateRanges frameRate90to90;
- RefreshRateRanges frameRate90to120;
- if (frameRateIsRefreshRate) {
- frameRate0to60 = new RefreshRateRanges(range0to60, range0to60);
- frameRate0to90 = new RefreshRateRanges(range0to90, range0to90);
- frameRate0to120 = new RefreshRateRanges(range0to120, range0to120);
- frameRate60to90 = new RefreshRateRanges(range60to90, range60to90);
- frameRate90to90 = new RefreshRateRanges(range90to90, range90to90);
- frameRate90to120 = new RefreshRateRanges(range90to120, range90to120);
- } else {
- frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
- frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
- frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
- frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
- frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
- frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
- }
+ RefreshRateRanges frameRate0to60 = new RefreshRateRanges(rangeAll, range0to60);
+ RefreshRateRanges frameRate0to90 = new RefreshRateRanges(rangeAll, range0to90);
+ RefreshRateRanges frameRate0to120 = new RefreshRateRanges(rangeAll, range0to120);
+ RefreshRateRanges frameRate60to90 = new RefreshRateRanges(range60toInf, range60to90);
+ RefreshRateRanges frameRate90to90 = new RefreshRateRanges(range90toInf, range90to90);
+ RefreshRateRanges frameRate90to120 = new RefreshRateRanges(range90toInf, range90to120);
verifySpecsWithRefreshRateSettings(director, 0, 0, 0, frameRateAll, frameRateAll);
verifySpecsWithRefreshRateSettings(director, 0, 0, 90, frameRate0to90, frameRateAll);
@@ -657,12 +585,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testBrightnessObserverCallWithRefreshRateSettings(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testBrightnessObserverCallWithRefreshRateSettings() {
// Confirm that, with varying settings for min, peak, and default refresh rate, we make the
// correct call to the brightness observer.
float[] refreshRates = {60.f, 90.f, 120.f};
@@ -677,12 +600,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithAlwaysRespectAppRequest(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithAlwaysRespectAppRequest() {
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/50, /*width=*/1000, /*height=*/1000, 50);
@@ -711,11 +629,7 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
@@ -734,23 +648,14 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(60);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithSwitchingTypeNone(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithSwitchingTypeNone() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -765,20 +670,11 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 60);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -800,12 +696,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testVotingWithSwitchingTypeRenderFrameRateOnly(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testVotingWithSwitchingTypeRenderFrameRateOnly() {
DisplayModeDirector director = createDirectorFromFpsRange(0, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -820,20 +711,11 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 60);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
@@ -846,20 +728,11 @@
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(30);
- } else {
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- }
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(30);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(30);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(30);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- }
+ assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(0);
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(30);
}
@@ -887,12 +760,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDefaultDisplayModeIsSelectedIfAvailable(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDefaultDisplayModeIsSelectedIfAvailable() {
final float[] refreshRates = new float[]{24f, 25f, 30f, 60f, 90f};
final int defaultModeId = 3;
DisplayModeDirector director = createDirectorFromRefreshRateArray(
@@ -1173,17 +1041,12 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestMinRefreshRate(boolean frameRateIsRefreshRate) {
+ public void testAppRequestMinRefreshRate() {
// Confirm that the app min request range doesn't include flicker or min refresh rate
// settings but does include everything else.
assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
>= Vote.APP_REQUEST_REFRESH_RATE_RANGE_PRIORITY_CUTOFF);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
Display.Mode[] modes = new Display.Mode[3];
modes[0] = new Display.Mode(
/*modeId=*/60, /*width=*/1000, /*height=*/1000, 60);
@@ -1225,11 +1088,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestMaxRefreshRate(boolean frameRateIsRefreshRate) {
+ public void testAppRequestMaxRefreshRate() {
// Confirm that the app max request range doesn't include flicker or min refresh rate
// settings but does include everything else.
assertTrue(Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE
@@ -1243,7 +1102,6 @@
modes[2] = new Display.Mode(
/*modeId=*/90, /*width=*/1000, /*height=*/1000, 90);
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
DisplayModeDirector director = createDirectorFromModeArray(modes, modes[1]);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1254,19 +1112,11 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.render.min).isZero();
- }
+ assertThat(desiredSpecs.primary.render.min).isZero();
assertThat(desiredSpecs.primary.render.max).isAtMost(60);
assertThat(desiredSpecs.appRequest.physical.min).isAtMost(60f);
assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isAtMost(60f);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isAtLeast(90f);
votes.put(Vote.PRIORITY_USER_SETTING_MIN_RENDER_FRAME_RATE,
@@ -1288,30 +1138,16 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(75);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(75);
- } else {
- assertThat(desiredSpecs.primary.render.min).isZero();
- }
+ assertThat(desiredSpecs.primary.render.min).isZero();
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(75);
assertThat(desiredSpecs.appRequest.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(
- 75);
- } else {
- assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
- }
+ assertThat(desiredSpecs.appRequest.physical.max).isAtLeast(90f);
assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(75);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_modeId(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_modeId() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 0, 0);
@@ -1373,12 +1209,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_minRefreshRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_minRefreshRate() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 60, 0);
Vote appRequestRefreshRate =
@@ -1391,15 +1222,9 @@
Vote appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
- .isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(60);
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max).isAtLeast(90);
@@ -1417,15 +1242,9 @@
appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isWithin(
- FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max).isAtLeast(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(90);
@@ -1435,12 +1254,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_maxRefreshRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_maxRefreshRate() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, -1, 0, 90);
Vote appRequestRefreshRate =
@@ -1454,13 +1268,8 @@
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1480,13 +1289,8 @@
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min).isZero();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1512,12 +1316,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestObserver_modeIdAndRefreshRateRange(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestObserver_modeIdAndRefreshRateRange() {
DisplayModeDirector director = createDirectorFromFpsRange(60, 90);
director.getAppRequestObserver().setAppRequest(DISPLAY_ID, 60, 90, 90);
@@ -1547,16 +1346,9 @@
Vote appRequestRefreshRateRange =
director.getVote(DISPLAY_ID, Vote.PRIORITY_APP_REQUEST_RENDER_FRAME_RATE_RANGE);
assertNotNull(appRequestRefreshRateRange);
- if (frameRateIsRefreshRate) {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min)
- .isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
- assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
- .isPositiveInfinity();
- }
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.min).isZero();
+ assertThat(appRequestRefreshRateRange.refreshRateRanges.physical.max)
+ .isPositiveInfinity();
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.min)
.isWithin(FLOAT_TOLERANCE).of(90);
assertThat(appRequestRefreshRateRange.refreshRateRanges.render.max)
@@ -1566,12 +1358,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testAppRequestsIsTheDefaultMode(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testAppRequestsIsTheDefaultMode() {
Display.Mode[] modes = new Display.Mode[2];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1600,12 +1387,7 @@
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testDisableRefreshRateSwitchingVote(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testDisableRefreshRateSwitchingVote() {
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1650,8 +1432,8 @@
"true",
"false"
})
- public void testBaseModeIdInPrimaryRange(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testBaseModeIdInPrimaryRange(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
DisplayModeDirector director = createDirectorFromFpsRange(50, 90);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1662,12 +1444,12 @@
director.injectVotesByDisplay(votesByDisplay);
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(desiredSpecs.baseModeId).isEqualTo(50);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.baseModeId).isEqualTo(70);
+ } else {
+ assertThat(desiredSpecs.baseModeId).isEqualTo(50);
+
}
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
@@ -1679,11 +1461,7 @@
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1697,12 +1475,11 @@
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(60);
- assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(52);
+ } else {
+ assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(60);
}
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
@@ -1716,23 +1493,14 @@
director.injectVotesByDisplay(votesByDisplay);
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.primary.physical.min).isWithin(FLOAT_TOLERANCE).of(0);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.primary.physical.max).isWithin(FLOAT_TOLERANCE).of(58);
- } else {
- assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
- }
+ assertThat(desiredSpecs.primary.physical.max).isPositiveInfinity();
assertThat(desiredSpecs.primary.render.min).isWithin(FLOAT_TOLERANCE).of(0);
assertThat(desiredSpecs.primary.render.max).isWithin(FLOAT_TOLERANCE).of(58);
assertThat(desiredSpecs.baseModeId).isEqualTo(55);
}
@Test
- @Parameters({
- "true",
- "false"
- })
- public void testStaleAppVote(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testStaleAppVote() {
Display.Mode[] modes = new Display.Mode[4];
modes[0] = new Display.Mode(
/*modeId=*/1, /*width=*/1000, /*height=*/1000, 60);
@@ -1782,8 +1550,8 @@
"true",
"false"
})
- public void testRefreshRateIsSubsetOfFrameRate(boolean frameRateIsRefreshRate) {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(frameRateIsRefreshRate);
+ public void testRefreshRateIsSubsetOfFrameRate(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1795,11 +1563,7 @@
DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
votes.clear();
@@ -1810,13 +1574,11 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
- 120);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
votes.clear();
@@ -1827,13 +1589,12 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(
- 120);
- } else {
+ if (supportsFrameRateOverride) {
assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(60);
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
votes.clear();
@@ -1844,17 +1605,12 @@
desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
assertThat(desiredSpecs.appRequest.physical.min).isWithin(FLOAT_TOLERANCE).of(90);
assertThat(desiredSpecs.appRequest.physical.max).isWithin(FLOAT_TOLERANCE).of(120);
- if (frameRateIsRefreshRate) {
- assertThat(desiredSpecs.appRequest.render.min).isWithin(FLOAT_TOLERANCE).of(90);
- } else {
- assertThat(desiredSpecs.appRequest.render.min).isZero();
- }
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(120);
}
@Test
public void testRenderFrameRateIsAchievableByPhysicalRefreshRate() {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -1872,8 +1628,34 @@
}
@Test
+ @Parameters({
+ "true",
+ "false"
+ })
+ public void testRenderFrameRateIncludesPhysicalRefreshRate(boolean supportsFrameRateOverride) {
+ when(mInjector.supportsFrameRateOverride()).thenReturn(supportsFrameRateOverride);
+ DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
+ SparseArray<Vote> votes = new SparseArray<>();
+ SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
+ votesByDisplay.put(DISPLAY_ID, votes);
+
+ votes.put(Vote.PRIORITY_LOW_POWER_MODE, Vote.forRenderFrameRates(0, 60));
+ votes.put(Vote.PRIORITY_USER_SETTING_PEAK_RENDER_FRAME_RATE,
+ Vote.forRenderFrameRates(0, 30));
+ director.injectVotesByDisplay(votesByDisplay);
+ DesiredDisplayModeSpecs desiredSpecs = director.getDesiredDisplayModeSpecs(DISPLAY_ID);
+ assertThat(desiredSpecs.appRequest.physical.min).isZero();
+ assertThat(desiredSpecs.appRequest.physical.max).isPositiveInfinity();
+ assertThat(desiredSpecs.appRequest.render.min).isZero();
+ if (supportsFrameRateOverride) {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(30);
+ } else {
+ assertThat(desiredSpecs.appRequest.render.max).isWithin(FLOAT_TOLERANCE).of(60);
+ }
+ }
+
+ @Test
public void testRenderFrameRateIsDroppedIfLowerPriorityThenBaseModeRefreshRate() {
- when(mInjector.renderFrameRateIsPhysicalRefreshRate()).thenReturn(false);
DisplayModeDirector director = createDirectorFromFpsRange(60, 120);
SparseArray<Vote> votes = new SparseArray<>();
SparseArray<SparseArray<Vote>> votesByDisplay = new SparseArray<>();
@@ -2692,7 +2474,7 @@
}
@Override
- public boolean renderFrameRateIsPhysicalRefreshRate() {
+ public boolean supportsFrameRateOverride() {
return true;
}
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
index 4dcb442..5451735 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp4.xml
@@ -32,7 +32,7 @@
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true"
- android:targetDisplayCategory="automotive">
+ android:requiredDisplayCategory="automotive">
<property android:name="android.cts.PROPERTY_ACTIVITY" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_COMPONENT" android:value="@integer/integer_property" />
<property android:name="android.cts.PROPERTY_STRING" android:value="koala activity" />
diff --git a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
index 8e694e1..601479d 100644
--- a/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
+++ b/services/tests/servicestests/test-apps/PackageParserApp/AndroidManifestApp6.xml
@@ -21,7 +21,7 @@
<application>
<activity android:name="com.android.servicestests.apps.packageparserapp.MyActivity"
android:exported="true"
- android:targetDisplayCategory="$automotive">
+ android:requiredDisplayCategory="$automotive">
</activity>
</application>
</manifest>
diff --git a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
index c898119..cdb2642 100644
--- a/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/InsetsStateControllerTest.java
@@ -443,6 +443,44 @@
}
@Test
+ public void testUpdateAboveInsetsState_imeTargetOnScreenBehavior() {
+ final WindowToken imeToken = createTestWindowToken(TYPE_INPUT_METHOD, mDisplayContent);
+ final WindowState ime = createWindow(null, TYPE_INPUT_METHOD, imeToken, "ime");
+ final WindowState app = createTestWindow("app");
+
+ getController().getSourceProvider(ITYPE_IME).setWindowContainer(ime, null, null);
+ ime.getControllableInsetProvider().setServerVisible(true);
+
+ app.mActivityRecord.setVisibility(true);
+ mDisplayContent.setImeLayeringTarget(app);
+ mDisplayContent.updateImeInputAndControlTarget(app);
+
+ app.setRequestedVisibleTypes(ime(), ime());
+ getController().onInsetsModified(app);
+ assertTrue(ime.getControllableInsetProvider().getSource().isVisible());
+
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+
+ // Expect the app will still get IME insets even when the app was invisible.
+ // (i.e. app invisible after locking the device)
+ app.mActivityRecord.setVisible(false);
+ app.setHasSurface(false);
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+
+ // Expect the app will get IME insets when the app is requesting visible.
+ // (i.e. app is going to visible when unlocking the device)
+ app.mActivityRecord.setVisibility(true);
+ assertTrue(app.isVisibleRequested());
+ getController().updateAboveInsetsState(true /* notifyInsetsChange */);
+ assertNotNull(app.getInsetsState().peekSource(ITYPE_IME));
+ verify(app, atLeastOnce()).notifyInsetsChanged();
+ }
+
+ @Test
public void testDispatchGlobalInsets() {
final WindowState navBar = createWindow(null, TYPE_APPLICATION, "navBar");
getController().getSourceProvider(ITYPE_NAVIGATION_BAR).setWindowContainer(navBar, null,
diff --git a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
index c548dc3..eb26415 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RemoteAnimationControllerTest.java
@@ -772,6 +772,7 @@
// Simulating now win1 is being covered by the lockscreen which has no surface,
// and then launching an activity win2 with the remote animation
win1.mHasSurface = false;
+ win1.mActivityRecord.setVisibility(false);
mDisplayContent.mOpeningApps.add(win2.mActivityRecord);
final AnimationAdapter adapter = mController.createRemoteAnimationRecord(
win2.mActivityRecord, new Point(50, 100), null,
diff --git a/startop/view_compiler/TEST_MAPPING b/startop/view_compiler/TEST_MAPPING
deleted file mode 100644
index 791e471..0000000
--- a/startop/view_compiler/TEST_MAPPING
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "presubmit": [
- {
- "name": "dex-builder-test"
- },
- {
- "name": "CtsViewTestCases",
- "options": [
- {
- "include-filter": "android.view.cts.PrecompiledLayoutTest"
- }
- ]
- }
- ]
-}
diff --git a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
index 720f835..0157596 100644
--- a/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
+++ b/tools/lint/common/src/main/java/com/google/android/lint/PermissionMethodUtils.kt
@@ -17,6 +17,7 @@
package com.google.android.lint
import com.android.tools.lint.detector.api.getUMethod
+import org.jetbrains.uast.UAnnotation
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UParameter
@@ -26,10 +27,11 @@
return hasPermissionMethodAnnotation(method)
}
-fun hasPermissionMethodAnnotation(method: UMethod): Boolean = method.annotations
- .any {
- it.hasQualifiedName(ANNOTATION_PERMISSION_METHOD)
- }
+fun hasPermissionMethodAnnotation(method: UMethod): Boolean =
+ getPermissionMethodAnnotation(method) != null
+
+fun getPermissionMethodAnnotation(method: UMethod?): UAnnotation? = method?.uAnnotations
+ ?.firstOrNull { it.qualifiedName == ANNOTATION_PERMISSION_METHOD }
fun hasPermissionNameAnnotation(parameter: UParameter) = parameter.annotations.any {
it.hasQualifiedName(ANNOTATION_PERMISSION_NAME)
diff --git a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
index 413e197..c5cf0fb 100644
--- a/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
+++ b/tools/lint/framework/checks/src/main/java/com/google/android/lint/AndroidFrameworkIssueRegistry.kt
@@ -40,7 +40,7 @@
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+ SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
SaferParcelChecker.ISSUE_UNSAFE_API_USAGE,
PackageVisibilityDetector.ISSUE_PACKAGE_NAME_NO_PACKAGE_VISIBILITY_FILTERS,
RegisterReceiverFlagDetector.ISSUE_RECEIVER_EXPORTED_FLAG,
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
index b377d50..a20266a 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/AndroidGlobalIssueRegistry.kt
@@ -31,7 +31,7 @@
EnforcePermissionDetector.ISSUE_MISSING_ENFORCE_PERMISSION,
EnforcePermissionDetector.ISSUE_MISMATCHING_ENFORCE_PERMISSION,
EnforcePermissionHelperDetector.ISSUE_ENFORCE_PERMISSION_HELPER,
- SimpleManualPermissionEnforcementDetector.ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
+ SimpleManualPermissionEnforcementDetector.ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
)
override val api: Int
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
index f1b6348..ee7dd62 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/EnforcePermissionFix.kt
@@ -17,10 +17,14 @@
package com.google.android.lint.aidl
import com.android.tools.lint.detector.api.JavaContext
+import com.android.tools.lint.detector.api.LintFix
import com.android.tools.lint.detector.api.Location
+import com.android.tools.lint.detector.api.UastLintUtils.Companion.getAnnotationBooleanValue
import com.android.tools.lint.detector.api.getUMethod
+import com.google.android.lint.getPermissionMethodAnnotation
import com.google.android.lint.hasPermissionNameAnnotation
import com.google.android.lint.isPermissionMethodCall
+import com.intellij.psi.PsiType
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.uast.UCallExpression
import org.jetbrains.uast.evaluateString
@@ -36,13 +40,37 @@
*/
data class EnforcePermissionFix(
val locations: List<Location>,
- val permissionNames: List<String>
+ val permissionNames: List<String>,
+ val errorLevel: Boolean,
) {
- val annotation: String
+ fun toLintFix(annotationLocation: Location): LintFix {
+ val removeFixes = this.locations.map {
+ LintFix.create()
+ .replace()
+ .reformat(true)
+ .range(it)
+ .with("")
+ .autoFix()
+ .build()
+ }
+
+ val annotateFix = LintFix.create()
+ .annotate(this.annotation)
+ .range(annotationLocation)
+ .autoFix()
+ .build()
+
+ return LintFix.create().composite(annotateFix, *removeFixes.toTypedArray())
+ }
+
+ private val annotation: String
get() {
val quotedPermissions = permissionNames.joinToString(", ") { """"$it"""" }
+
val annotationParameter =
- if (permissionNames.size > 1) "allOf={$quotedPermissions}" else quotedPermissions
+ if (permissionNames.size > 1) "allOf={$quotedPermissions}"
+ else quotedPermissions
+
return "@$ANNOTATION_ENFORCE_PERMISSION($annotationParameter)"
}
@@ -54,19 +82,28 @@
fun fromCallExpression(
context: JavaContext,
callExpression: UCallExpression
- ): EnforcePermissionFix? =
- if (isPermissionMethodCall(callExpression)) {
- EnforcePermissionFix(
+ ): EnforcePermissionFix? {
+ val method = callExpression.resolve()?.getUMethod() ?: return null
+ val annotation = getPermissionMethodAnnotation(method) ?: return null
+ val enforces = method.returnType == PsiType.VOID
+ val orSelf = getAnnotationBooleanValue(annotation, "orSelf") ?: false
+ return EnforcePermissionFix(
listOf(getPermissionCheckLocation(context, callExpression)),
- getPermissionCheckValues(callExpression)
- )
- } else null
+ getPermissionCheckValues(callExpression),
+ // If we detect that the PermissionMethod enforces that permission is granted,
+ // AND is of the "orSelf" variety, we are very confident that this is a behavior
+ // preserving migration to @EnforcePermission. Thus, the incident should be ERROR
+ // level.
+ errorLevel = enforces && orSelf
+ )
+ }
fun compose(individuals: List<EnforcePermissionFix>): EnforcePermissionFix =
EnforcePermissionFix(
individuals.flatMap { it.locations },
- individuals.flatMap { it.permissionNames }
+ individuals.flatMap { it.permissionNames },
+ errorLevel = individuals.all(EnforcePermissionFix::errorLevel)
)
/**
diff --git a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
index 4c0cbe7..9999a0b 100644
--- a/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
+++ b/tools/lint/global/checks/src/main/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetector.kt
@@ -18,6 +18,7 @@
import com.android.tools.lint.detector.api.Category
import com.android.tools.lint.detector.api.Implementation
+import com.android.tools.lint.detector.api.Incident
import com.android.tools.lint.detector.api.Issue
import com.android.tools.lint.detector.api.JavaContext
import com.android.tools.lint.detector.api.Scope
@@ -28,6 +29,7 @@
import org.jetbrains.uast.UIfExpression
import org.jetbrains.uast.UMethod
import org.jetbrains.uast.UQualifiedReferenceExpression
+import org.jetbrains.uast.skipParenthesizedExprDown
/**
* Looks for methods implementing generated AIDL interface stubs
@@ -44,30 +46,25 @@
interfaceName: String,
body: UBlockExpression
) {
- val fix = accumulateSimplePermissionCheckFixes(body, context) ?: return
+ val enforcePermissionFix = accumulateSimplePermissionCheckFixes(body, context) ?: return
+ val lintFix = enforcePermissionFix.toLintFix(context.getLocation(node))
+ val message =
+ "$interfaceName permission check ${
+ if (enforcePermissionFix.errorLevel) "should" else "can"
+ } be converted to @EnforcePermission annotation"
- val javaRemoveFixes = fix.locations.map {
- fix()
- .replace()
- .reformat(true)
- .range(it)
- .with("")
- .autoFix()
- .build()
+ val incident = Incident(
+ ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT,
+ enforcePermissionFix.locations.last(),
+ message,
+ lintFix
+ )
+
+ if (enforcePermissionFix.errorLevel) {
+ incident.overrideSeverity(Severity.ERROR)
}
- val javaAnnotateFix = fix()
- .annotate(fix.annotation)
- .range(context.getLocation(node))
- .autoFix()
- .build()
-
- context.report(
- ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION,
- fix.locations.last(),
- "$interfaceName permission check can be converted to @EnforcePermission annotation",
- fix().composite(*javaRemoveFixes.toTypedArray(), javaAnnotateFix)
- )
+ context.report(incident)
}
/**
@@ -89,7 +86,8 @@
EnforcePermissionFix? {
val singleFixes = mutableListOf<EnforcePermissionFix>()
for (expression in methodBody.expressions) {
- singleFixes.add(getPermissionCheckFix(expression, context) ?: break)
+ singleFixes.add(getPermissionCheckFix(expression.skipParenthesizedExprDown(), context)
+ ?: break)
}
return when (singleFixes.size) {
0 -> null
@@ -133,7 +131,7 @@
""".trimIndent()
@JvmField
- val ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION = Issue.create(
+ val ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT = Issue.create(
id = "SimpleManualPermissionEnforcement",
briefDescription = "Manual permission check can be @EnforcePermission annotation",
explanation = EXPLANATION,
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
index 150fc26..bdf9c89 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/SimpleManualPermissionEnforcementDetectorTest.kt
@@ -18,7 +18,6 @@
import com.android.tools.lint.checks.infrastructure.LintDetectorTest
import com.android.tools.lint.checks.infrastructure.TestLintTask
-import com.android.tools.lint.checks.infrastructure.TestMode
import com.android.tools.lint.detector.api.Detector
import com.android.tools.lint.detector.api.Issue
@@ -27,7 +26,7 @@
override fun getDetector(): Detector = SimpleManualPermissionEnforcementDetector()
override fun getIssues(): List<Issue> = listOf(
SimpleManualPermissionEnforcementDetector
- .ISSUE_USE_ENFORCE_PERMISSION_ANNOTATION
+ .ISSUE_SIMPLE_MANUAL_PERMISSION_ENFORCEMENT
)
override fun lint(): TestLintTask = super.lint().allowMissingSdk()
@@ -36,15 +35,15 @@
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
+ }
"""
).indented(),
*stubs
@@ -52,10 +51,10 @@
.run()
.expect(
"""
- src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:7: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -69,22 +68,96 @@
)
}
+ fun testClass_orSelfFalse_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ @@ -5 +5
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -7 +8
+ - mContext.enforceCallingPermission("android.permission.READ_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testClass_enforcesFalse_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:7: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 7: Annotate with @EnforcePermission:
+ @@ -5 +5
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -7 +8
+ - mContext.checkCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ """
+ )
+ }
+
fun testAnonClass() {
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
- public class Foo {
- private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- }
- };
- }
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ }
+ };
+ }
"""
).indented(),
*stubs
@@ -92,10 +165,10 @@
.run()
.expect(
"""
- src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -114,16 +187,16 @@
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
+ import android.content.Context;
+ import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
- }
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
}
+ }
"""
).indented(),
*stubs,
@@ -132,10 +205,10 @@
.run()
.expect(
"""
- src/Foo.java:8: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:8: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.READ_CONTACTS, "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -153,20 +226,20 @@
lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
- public class Foo {
- private Context mContext;
- private ITest itest = new ITest.Stub() {
- @Override
- public void test() throws android.os.RemoteException {
- mContext.enforceCallingOrSelfPermission(
- "android.permission.READ_CONTACTS", "foo");
- mContext.enforceCallingOrSelfPermission(
- "android.permission.WRITE_CONTACTS", "foo");
- }
- };
- }
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
"""
).indented(),
*stubs
@@ -174,10 +247,10 @@
.run()
.expect(
"""
- src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:10: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission(
^
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -194,20 +267,110 @@
)
}
+ fun testAllOf_mixedOrSelf_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.enforceCallingPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.enforceCallingPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.enforceCallingPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ """
+ )
+ }
+
+ fun testAllOf_mixedEnforces_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+ public class Foo {
+ private Context mContext;
+ private ITest itest = new ITest.Stub() {
+ @Override
+ public void test() throws android.os.RemoteException {
+ mContext.enforceCallingOrSelfPermission(
+ "android.permission.READ_CONTACTS", "foo");
+ mContext.checkCallingOrSelfPermission(
+ "android.permission.WRITE_CONTACTS", "foo");
+ }
+ };
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:10: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ mContext.checkCallingOrSelfPermission(
+ ^
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 10: Annotate with @EnforcePermission:
+ @@ -6 +6
+ + @android.annotation.EnforcePermission(allOf={"android.permission.READ_CONTACTS", "android.permission.WRITE_CONTACTS"})
+ @@ -8 +9
+ - mContext.enforceCallingOrSelfPermission(
+ - "android.permission.READ_CONTACTS", "foo");
+ - mContext.checkCallingOrSelfPermission(
+ - "android.permission.WRITE_CONTACTS", "foo");
+ """
+ )
+ }
+
fun testPrecedingExpressions() {
lint().files(
java(
"""
- import android.os.Binder;
- import android.test.ITest;
- public class Foo extends ITest.Stub {
- private mContext Context;
- @Override
- public void test() throws android.os.RemoteException {
- long uid = Binder.getCallingUid();
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
+ import android.os.Binder;
+ import android.test.ITest;
+ public class Foo extends ITest.Stub {
+ private mContext Context;
+ @Override
+ public void test() throws android.os.RemoteException {
+ long uid = Binder.getCallingUid();
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
+ }
"""
).indented(),
*stubs
@@ -217,25 +380,25 @@
}
fun testPermissionHelper() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
- import android.content.Context;
- import android.test.ITest;
+ import android.content.Context;
+ import android.test.ITest;
- public class Foo extends ITest.Stub {
- private Context mContext;
+ public class Foo extends ITest.Stub {
+ private Context mContext;
- @android.content.pm.PermissionMethod
- private void helper() {
- mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
- }
-
- @Override
- public void test() throws android.os.RemoteException {
- helper();
- }
+ @android.content.pm.PermissionMethod(orSelf = true)
+ private void helper() {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
+
+ @Override
+ public void test() throws android.os.RemoteException {
+ helper();
+ }
+ }
"""
).indented(),
*stubs
@@ -243,10 +406,10 @@
.run()
.expect(
"""
- src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:14: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helper();
~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -260,8 +423,52 @@
)
}
+ fun testPermissionHelper_orSelfNotBubbledUp_warning() {
+ lint().files(
+ java(
+ """
+ import android.content.Context;
+ import android.test.ITest;
+
+ public class Foo extends ITest.Stub {
+ private Context mContext;
+
+ @android.content.pm.PermissionMethod
+ private void helper() {
+ mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
+ }
+
+ @Override
+ public void test() throws android.os.RemoteException {
+ helper();
+ }
+ }
+ """
+ ).indented(),
+ *stubs
+ )
+ .run()
+ .expect(
+ """
+ src/Foo.java:14: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ helper();
+ ~~~~~~~~~
+ 0 errors, 1 warnings
+ """
+ )
+ .expectFixDiffs(
+ """
+ Fix for src/Foo.java line 14: Annotate with @EnforcePermission:
+ @@ -12 +12
+ + @android.annotation.EnforcePermission("android.permission.READ_CONTACTS")
+ @@ -14 +15
+ - helper();
+ """
+ )
+ }
+
fun testPermissionHelperAllOf() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
import android.content.Context;
@@ -270,7 +477,7 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helper() {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
mContext.enforceCallingOrSelfPermission("android.permission.WRITE_CONTACTS", "foo");
@@ -289,10 +496,10 @@
.run()
.expect(
"""
- src/Foo.java:16: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:16: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
mContext.enforceCallingOrSelfPermission("FOO", "foo");
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
@@ -309,7 +516,7 @@
fun testPermissionHelperNested() {
- lint().skipTestModes(TestMode.PARENTHESIZED).files(
+ lint().files(
java(
"""
import android.content.Context;
@@ -318,12 +525,12 @@
public class Foo extends ITest.Stub {
private Context mContext;
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helperHelper() {
helper("android.permission.WRITE_CONTACTS");
}
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
private void helper(@android.content.pm.PermissionName String extraPermission) {
mContext.enforceCallingOrSelfPermission("android.permission.READ_CONTACTS", "foo");
}
@@ -340,10 +547,10 @@
.run()
.expect(
"""
- src/Foo.java:19: Warning: ITest permission check can be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
+ src/Foo.java:19: Error: ITest permission check should be converted to @EnforcePermission annotation [SimpleManualPermissionEnforcement]
helperHelper();
~~~~~~~~~~~~~~~
- 0 errors, 1 warnings
+ 1 errors, 0 warnings
"""
)
.expectFixDiffs(
diff --git a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
index bd6b195..5ac8a0b 100644
--- a/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
+++ b/tools/lint/global/checks/src/test/java/com/google/android/lint/aidl/Stubs.kt
@@ -17,8 +17,12 @@
"""
package android.content;
public class Context {
- @android.content.pm.PermissionMethod
+ @android.content.pm.PermissionMethod(orSelf = true)
public void enforceCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
+ @android.content.pm.PermissionMethod
+ public void enforceCallingPermission(@android.content.pm.PermissionName String permission, String message) {}
+ @android.content.pm.PermissionMethod(orSelf = true)
+ public int checkCallingOrSelfPermission(@android.content.pm.PermissionName String permission, String message) {}
}
"""
).indented()