Merge "Check for nullness in WallpaperService onDestroy" into udc-dev
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index 3772960..df1b666 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -4873,8 +4873,7 @@
}
}
if (wakeupUids.size() > 0 && mBatteryStatsInternal != null) {
- mBatteryStatsInternal.noteCpuWakingActivity(
- BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM, nowELAPSED,
+ mBatteryStatsInternal.noteWakingAlarmBatch(nowELAPSED,
wakeupUids.toArray());
}
deliverAlarmsLocked(triggerList, nowELAPSED);
diff --git a/core/java/android/content/ContentProvider.java b/core/java/android/content/ContentProvider.java
index 31c02b8..fa99b59 100644
--- a/core/java/android/content/ContentProvider.java
+++ b/core/java/android/content/ContentProvider.java
@@ -2085,7 +2085,8 @@
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new ParcelFileDescriptor which you can use to access
* the file.
@@ -2147,7 +2148,8 @@
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2208,7 +2210,8 @@
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new AssetFileDescriptor which you can use to access
* the file.
@@ -2262,7 +2265,8 @@
*
* @param uri The URI whose file is to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param signal A signal to cancel the operation in progress, or
* {@code null} if none. For example, if you are downloading a
* file from the network to service a "rw" mode request, you
@@ -2294,7 +2298,8 @@
*
* @param uri The URI to be opened.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
*
* @return Returns a new ParcelFileDescriptor that can be used by the
* client to access the file.
diff --git a/core/java/android/content/ContentResolver.java b/core/java/android/content/ContentResolver.java
index feca7a0..b2cd7e9 100644
--- a/core/java/android/content/ContentResolver.java
+++ b/core/java/android/content/ContentResolver.java
@@ -1536,7 +1536,8 @@
/**
* Synonym for {@link #openOutputStream(Uri, String)
- * openOutputStream(uri, "w")}.
+ * openOutputStream(uri, "w")}. Please note the implementation of "w" is up to each
+ * Provider implementation and it may or may not truncate.
*
* @param uri The desired URI.
* @return an OutputStream or {@code null} if the provider recently crashed.
@@ -1562,7 +1563,8 @@
*
* @param uri The desired URI.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return an OutputStream or {@code null} if the provider recently crashed.
* @throws FileNotFoundException if the provided URI could not be opened.
* @see #openAssetFileDescriptor(Uri, String)
@@ -1619,7 +1621,8 @@
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1662,7 +1665,8 @@
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @param cancellationSignal A signal to cancel the operation in progress,
* or null if none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
@@ -1756,7 +1760,8 @@
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note the exact implementation of these may differ for each
+ * Provider implementation - for example, "w" may or may not truncate.
* @return Returns a new ParcelFileDescriptor pointing to the file or {@code null} if the
* provider recently crashed. You own this descriptor and are responsible for closing it
* when done.
@@ -1810,7 +1815,8 @@
*
* @param uri The desired URI to open.
* @param mode The string representation of the file mode. Can be "r", "w", "wt", "wa", "rw"
- * or "rwt". See{@link ParcelFileDescriptor#parseMode} for more details.
+ * or "rwt". Please note "w" is write only and "wt" is write and truncate.
+ * See{@link ParcelFileDescriptor#parseMode} for more details.
* @param cancellationSignal A signal to cancel the operation in progress, or null if
* none. If the operation is canceled, then
* {@link OperationCanceledException} will be thrown.
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 2b73afc..c221d72 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -7613,7 +7613,7 @@
* the device association is changed by the system.
* <p>
* The callback can be called when an app is moved to a different device and the {@code Context}
- * is not explicily associated with a specific device.
+ * is not explicitly associated with a specific device.
* </p>
* <p> When an application receives a device id update callback, this Context is guaranteed to
* also have an updated display ID(if any) and {@link Configuration}.
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2b29e78..6bd9538 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -885,6 +885,16 @@
*/
private SurfaceSyncGroup mActiveSurfaceSyncGroup;
+
+ private final Object mPreviousSyncSafeguardLock = new Object();
+
+ /**
+ * Wraps the TransactionCommitted callback for the previous SSG so it can be added to the next
+ * SSG if started before previous has completed.
+ */
+ @GuardedBy("mPreviousSyncSafeguardLock")
+ private SurfaceSyncGroup mPreviousSyncSafeguard;
+
private static final Object sSyncProgressLock = new Object();
// The count needs to be static since it's used to enable or disable RT animations which is
// done at a global level per process. If any VRI syncs are in progress, we can't enable RT
@@ -11329,6 +11339,61 @@
});
}
+ /**
+ * This code will ensure that if multiple SurfaceSyncGroups are created for the same
+ * ViewRootImpl the SurfaceSyncGroups will maintain an order. The scenario that could occur
+ * is the following:
+ * <p>
+ * 1. SSG1 is created that includes the target VRI. There could be other VRIs in SSG1
+ * 2. The target VRI draws its frame and marks its own active SSG as ready, but SSG1 is still
+ * waiting on other things in the SSG
+ * 3. Another SSG2 is created for the target VRI. The second frame renders and marks its own
+ * second SSG as complete. SSG2 has nothing else to wait on, so it will apply at this point,
+ * even though SSG1 has not finished.
+ * 4. Frame2 will get to SF first and Frame1 will later get to SF when SSG1 completes.
+ * <p>
+ * The code below ensures the SSGs that contains the VRI maintain an order. We create a new SSG
+ * that's a safeguard SSG. Its only job is to prevent the next active SSG from completing.
+ * The current active SSG for VRI will add a transaction committed callback and when that's
+ * invoked, it will mark the safeguard SSG as ready. If a new request to create a SSG comes
+ * in and the safeguard SSG is not null, it's added as part of the new active SSG. A new
+ * safeguard SSG is created to correspond to the new active SSG. This creates a chain to
+ * ensure the latter SSG always waits for the former SSG's transaction to get to SF.
+ */
+ private void safeguardOverlappingSyncs(SurfaceSyncGroup activeSurfaceSyncGroup) {
+ SurfaceSyncGroup safeguardSsg = new SurfaceSyncGroup("VRI-Safeguard");
+ // Always disable timeout on the safeguard sync
+ safeguardSsg.toggleTimeout(false /* enable */);
+ synchronized (mPreviousSyncSafeguardLock) {
+ if (mPreviousSyncSafeguard != null) {
+ activeSurfaceSyncGroup.add(mPreviousSyncSafeguard, null /* runnable */);
+ // Temporarily disable the timeout on the SSG that will contain the buffer. This
+ // is to ensure we don't timeout the active SSG before the previous one completes to
+ // ensure the order is maintained. The previous SSG has a timeout on its own SSG
+ // so it's guaranteed to complete.
+ activeSurfaceSyncGroup.toggleTimeout(false /* enable */);
+ mPreviousSyncSafeguard.addSyncCompleteCallback(mSimpleExecutor, () -> {
+ // Once we receive that the previous sync guard has been invoked, we can re-add
+ // the timeout on the active sync to ensure we eventually complete so it's not
+ // stuck permanently.
+ activeSurfaceSyncGroup.toggleTimeout(true /*enable */);
+ });
+ }
+ mPreviousSyncSafeguard = safeguardSsg;
+ }
+
+ Transaction t = new Transaction();
+ t.addTransactionCommittedListener(mSimpleExecutor, () -> {
+ safeguardSsg.markSyncReady();
+ synchronized (mPreviousSyncSafeguardLock) {
+ if (mPreviousSyncSafeguard == safeguardSsg) {
+ mPreviousSyncSafeguard = null;
+ }
+ }
+ });
+ activeSurfaceSyncGroup.addTransaction(t);
+ }
+
@Override
public SurfaceSyncGroup getOrCreateSurfaceSyncGroup() {
boolean newSyncGroup = false;
@@ -11355,6 +11420,7 @@
mHandler.post(runnable);
}
});
+ safeguardOverlappingSyncs(mActiveSurfaceSyncGroup);
updateSyncInProgressCount(mActiveSurfaceSyncGroup);
newSyncGroup = true;
}
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index ddcb431..5b6df1c 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -467,7 +467,7 @@
* implementation.
* @hide
*/
- int TRANSIT_FIRST_CUSTOM = 13;
+ int TRANSIT_FIRST_CUSTOM = 1000;
/**
* @hide
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index b3d5124..1840567 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -38,6 +38,7 @@
import android.view.WindowManagerGlobal;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
@@ -63,7 +64,12 @@
private static final int MAX_COUNT = 100;
private static final AtomicInteger sCounter = new AtomicInteger(0);
- private static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER;
+
+ /**
+ * @hide
+ */
+ @VisibleForTesting
+ public static final int TRANSACTION_READY_TIMEOUT = 1000 * Build.HW_TIMEOUT_MULTIPLIER;
private static Supplier<Transaction> sTransactionFactory = Transaction::new;
@@ -123,6 +129,14 @@
@GuardedBy("mLock")
private boolean mTimeoutAdded;
+ /**
+ * Disable the timeout for this SSG so it will never be set until there's an explicit call to
+ * add a timeout.
+ */
+ @GuardedBy("mLock")
+ private boolean mTimeoutDisabled;
+
+
private static boolean isLocalBinder(IBinder binder) {
return !(binder instanceof BinderProxy);
}
@@ -223,6 +237,10 @@
*/
public void addSyncCompleteCallback(Executor executor, Runnable runnable) {
synchronized (mLock) {
+ if (mFinished) {
+ executor.execute(runnable);
+ return;
+ }
mSyncCompleteCallbacks.add(new Pair<>(executor, runnable));
}
}
@@ -768,6 +786,21 @@
}
}
+ /**
+ * @hide
+ */
+ public void toggleTimeout(boolean enable) {
+ synchronized (mLock) {
+ mTimeoutDisabled = !enable;
+ if (mTimeoutAdded && !enable) {
+ mHandler.removeCallbacksAndMessages(this);
+ mTimeoutAdded = false;
+ } else if (!mTimeoutAdded && enable) {
+ addTimeout();
+ }
+ }
+ }
+
private void addTimeout() {
synchronized (sHandlerThreadLock) {
if (sHandlerThread == null) {
@@ -777,7 +810,7 @@
}
synchronized (mLock) {
- if (mTimeoutAdded) {
+ if (mTimeoutAdded || mTimeoutDisabled) {
// We only need one timeout for the entire SurfaceSyncGroup since we just want to
// ensure it doesn't stay stuck forever.
return;
diff --git a/core/java/android/window/TaskConstants.java b/core/java/android/window/TaskConstants.java
index 3a04198..e18fd50 100644
--- a/core/java/android/window/TaskConstants.java
+++ b/core/java/android/window/TaskConstants.java
@@ -47,37 +47,31 @@
-2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
- * When a unresizable app is moved in the different configuration, a restart button appears
- * allowing to adapt (~resize) app to the new configuration mocks.
+ * Compat UI components: reachability education, size compat restart
+ * button, letterbox education, restart dialog.
* @hide
*/
- public static final int TASK_CHILD_LAYER_SIZE_COMPAT_RESTART_BUTTON =
- TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_COMPAT_UI = TASK_CHILD_LAYER_REGION_SIZE;
- /**
- * Shown the first time an app is opened in size compat mode in landscape.
- * @hide
- */
- public static final int TASK_CHILD_LAYER_LETTERBOX_EDUCATION = 2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Captions, window frames and resize handlers around task windows.
* @hide
*/
- public static final int TASK_CHILD_LAYER_WINDOW_DECORATIONS = 3 * TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_WINDOW_DECORATIONS = 2 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Overlays the task when going into PIP w/ gesture navigation.
* @hide
*/
public static final int TASK_CHILD_LAYER_RECENTS_ANIMATION_PIP_OVERLAY =
- 4 * TASK_CHILD_LAYER_REGION_SIZE;
+ 3 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Allows other apps to add overlays on the task (i.e. game dashboard)
* @hide
*/
- public static final int TASK_CHILD_LAYER_TASK_OVERLAY = 5 * TASK_CHILD_LAYER_REGION_SIZE;
+ public static final int TASK_CHILD_LAYER_TASK_OVERLAY = 4 * TASK_CHILD_LAYER_REGION_SIZE;
/**
* Z-orders of task child layers other than activities, task fragments and layers interleaved
@@ -87,8 +81,7 @@
@IntDef({
TASK_CHILD_LAYER_TASK_BACKGROUND,
TASK_CHILD_LAYER_LETTERBOX_BACKGROUND,
- TASK_CHILD_LAYER_SIZE_COMPAT_RESTART_BUTTON,
- TASK_CHILD_LAYER_LETTERBOX_EDUCATION,
+ TASK_CHILD_LAYER_COMPAT_UI,
TASK_CHILD_LAYER_WINDOW_DECORATIONS,
TASK_CHILD_LAYER_RECENTS_ANIMATION_PIP_OVERLAY,
TASK_CHILD_LAYER_TASK_OVERLAY
diff --git a/core/proto/android/server/windowmanagertransitiontrace.proto b/core/proto/android/server/windowmanagertransitiontrace.proto
index 25985eb..c3cbc91 100644
--- a/core/proto/android/server/windowmanagertransitiontrace.proto
+++ b/core/proto/android/server/windowmanagertransitiontrace.proto
@@ -55,12 +55,14 @@
optional int64 finish_time_ns = 6; // consider aborted if not provided
required int32 type = 7;
repeated Target targets = 8;
+ optional int32 flags = 9;
}
message Target {
required int32 mode = 1;
required int32 layer_id = 2;
optional int32 window_id = 3; // Not dumped in always on tracing
+ optional int32 flags = 4;
}
message TransitionState {
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 4f91e7a..fb0f3d4 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -71,6 +71,7 @@
import com.android.internal.content.ReferrerIntent;
import org.junit.After;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -95,7 +96,8 @@
// few sequence numbers the framework used to launch the test activity.
private static final int BASE_SEQ = 10000;
- private final ActivityTestRule<TestActivity> mActivityTestRule =
+ @Rule
+ public final ActivityTestRule<TestActivity> mActivityTestRule =
new ActivityTestRule<>(TestActivity.class, true /* initialTouchMode */,
false /* launchActivity */);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index 1b20f67..60111aa 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -2922,14 +2922,15 @@
final float targetX = isLtr
? mTempRect.left - margin
: mTempRect.right + margin - mManageMenu.getWidth();
- final float targetY = mTempRect.bottom - mManageMenu.getHeight();
+ final float menuHeight = getVisibleManageMenuHeight();
+ final float targetY = mTempRect.bottom - menuHeight;
final float xOffsetForAnimation = (isLtr ? 1 : -1) * mManageMenu.getWidth() / 4f;
if (show) {
mManageMenu.setScaleX(0.5f);
mManageMenu.setScaleY(0.5f);
mManageMenu.setTranslationX(targetX - xOffsetForAnimation);
- mManageMenu.setTranslationY(targetY + mManageMenu.getHeight() / 4f);
+ mManageMenu.setTranslationY(targetY + menuHeight / 4f);
mManageMenu.setAlpha(0f);
PhysicsAnimator.getInstance(mManageMenu)
@@ -2955,7 +2956,7 @@
.spring(DynamicAnimation.SCALE_X, 0.5f)
.spring(DynamicAnimation.SCALE_Y, 0.5f)
.spring(DynamicAnimation.TRANSLATION_X, targetX - xOffsetForAnimation)
- .spring(DynamicAnimation.TRANSLATION_Y, targetY + mManageMenu.getHeight() / 4f)
+ .spring(DynamicAnimation.TRANSLATION_Y, targetY + menuHeight / 4f)
.withEndActions(() -> {
mManageMenu.setVisibility(View.INVISIBLE);
if (mExpandedBubble != null && mExpandedBubble.getExpandedView() != null) {
@@ -3272,6 +3273,24 @@
}
/**
+ * Menu height calculated for animation
+ * It takes into account view visibility to get the correct total height
+ */
+ private float getVisibleManageMenuHeight() {
+ float menuHeight = 0;
+
+ for (int i = 0; i < mManageMenu.getChildCount(); i++) {
+ View subview = mManageMenu.getChildAt(i);
+
+ if (subview.getVisibility() == VISIBLE) {
+ menuHeight += subview.getHeight();
+ }
+ }
+
+ return menuHeight;
+ }
+
+ /**
* @return the normalized x-axis position of the bubble stack rounded to 4 decimal places.
*/
public float getNormalizedXPosition() {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 170c0ee..6592292 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -20,6 +20,7 @@
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,11 +47,6 @@
*/
class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Compat UI should be below the Letterbox Education.
- */
- private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1;
-
private final CompatUICallback mCallback;
private final CompatUIConfiguration mCompatUIConfiguration;
@@ -92,7 +88,7 @@
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 1;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
index 0c21c8c..959c50d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/LetterboxEduWindowManager.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -46,12 +47,6 @@
*/
class LetterboxEduWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Letterbox Education should be the topmost child of the Task in case there can be more
- * than one child.
- */
- public static final int Z_ORDER = Integer.MAX_VALUE;
-
private final DialogAnimationController<LetterboxEduDialogLayout> mAnimationController;
private final Transitions mTransitions;
@@ -118,7 +113,7 @@
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 2;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
index b6e396d..a18ab91 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/ReachabilityEduWindowManager.java
@@ -18,6 +18,7 @@
import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -41,11 +42,6 @@
*/
class ReachabilityEduWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The Compat UI should be below the Letterbox Education.
- */
- private static final int Z_ORDER = LetterboxEduWindowManager.Z_ORDER - 1;
-
// The time to wait before hiding the education
private static final long DISAPPEAR_DELAY_MS = 4000L;
@@ -102,7 +98,7 @@
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 1;
}
@Override
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
index aab123a..51e5141 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/RestartDialogWindowManager.java
@@ -17,6 +17,7 @@
package com.android.wm.shell.compatui;
import static android.provider.Settings.Secure.LAUNCHER_TASKBAR_EDUCATION_SHOWING;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_COMPAT_UI;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -47,12 +48,6 @@
*/
class RestartDialogWindowManager extends CompatUIWindowManagerAbstract {
- /**
- * The restart dialog should be the topmost child of the Task in case there can be more
- * than one child.
- */
- private static final int Z_ORDER = Integer.MAX_VALUE;
-
private final DialogAnimationController<RestartDialogLayout> mAnimationController;
private final Transitions mTransitions;
@@ -112,7 +107,7 @@
@Override
protected int getZOrder() {
- return Z_ORDER;
+ return TASK_CHILD_LAYER_COMPAT_UI + 2;
}
@Override
@@ -170,10 +165,10 @@
final Rect taskBounds = getTaskBounds();
final Rect taskStableBounds = getTaskStableBounds();
-
- marginParams.topMargin = taskStableBounds.top - taskBounds.top + mDialogVerticalMargin;
- marginParams.bottomMargin =
- taskBounds.bottom - taskStableBounds.bottom + mDialogVerticalMargin;
+ // only update margins based on taskbar insets
+ marginParams.topMargin = mDialogVerticalMargin;
+ marginParams.bottomMargin = taskBounds.bottom - taskStableBounds.bottom
+ + mDialogVerticalMargin;
dialogContainer.setLayoutParams(marginParams);
}
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a00f401..24de487 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -380,6 +380,9 @@
<service android:name="SystemUIService"
android:exported="true"
/>
+ <service android:name=".wallet.controller.WalletContextualLocationsService"
+ android:exported="true"
+ />
<!-- Service for dumping extremely verbose content during a bug report -->
<service android:name=".dump.SystemUIAuxiliaryDumpService"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 64aa629..331307a0 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -44,6 +44,8 @@
<LinearLayout android:id="@+id/status_bar_contents"
android:layout_width="match_parent"
android:layout_height="match_parent"
+ android:clipChildren="false"
+ android:clipToPadding="false"
android:paddingStart="@dimen/status_bar_padding_start"
android:paddingEnd="@dimen/status_bar_padding_end"
android:paddingTop="@dimen/status_bar_padding_top"
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 10c08bc..9573913 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1832,7 +1832,7 @@
} else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_DPM_STATE_CHANGED,
- getSendingUserId()));
+ getSendingUserId(), 0));
} else if (ACTION_USER_UNLOCKED.equals(action)) {
mHandler.sendMessage(mHandler.obtainMessage(MSG_USER_UNLOCKED,
getSendingUserId(), 0));
diff --git a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
index b86083a..1f13291 100644
--- a/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyboard/data/repository/KeyboardRepository.kt
@@ -106,7 +106,7 @@
}
private fun isPhysicalFullKeyboard(deviceId: Int): Boolean {
- val device = inputManager.getInputDevice(deviceId)
+ val device = inputManager.getInputDevice(deviceId) ?: return false
return !device.isVirtual && device.isFullKeyboard
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
index fbe374c..c0269b8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemBarAttributesListener.kt
@@ -23,10 +23,10 @@
import android.view.WindowInsetsController.Behavior
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.view.AppearanceRegion
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.SysuiStatusBarStateController
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import java.io.PrintWriter
import javax.inject.Inject
@@ -37,7 +37,7 @@
* It is responsible for modifying any attributes if necessary, and then notifying the other
* downstream listeners.
*/
-@CentralSurfacesScope
+@SysUISingleton
class SystemBarAttributesListener
@Inject
internal constructor(
@@ -45,18 +45,14 @@
private val letterboxAppearanceCalculator: LetterboxAppearanceCalculator,
private val statusBarStateController: SysuiStatusBarStateController,
private val lightBarController: LightBarController,
- private val dumpManager: DumpManager,
-) : CentralSurfacesComponent.Startable, StatusBarBoundsProvider.BoundsChangeListener {
+ dumpManager: DumpManager,
+) : Dumpable, StatusBarBoundsProvider.BoundsChangeListener {
private var lastLetterboxAppearance: LetterboxAppearance? = null
private var lastSystemBarAttributesParams: SystemBarAttributesParams? = null
- override fun start() {
- dumpManager.registerDumpable(javaClass.simpleName, this::dump)
- }
-
- override fun stop() {
- dumpManager.unregisterDumpable(javaClass.simpleName)
+ init {
+ dumpManager.registerCriticalDumpable(this)
}
override fun onStatusBarBoundsChanged() {
@@ -128,7 +124,7 @@
private fun shouldUseLetterboxAppearance(letterboxDetails: Array<LetterboxDetails>) =
letterboxDetails.isNotEmpty()
- private fun dump(printWriter: PrintWriter, strings: Array<String>) {
+ override fun dump(printWriter: PrintWriter, strings: Array<String>) {
printWriter.println("lastSystemBarAttributesParams: $lastSystemBarAttributesParams")
printWriter.println("lastLetterboxAppearance: $lastLetterboxAppearance")
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
index f72e74b..7ded90f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
@@ -16,11 +16,7 @@
package com.android.systemui.statusbar.phone.dagger;
-import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
-
-import dagger.Binds;
import dagger.Module;
-import dagger.multibindings.IntoSet;
import dagger.multibindings.Multibinds;
import java.util.Set;
@@ -29,9 +25,4 @@
interface CentralSurfacesStartableModule {
@Multibinds
Set<CentralSurfacesComponent.Startable> multibindStartables();
-
- @Binds
- @IntoSet
- CentralSurfacesComponent.Startable sysBarAttrsListener(
- SystemBarAttributesListener systemBarAttributesListener);
}
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt
new file mode 100644
index 0000000..1c17fc3
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualLocationsService.kt
@@ -0,0 +1,93 @@
+package com.android.systemui.wallet.controller
+
+import android.content.Intent
+import android.os.IBinder
+import android.util.Log
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.LifecycleService
+import androidx.lifecycle.lifecycleScope
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+
+/**
+ * Serves as an intermediary between QuickAccessWalletService and ContextualCardManager (in PCC).
+ * When QuickAccessWalletService has a list of store locations, WalletContextualLocationsService
+ * will send them to ContextualCardManager. When the user enters a store location, this Service
+ * class will be notified, and WalletContextualSuggestionsController will be updated.
+ */
+class WalletContextualLocationsService
+@Inject
+constructor(
+ private val controller: WalletContextualSuggestionsController,
+ private val featureFlags: FeatureFlags,
+) : LifecycleService() {
+ private var listener: IWalletCardsUpdatedListener? = null
+ private var scope: CoroutineScope = this.lifecycleScope
+
+ @VisibleForTesting
+ constructor(
+ controller: WalletContextualSuggestionsController,
+ featureFlags: FeatureFlags,
+ scope: CoroutineScope,
+ ) : this(controller, featureFlags) {
+ this.scope = scope
+ }
+
+ override fun onBind(intent: Intent): IBinder {
+ super.onBind(intent)
+ scope.launch {
+ controller.allWalletCards.collect { cards ->
+ val cardsSize = cards.size
+ Log.i(TAG, "Number of cards registered $cardsSize")
+ listener?.registerNewWalletCards(cards)
+ }
+ }
+ return binder
+ }
+
+ override fun onDestroy() {
+ super.onDestroy()
+ listener = null
+ }
+
+ @VisibleForTesting
+ fun addWalletCardsUpdatedListenerInternal(listener: IWalletCardsUpdatedListener) {
+ if (!featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ return
+ }
+ this.listener = listener // Currently, only one listener at a time is supported
+ // Sends WalletCard objects from QuickAccessWalletService to the listener
+ val cards = controller.allWalletCards.value
+ if (!cards.isEmpty()) {
+ val cardsSize = cards.size
+ Log.i(TAG, "Number of cards registered $cardsSize")
+ listener.registerNewWalletCards(cards)
+ }
+ }
+
+ @VisibleForTesting
+ fun onWalletContextualLocationsStateUpdatedInternal(storeLocations: List<String>) {
+ if (!featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
+ return
+ }
+ Log.i(TAG, "Entered store $storeLocations")
+ controller.setSuggestionCardIds(storeLocations.toSet())
+ }
+
+ private val binder: IWalletContextualLocationsService.Stub
+ = object : IWalletContextualLocationsService.Stub() {
+ override fun addWalletCardsUpdatedListener(listener: IWalletCardsUpdatedListener) {
+ addWalletCardsUpdatedListenerInternal(listener)
+ }
+ override fun onWalletContextualLocationsStateUpdated(storeLocations: List<String>) {
+ onWalletContextualLocationsStateUpdatedInternal(storeLocations)
+ }
+ }
+
+ companion object {
+ private const val TAG = "WalletContextualLocationsService"
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
index 518f5a7..b3ad9b0 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/wallet/controller/WalletContextualSuggestionsController.kt
@@ -36,6 +36,7 @@
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
+import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.emptyFlow
@@ -57,7 +58,8 @@
) {
private val cardsReceivedCallbacks: MutableSet<(List<WalletCard>) -> Unit> = mutableSetOf()
- private val allWalletCards: Flow<List<WalletCard>> =
+ /** All potential cards. */
+ val allWalletCards: StateFlow<List<WalletCard>> =
if (featureFlags.isEnabled(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS)) {
// TODO(b/237409756) determine if we should debounce this so we don't call the service
// too frequently. Also check if the list actually changed before calling callbacks.
@@ -107,12 +109,13 @@
emptyList()
)
} else {
- emptyFlow()
+ MutableStateFlow<List<WalletCard>>(emptyList()).asStateFlow()
}
private val _suggestionCardIds: MutableStateFlow<Set<String>> = MutableStateFlow(emptySet())
private val contextualSuggestionsCardIds: Flow<Set<String>> = _suggestionCardIds.asStateFlow()
+ /** Contextually-relevant cards. */
val contextualSuggestionCards: Flow<List<WalletCard>> =
combine(allWalletCards, contextualSuggestionsCardIds) { cards, ids ->
val ret =
diff --git a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
index 9429d89..efba3e5 100644
--- a/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
+++ b/packages/SystemUI/src/com/android/systemui/wallet/dagger/WalletModule.java
@@ -35,6 +35,8 @@
import dagger.multibindings.IntoMap;
import dagger.multibindings.StringKey;
+import android.app.Service;
+import com.android.systemui.wallet.controller.WalletContextualLocationsService;
/**
* Module for injecting classes in Wallet.
@@ -42,6 +44,12 @@
@Module
public abstract class WalletModule {
+ @Binds
+ @IntoMap
+ @ClassKey(WalletContextualLocationsService.class)
+ abstract Service bindWalletContextualLocationsService(
+ WalletContextualLocationsService service);
+
/** */
@Binds
@IntoMap
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
index f6ff4b2..6f9dedf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyboard/data/repository/KeyboardRepositoryTest.kt
@@ -96,6 +96,16 @@
}
@Test
+ fun emitsDisconnected_whenDeviceWithIdDoesNotExist() =
+ testScope.runTest {
+ val deviceListener = captureDeviceListener()
+ val isKeyboardConnected by collectLastValue(underTest.keyboardConnected)
+
+ deviceListener.onInputDeviceAdded(NULL_DEVICE_ID)
+ assertThat(isKeyboardConnected).isFalse()
+ }
+
+ @Test
fun emitsDisconnected_whenKeyboardDisconnects() =
testScope.runTest {
val deviceListener = captureDeviceListener()
@@ -172,6 +182,7 @@
private const val VIRTUAL_FULL_KEYBOARD_ID = 2
private const val PHYSICAL_NOT_FULL_KEYBOARD_ID = 3
private const val ANOTHER_PHYSICAL_FULL_KEYBOARD_ID = 4
+ private const val NULL_DEVICE_ID = 5
private val INPUT_DEVICES_MAP: Map<Int, InputDevice> =
mapOf(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
new file mode 100644
index 0000000..af1d788
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/wallet/controller/WalletContextualLocationsServiceTest.kt
@@ -0,0 +1,128 @@
+package com.android.systemui.wallet.controller
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Icon
+import android.os.Looper
+import android.service.quickaccesswallet.WalletCard
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.util.mockito.whenever
+import com.google.common.truth.Truth.assertThat
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.test.TestCoroutineScope
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.flow.asStateFlow
+import kotlinx.coroutines.flow.update
+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.Mock
+import org.mockito.Mockito.anySet
+import org.mockito.Mockito.doNothing
+import org.mockito.Mockito.doReturn
+import org.mockito.Mockito.times
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(JUnit4::class)
+@SmallTest
+@kotlinx.coroutines.ExperimentalCoroutinesApi
+class WalletContextualLocationsServiceTest : SysuiTestCase() {
+ @Mock private lateinit var controller: WalletContextualSuggestionsController
+ private var featureFlags = FakeFeatureFlags()
+ private lateinit var underTest: WalletContextualLocationsService
+ private lateinit var testScope: TestScope
+ private var listenerRegisteredCount: Int = 0
+ private val listener: IWalletCardsUpdatedListener.Stub = object : IWalletCardsUpdatedListener.Stub() {
+ override fun registerNewWalletCards(cards: List<WalletCard?>) {
+ listenerRegisteredCount++
+ }
+ }
+
+ @Before
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ doReturn(fakeWalletCards).whenever(controller).allWalletCards
+ doNothing().whenever(controller).setSuggestionCardIds(anySet())
+
+ if (Looper.myLooper() == null) Looper.prepare()
+
+ testScope = TestScope()
+ featureFlags.set(Flags.ENABLE_WALLET_CONTEXTUAL_LOYALTY_CARDS, true)
+ listenerRegisteredCount = 0
+
+ underTest = WalletContextualLocationsService(controller, featureFlags, testScope.backgroundScope)
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun addListener() = testScope.runTest {
+ underTest.addWalletCardsUpdatedListenerInternal(listener)
+ assertThat(listenerRegisteredCount).isEqualTo(1)
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun addStoreLocations() = testScope.runTest {
+ underTest.onWalletContextualLocationsStateUpdatedInternal(ArrayList<String>())
+ verify(controller, times(1)).setSuggestionCardIds(anySet())
+ }
+
+ @Test
+ @kotlinx.coroutines.ExperimentalCoroutinesApi
+ fun updateListenerAndLocationsState() = testScope.runTest {
+ // binds to the service and adds a listener
+ val underTestStub = getInterface
+ underTestStub.addWalletCardsUpdatedListener(listener)
+ assertThat(listenerRegisteredCount).isEqualTo(1)
+
+ // sends a list of card IDs to the controller
+ underTestStub.onWalletContextualLocationsStateUpdated(ArrayList<String>())
+ verify(controller, times(1)).setSuggestionCardIds(anySet())
+
+ // adds another listener
+ fakeWalletCards.update{ updatedFakeWalletCards }
+ runCurrent()
+ assertThat(listenerRegisteredCount).isEqualTo(2)
+
+ // sends another list of card IDs to the controller
+ underTestStub.onWalletContextualLocationsStateUpdated(ArrayList<String>())
+ verify(controller, times(2)).setSuggestionCardIds(anySet())
+ }
+
+ private val fakeWalletCards: MutableStateFlow<List<WalletCard>>
+ get() {
+ val intent = Intent(getContext(), WalletContextualLocationsService::class.java)
+ val pi: PendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val icon: Icon = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888))
+ val walletCards: ArrayList<WalletCard> = ArrayList<WalletCard>()
+ walletCards.add(WalletCard.Builder("card1", icon, "card", pi).build())
+ walletCards.add(WalletCard.Builder("card2", icon, "card", pi).build())
+ return MutableStateFlow<List<WalletCard>>(walletCards)
+ }
+
+ private val updatedFakeWalletCards: List<WalletCard>
+ get() {
+ val intent = Intent(getContext(), WalletContextualLocationsService::class.java)
+ val pi: PendingIntent = PendingIntent.getActivity(getContext(), 0, intent, PendingIntent.FLAG_IMMUTABLE)
+ val icon: Icon = Icon.createWithBitmap(Bitmap.createBitmap(70, 50, Bitmap.Config.ARGB_8888))
+ val walletCards: ArrayList<WalletCard> = ArrayList<WalletCard>()
+ walletCards.add(WalletCard.Builder("card3", icon, "card", pi).build())
+ return walletCards
+ }
+
+ private val getInterface: IWalletContextualLocationsService
+ get() {
+ val intent = Intent()
+ return IWalletContextualLocationsService.Stub.asInterface(underTest.onBind(intent))
+ }
+}
\ No newline at end of file
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index 1a0588e..307f7bf 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -367,7 +367,7 @@
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeDpadKeyEvent(inputDeviceDescriptor.getNativePointer(),
- event.getKeyCode(), event.getAction());
+ event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -380,7 +380,7 @@
"Could not send key event to input device for given token");
}
return mNativeWrapper.writeKeyEvent(inputDeviceDescriptor.getNativePointer(),
- event.getKeyCode(), event.getAction());
+ event.getKeyCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -398,7 +398,7 @@
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeButtonEvent(inputDeviceDescriptor.getNativePointer(),
- event.getButtonCode(), event.getAction());
+ event.getButtonCode(), event.getAction(), event.getEventTimeNanos());
}
}
@@ -412,7 +412,8 @@
}
return mNativeWrapper.writeTouchEvent(inputDeviceDescriptor.getNativePointer(),
event.getPointerId(), event.getToolType(), event.getAction(), event.getX(),
- event.getY(), event.getPressure(), event.getMajorAxisSize());
+ event.getY(), event.getPressure(), event.getMajorAxisSize(),
+ event.getEventTimeNanos());
}
}
@@ -430,7 +431,7 @@
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeRelativeEvent(inputDeviceDescriptor.getNativePointer(),
- event.getRelativeX(), event.getRelativeY());
+ event.getRelativeX(), event.getRelativeY(), event.getEventTimeNanos());
}
}
@@ -448,7 +449,7 @@
"Display id associated with this mouse is not currently targetable");
}
return mNativeWrapper.writeScrollEvent(inputDeviceDescriptor.getNativePointer(),
- event.getXAxisMovement(), event.getYAxisMovement());
+ event.getXAxisMovement(), event.getYAxisMovement(), event.getEventTimeNanos());
}
}
@@ -514,15 +515,19 @@
private static native long nativeOpenUinputTouchscreen(String deviceName, int vendorId,
int productId, String phys, int height, int width);
private static native void nativeCloseUinput(long ptr);
- private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action);
- private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action);
- private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action);
+ private static native boolean nativeWriteDpadKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos);
+ private static native boolean nativeWriteKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos);
+ private static native boolean nativeWriteButtonEvent(long ptr, int buttonCode, int action,
+ long eventTimeNanos);
private static native boolean nativeWriteTouchEvent(long ptr, int pointerId, int toolType,
- int action, float locationX, float locationY, float pressure, float majorAxisSize);
+ int action, float locationX, float locationY, float pressure, float majorAxisSize,
+ long eventTimeNanos);
private static native boolean nativeWriteRelativeEvent(long ptr, float relativeX,
- float relativeY);
+ float relativeY, long eventTimeNanos);
private static native boolean nativeWriteScrollEvent(long ptr, float xAxisMovement,
- float yAxisMovement);
+ float yAxisMovement, long eventTimeNanos);
/** Wrapper around the static native methods for tests. */
@VisibleForTesting
@@ -550,32 +555,37 @@
nativeCloseUinput(ptr);
}
- public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action) {
- return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action);
+ public boolean writeDpadKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteDpadKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
}
- public boolean writeKeyEvent(long ptr, int androidKeyCode, int action) {
- return nativeWriteKeyEvent(ptr, androidKeyCode, action);
+ public boolean writeKeyEvent(long ptr, int androidKeyCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteKeyEvent(ptr, androidKeyCode, action, eventTimeNanos);
}
- public boolean writeButtonEvent(long ptr, int buttonCode, int action) {
- return nativeWriteButtonEvent(ptr, buttonCode, action);
+ public boolean writeButtonEvent(long ptr, int buttonCode, int action,
+ long eventTimeNanos) {
+ return nativeWriteButtonEvent(ptr, buttonCode, action, eventTimeNanos);
}
public boolean writeTouchEvent(long ptr, int pointerId, int toolType, int action,
- float locationX, float locationY, float pressure, float majorAxisSize) {
+ float locationX, float locationY, float pressure, float majorAxisSize,
+ long eventTimeNanos) {
return nativeWriteTouchEvent(ptr, pointerId, toolType,
action, locationX, locationY,
- pressure, majorAxisSize);
+ pressure, majorAxisSize, eventTimeNanos);
}
- public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY) {
- return nativeWriteRelativeEvent(ptr, relativeX, relativeY);
+ public boolean writeRelativeEvent(long ptr, float relativeX, float relativeY,
+ long eventTimeNanos) {
+ return nativeWriteRelativeEvent(ptr, relativeX, relativeY, eventTimeNanos);
}
- public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement) {
- return nativeWriteScrollEvent(ptr, xAxisMovement,
- yAxisMovement);
+ public boolean writeScrollEvent(long ptr, float xAxisMovement, float yAxisMovement,
+ long eventTimeNanos) {
+ return nativeWriteScrollEvent(ptr, xAxisMovement, yAxisMovement, eventTimeNanos);
}
}
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index 12ee131..0713999 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -17,7 +17,6 @@
package android.os;
import android.annotation.IntDef;
-import android.annotation.NonNull;
import android.net.Network;
import com.android.internal.os.BinderCallsStats;
@@ -40,6 +39,8 @@
public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
public static final int CPU_WAKEUP_SUBSYSTEM_WIFI = 2;
public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
+ public static final int CPU_WAKEUP_SUBSYSTEM_SENSOR = 4;
+ public static final int CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA = 5;
/** @hide */
@IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
@@ -47,9 +48,11 @@
CPU_WAKEUP_SUBSYSTEM_ALARM,
CPU_WAKEUP_SUBSYSTEM_WIFI,
CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ CPU_WAKEUP_SUBSYSTEM_SENSOR,
+ CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
})
@Retention(RetentionPolicy.SOURCE)
- @interface CpuWakeupSubsystem {
+ public @interface CpuWakeupSubsystem {
}
/**
@@ -107,19 +110,16 @@
public abstract void noteBinderThreadNativeIds(int[] binderThreadNativeTids);
/**
- * Reports any activity that could potentially have caused the CPU to wake up.
- * Accepts a timestamp to allow free ordering between the event and its reporting.
- * @param subsystem The subsystem this activity should be attributed to.
- * @param elapsedMillis The time when this activity happened in the elapsed timebase.
- * @param uids The uid (or uids) that should be blamed for this activity.
- */
- public abstract void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem,
- long elapsedMillis, @NonNull int... uids);
-
- /**
* Reports a sound trigger recognition event that may have woken up the CPU.
* @param elapsedMillis The time when the event happened in the elapsed timebase.
* @param uid The uid that requested this trigger.
*/
public abstract void noteWakingSoundTrigger(long elapsedMillis, int uid);
+
+ /**
+ * Reports an alarm batch that would have woken up the CPU.
+ * @param elapsedMillis The time at which this alarm batch was scheduled to go off.
+ * @param uids the uids of all apps that have any alarm in this batch.
+ */
+ public abstract void noteWakingAlarmBatch(long elapsedMillis, int... uids);
}
diff --git a/services/core/java/com/android/server/PackageWatchdog.java b/services/core/java/com/android/server/PackageWatchdog.java
index d256aea..f4f5c95 100644
--- a/services/core/java/com/android/server/PackageWatchdog.java
+++ b/services/core/java/com/android/server/PackageWatchdog.java
@@ -580,6 +580,7 @@
PackageHealthObserverImpact.USER_IMPACT_LEVEL_10,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_50,
+ PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_70,
PackageHealthObserverImpact.USER_IMPACT_LEVEL_100})
public @interface PackageHealthObserverImpact {
@@ -590,6 +591,7 @@
/* Actions having medium user impact, user of a device will likely notice. */
int USER_IMPACT_LEVEL_30 = 30;
int USER_IMPACT_LEVEL_50 = 50;
+ int USER_IMPACT_LEVEL_60 = 60;
int USER_IMPACT_LEVEL_70 = 70;
/* Action has high user impact, a last resort, user of a device will be very frustrated. */
int USER_IMPACT_LEVEL_100 = 100;
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index c0b3a90..d140403 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -23,6 +23,7 @@
import static android.Manifest.permission.POWER_SAVER;
import static android.Manifest.permission.UPDATE_DEVICE_STATS;
import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED;
+import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
@@ -51,6 +52,7 @@
import android.os.BatteryManagerInternal;
import android.os.BatteryStats;
import android.os.BatteryStatsInternal;
+import android.os.BatteryStatsInternal.CpuWakeupSubsystem;
import android.os.BatteryUsageStats;
import android.os.BatteryUsageStatsQuery;
import android.os.Binder;
@@ -474,6 +476,8 @@
private int transportToSubsystem(NetworkCapabilities nc) {
if (nc.hasTransport(TRANSPORT_WIFI)) {
return CPU_WAKEUP_SUBSYSTEM_WIFI;
+ } else if (nc.hasTransport(TRANSPORT_CELLULAR)) {
+ return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
}
return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
}
@@ -514,14 +518,32 @@
}
@Override
- public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
- Objects.requireNonNull(uids);
- mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
- }
- @Override
public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, elapsedMillis, uid);
}
+
+ @Override
+ public void noteWakingAlarmBatch(long elapsedMillis, int... uids) {
+ noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, elapsedMillis, uids);
+ }
+ }
+
+ /**
+ * Reports any activity that could potentially have caused the CPU to wake up.
+ * Accepts a timestamp to allow free ordering between the event and its reporting.
+ *
+ * <p>
+ * This method can be called multiple times for the same wakeup and then all attribution
+ * reported will be unioned as long as all reports are made within a small amount of cpu uptime
+ * after the wakeup is reported to batterystats.
+ *
+ * @param subsystem The subsystem this activity should be attributed to.
+ * @param elapsedMillis The time when this activity happened in the elapsed timebase.
+ * @param uids The uid (or uids) that should be blamed for this activity.
+ */
+ void noteCpuWakingActivity(@CpuWakeupSubsystem int subsystem, long elapsedMillis, int... uids) {
+ Objects.requireNonNull(uids);
+ mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
}
@Override
@@ -1267,6 +1289,7 @@
if (callingUid != Process.SYSTEM_UID) {
throw new SecurityException("Calling uid " + callingUid + " is not system uid");
}
+ final long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(elapsedNanos);
final SensorManager sm = mContext.getSystemService(SensorManager.class);
final Sensor sensor = sm.getSensorByHandle(sensorHandle);
@@ -1275,10 +1298,12 @@
+ " received in noteWakeupSensorEvent");
return;
}
- Slog.i(TAG, "Sensor " + sensor + " wakeup event at " + elapsedNanos + " sent to uid "
- + uid);
- // TODO (b/275436924): Remove log and pipe to CpuWakeupStats for wakeup attribution
- // This method should return as quickly as possible. Use mHandler#post to do longer work.
+ if (uid < 0) {
+ Slog.wtf(TAG, "Invalid uid " + uid + " for sensor event with sensor: " + sensor);
+ return;
+ }
+ // TODO (b/278319756): Also pipe in Sensor type for more usefulness.
+ noteCpuWakingActivity(BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR, elapsedMillis, uid);
}
@Override
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 5d92c7f..26b6cb0 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1781,7 +1781,24 @@
} else {
configurePreferredDisplayModeLocked(display);
}
- addDisplayPowerControllerLocked(display);
+ DisplayPowerControllerInterface dpc = addDisplayPowerControllerLocked(display);
+
+ if (dpc != null) {
+ final int leadDisplayId = display.getLeadDisplayIdLocked();
+ updateDisplayPowerControllerLeaderLocked(dpc, leadDisplayId);
+
+ // Loop through all the displays and check if any should follow this one - it could be
+ // that the follower display was added before the lead display.
+ mLogicalDisplayMapper.forEachLocked(d -> {
+ if (d.getLeadDisplayIdLocked() == displayId) {
+ DisplayPowerControllerInterface followerDpc =
+ mDisplayPowerControllers.get(d.getDisplayIdLocked());
+ if (followerDpc != null) {
+ updateDisplayPowerControllerLeaderLocked(followerDpc, displayId);
+ }
+ }
+ });
+ }
mDisplayStates.append(displayId, Display.STATE_UNKNOWN);
@@ -1832,8 +1849,8 @@
}
}
- private void updateDisplayPowerControllerLeaderLocked(DisplayPowerControllerInterface dpc,
- int leadDisplayId) {
+ private void updateDisplayPowerControllerLeaderLocked(
+ @NonNull DisplayPowerControllerInterface dpc, int leadDisplayId) {
if (dpc.getLeadDisplayId() == leadDisplayId) {
// Lead display hasn't changed, nothing to do.
return;
@@ -1851,9 +1868,11 @@
// And then, if it's following, register it with the new one.
if (leadDisplayId != Layout.NO_LEAD_DISPLAY) {
- final DisplayPowerControllerInterface newLead =
+ final DisplayPowerControllerInterface newLeader =
mDisplayPowerControllers.get(leadDisplayId);
- newLead.addDisplayBrightnessFollower(dpc);
+ if (newLeader != null) {
+ newLeader.addDisplayBrightnessFollower(dpc);
+ }
}
}
@@ -1872,6 +1891,7 @@
final DisplayPowerControllerInterface dpc =
mDisplayPowerControllers.removeReturnOld(displayId);
if (dpc != null) {
+ updateDisplayPowerControllerLeaderLocked(dpc, Layout.NO_LEAD_DISPLAY);
dpc.stop();
}
mDisplayStates.delete(displayId);
@@ -3062,10 +3082,11 @@
}
@RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
- private void addDisplayPowerControllerLocked(LogicalDisplay display) {
+ private DisplayPowerControllerInterface addDisplayPowerControllerLocked(
+ LogicalDisplay display) {
if (mPowerHandler == null) {
// initPowerManagement has not yet been called.
- return;
+ return null;
}
if (mBrightnessTracker == null && display.getDisplayIdLocked() == Display.DEFAULT_DISPLAY) {
@@ -3086,7 +3107,7 @@
if (hbmMetadata == null) {
Slog.wtf(TAG, "High Brightness Mode Metadata is null in DisplayManagerService for "
+ "display: " + display.getDisplayIdLocked());
- return;
+ return null;
}
if (DeviceConfig.getBoolean("display_manager",
"use_newly_structured_display_power_controller", true)) {
@@ -3101,6 +3122,7 @@
() -> handleBrightnessChange(display), hbmMetadata, mBootCompleted);
}
mDisplayPowerControllers.append(display.getDisplayIdLocked(), displayPowerController);
+ return displayPowerController;
}
private void handleBrightnessChange(LogicalDisplay display) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index db18c5f..f1efec0 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -789,6 +789,17 @@
}
}
+ @GuardedBy("mLock")
+ private void clearDisplayBrightnessFollowersLocked() {
+ for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+ mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1,
+ /* ambientLux= */ 0), mClock.uptimeMillis());
+ }
+ mDisplayBrightnessFollowers.clear();
+ }
+
@Nullable
@Override
public ParceledListSlice<AmbientBrightnessDayStats> getAmbientBrightnessStats(
@@ -946,6 +957,8 @@
@Override
public void stop() {
synchronized (mLock) {
+ clearDisplayBrightnessFollowersLocked();
+
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
diff --git a/services/core/java/com/android/server/display/DisplayPowerController2.java b/services/core/java/com/android/server/display/DisplayPowerController2.java
index 125dce2..59e112e 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController2.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController2.java
@@ -764,6 +764,8 @@
@Override
public void stop() {
synchronized (mLock) {
+ clearDisplayBrightnessFollowersLocked();
+
mStopped = true;
Message msg = mHandler.obtainMessage(MSG_STOP);
mHandler.sendMessageAtTime(msg, mClock.uptimeMillis());
@@ -2201,6 +2203,17 @@
}
}
+ @GuardedBy("mLock")
+ private void clearDisplayBrightnessFollowersLocked() {
+ for (int i = 0; i < mDisplayBrightnessFollowers.size(); i++) {
+ DisplayPowerControllerInterface follower = mDisplayBrightnessFollowers.valueAt(i);
+ mHandler.postAtTime(() -> follower.setBrightnessToFollow(
+ PowerManager.BRIGHTNESS_INVALID_FLOAT, /* nits= */ -1,
+ /* ambientLux= */ 0), mClock.uptimeMillis());
+ }
+ mDisplayBrightnessFollowers.clear();
+ }
+
@Override
public void dump(final PrintWriter pw) {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/notification/ManagedServices.java b/services/core/java/com/android/server/notification/ManagedServices.java
index 73440b7..12fc263 100644
--- a/services/core/java/com/android/server/notification/ManagedServices.java
+++ b/services/core/java/com/android/server/notification/ManagedServices.java
@@ -140,34 +140,39 @@
* The services that have been bound by us. If the service is also connected, it will also
* be in {@link #mServices}.
*/
+ @GuardedBy("mMutex")
private final ArrayList<Pair<ComponentName, Integer>> mServicesBound = new ArrayList<>();
+ @GuardedBy("mMutex")
private final ArraySet<Pair<ComponentName, Integer>> mServicesRebinding = new ArraySet<>();
// we need these packages to be protected because classes that inherit from it need to see it
protected final Object mDefaultsLock = new Object();
+ @GuardedBy("mDefaultsLock")
protected final ArraySet<ComponentName> mDefaultComponents = new ArraySet<>();
+ @GuardedBy("mDefaultsLock")
protected final ArraySet<String> mDefaultPackages = new ArraySet<>();
// lists the component names of all enabled (and therefore potentially connected)
// app services for current profiles.
- private ArraySet<ComponentName> mEnabledServicesForCurrentProfiles
- = new ArraySet<>();
+ @GuardedBy("mMutex")
+ private final ArraySet<ComponentName> mEnabledServicesForCurrentProfiles = new ArraySet<>();
// Just the packages from mEnabledServicesForCurrentProfiles
- private ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
+ @GuardedBy("mMutex")
+ private final ArraySet<String> mEnabledServicesPackageNames = new ArraySet<>();
// Per user id, list of enabled packages that have nevertheless asked not to be run
- private final android.util.SparseSetArray<ComponentName> mSnoozing =
- new android.util.SparseSetArray<>();
+ @GuardedBy("mSnoozing")
+ private final SparseSetArray<ComponentName> mSnoozing = new SparseSetArray<>();
// List of approved packages or components (by user, then by primary/secondary) that are
// allowed to be bound as managed services. A package or component appearing in this list does
// not mean that we are currently bound to said package/component.
+ @GuardedBy("mApproved")
protected final ArrayMap<Integer, ArrayMap<Boolean, ArraySet<String>>> mApproved =
new ArrayMap<>();
-
// List of packages or components (by user) that are configured to be enabled/disabled
// explicitly by the user
@GuardedBy("mApproved")
protected ArrayMap<Integer, ArraySet<String>> mUserSetServices = new ArrayMap<>();
-
+ @GuardedBy("mApproved")
protected ArrayMap<Integer, Boolean> mIsUserChanged = new ArrayMap<>();
// True if approved services are stored in xml, not settings.
@@ -262,20 +267,18 @@
@NonNull
ArrayMap<Boolean, ArrayList<ComponentName>> resetComponents(String packageName, int userId) {
// components that we want to enable
- ArrayList<ComponentName> componentsToEnable =
- new ArrayList<>(mDefaultComponents.size());
-
+ ArrayList<ComponentName> componentsToEnable;
// components that were removed
- ArrayList<ComponentName> disabledComponents =
- new ArrayList<>(mDefaultComponents.size());
-
+ ArrayList<ComponentName> disabledComponents;
// all components that are enabled now
- ArraySet<ComponentName> enabledComponents =
- new ArraySet<>(getAllowedComponents(userId));
+ ArraySet<ComponentName> enabledComponents = new ArraySet<>(getAllowedComponents(userId));
boolean changed = false;
synchronized (mDefaultsLock) {
+ componentsToEnable = new ArrayList<>(mDefaultComponents.size());
+ disabledComponents = new ArrayList<>(mDefaultComponents.size());
+
// record all components that are enabled but should not be by default
for (int i = 0; i < mDefaultComponents.size() && enabledComponents.size() > 0; i++) {
ComponentName currentDefault = mDefaultComponents.valueAt(i);
@@ -374,14 +377,14 @@
}
}
- pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
- + ") enabled for current profiles:");
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- pw.println(" " + cmpt);
- }
-
synchronized (mMutex) {
+ pw.println(" All " + getCaption() + "s (" + mEnabledServicesForCurrentProfiles.size()
+ + ") enabled for current profiles:");
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ pw.println(" " + cmpt);
+ }
+
pw.println(" Live " + getCaption() + "s (" + mServices.size() + "):");
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
@@ -434,12 +437,12 @@
}
}
- for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
- if (filter != null && !filter.matches(cmpt)) continue;
- cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
- }
synchronized (mMutex) {
+ for (ComponentName cmpt : mEnabledServicesForCurrentProfiles) {
+ if (filter != null && !filter.matches(cmpt)) continue;
+ cmpt.dumpDebug(proto, ManagedServicesProto.ENABLED);
+ }
for (ManagedServiceInfo info : mServices) {
if (filter != null && !filter.matches(info.component)) continue;
info.dumpDebug(proto, ManagedServicesProto.LIVE_SERVICES, this);
@@ -677,7 +680,9 @@
if (isUserChanged == null) { //NLS
userSetComponent = TextUtils.emptyIfNull(userSetComponent);
} else { //NAS
- mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ synchronized (mApproved) {
+ mIsUserChanged.put(resolvedUserId, Boolean.valueOf(isUserChanged));
+ }
userSetComponent = Boolean.valueOf(isUserChanged) ? approved : "";
}
} else {
@@ -688,7 +693,9 @@
if (isUserChanged_Old != null && Boolean.valueOf(isUserChanged_Old)) {
//user_set = true
userSetComponent = approved;
- mIsUserChanged.put(resolvedUserId, true);
+ synchronized (mApproved) {
+ mIsUserChanged.put(resolvedUserId, true);
+ }
needUpgradeUserset = false;
} else {
userSetComponent = "";
@@ -724,8 +731,11 @@
}
void upgradeDefaultsXmlVersion() {
- // check if any defaults are loaded
- int defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ int defaultsSize;
+ synchronized (mDefaultsLock) {
+ // check if any defaults are loaded
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ }
if (defaultsSize == 0) {
// load defaults from current allowed
if (this.mApprovalLevel == APPROVAL_BY_COMPONENT) {
@@ -741,8 +751,10 @@
}
}
}
+ synchronized (mDefaultsLock) {
+ defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
+ }
// if no defaults are loaded, then load from config
- defaultsSize = mDefaultComponents.size() + mDefaultPackages.size();
if (defaultsSize == 0) {
loadDefaultsFromConfig();
}
@@ -806,7 +818,9 @@
}
protected boolean isComponentEnabledForPackage(String pkg) {
- return mEnabledServicesPackageNames.contains(pkg);
+ synchronized (mMutex) {
+ return mEnabledServicesPackageNames.contains(pkg);
+ }
}
protected void setPackageOrComponentEnabled(String pkgOrComponent, int userId,
@@ -959,9 +973,13 @@
}
public void onPackagesChanged(boolean removingPackage, String[] pkgList, int[] uidList) {
- if (DEBUG) Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
- + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
- + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ if (DEBUG) {
+ synchronized (mMutex) {
+ Slog.d(TAG, "onPackagesChanged removingPackage=" + removingPackage
+ + " pkgList=" + (pkgList == null ? null : Arrays.asList(pkgList))
+ + " mEnabledServicesPackageNames=" + mEnabledServicesPackageNames);
+ }
+ }
if (pkgList != null && (pkgList.length > 0)) {
boolean anyServicesInvolved = false;
@@ -975,7 +993,7 @@
}
}
for (String pkgName : pkgList) {
- if (mEnabledServicesPackageNames.contains(pkgName)) {
+ if (isComponentEnabledForPackage(pkgName)) {
anyServicesInvolved = true;
}
if (uidList != null && uidList.length > 0) {
@@ -1299,9 +1317,11 @@
}
final Set<ComponentName> add = new HashSet<>(userComponents);
- ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
- if (snoozed != null) {
- add.removeAll(snoozed);
+ synchronized (mSnoozing) {
+ ArraySet<ComponentName> snoozed = mSnoozing.get(userId);
+ if (snoozed != null) {
+ add.removeAll(snoozed);
+ }
}
componentsToBind.put(userId, add);
@@ -1605,9 +1625,12 @@
}
}
+ @VisibleForTesting
boolean isBound(ComponentName cn, int userId) {
final Pair<ComponentName, Integer> servicesBindingTag = Pair.create(cn, userId);
- return mServicesBound.contains(servicesBindingTag);
+ synchronized (mMutex) {
+ return mServicesBound.contains(servicesBindingTag);
+ }
}
protected boolean isBoundOrRebinding(final ComponentName cn, final int userId) {
@@ -1833,7 +1856,9 @@
public boolean isEnabledForCurrentProfiles() {
if (this.isSystem) return true;
if (this.connection == null) return false;
- return mEnabledServicesForCurrentProfiles.contains(this.component);
+ synchronized (mMutex) {
+ return mEnabledServicesForCurrentProfiles.contains(this.component);
+ }
}
/**
@@ -1877,7 +1902,9 @@
/** convenience method for looking in mEnabledServicesForCurrentProfiles */
public boolean isComponentEnabledForCurrentProfiles(ComponentName component) {
- return mEnabledServicesForCurrentProfiles.contains(component);
+ synchronized (mMutex) {
+ return mEnabledServicesForCurrentProfiles.contains(component);
+ }
}
public static class UserProfiles {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index fc6b4e9..4094b1a 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3484,6 +3484,11 @@
interceptScreenshotChord(SCREENSHOT_KEY_OTHER, 0 /*pressDelay*/);
}
return true;
+ case KeyEvent.KEYCODE_ESCAPE:
+ if (down && repeatCount == 0) {
+ mContext.closeSystemDialogs();
+ }
+ return true;
}
return false;
diff --git a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 1d63489..eb6d28e 100644
--- a/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -17,6 +17,8 @@
package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
@@ -54,10 +56,11 @@
*/
public class CpuWakeupStats {
private static final String TAG = "CpuWakeupStats";
-
private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
private static final String SUBSYSTEM_WIFI_STRING = "Wifi";
private static final String SUBSYSTEM_SOUND_TRIGGER_STRING = "Sound_trigger";
+ private static final String SUBSYSTEM_SENSOR_STRING = "Sensor";
+ private static final String SUBSYSTEM_CELLULAR_DATA_STRING = "Cellular_data";
private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
@VisibleForTesting
static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
@@ -111,6 +114,10 @@
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__WIFI;
case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SOUND_TRIGGER;
+ case CPU_WAKEUP_SUBSYSTEM_SENSOR:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SENSOR;
+ case CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__CELLULAR_DATA;
}
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN;
}
@@ -542,6 +549,10 @@
return CPU_WAKEUP_SUBSYSTEM_WIFI;
case SUBSYSTEM_SOUND_TRIGGER_STRING:
return CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
+ case SUBSYSTEM_SENSOR_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_SENSOR;
+ case SUBSYSTEM_CELLULAR_DATA_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
}
return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
}
@@ -554,6 +565,10 @@
return SUBSYSTEM_WIFI_STRING;
case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
return SUBSYSTEM_SOUND_TRIGGER_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_SENSOR:
+ return SUBSYSTEM_SENSOR_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA:
+ return SUBSYSTEM_CELLULAR_DATA_STRING;
case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
return "Unknown";
}
diff --git a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
index 2007079..0ca5603 100644
--- a/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
+++ b/services/core/java/com/android/server/rollback/RollbackPackageHealthObserver.java
@@ -121,7 +121,7 @@
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
} else if (getAvailableRollback(failedPackage) != null) {
// Rollback is available, we may get a callback into #execute
- impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_30;
+ impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_60;
} else if (anyRollbackAvailable) {
// If any rollbacks are available, we will commit them
impact = PackageHealthObserverImpact.USER_IMPACT_LEVEL_70;
diff --git a/services/core/java/com/android/server/wm/BLASTSyncEngine.java b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
index 45c24c4..7ecc083 100644
--- a/services/core/java/com/android/server/wm/BLASTSyncEngine.java
+++ b/services/core/java/com/android/server/wm/BLASTSyncEngine.java
@@ -168,14 +168,13 @@
class CommitCallback implements Runnable {
// Can run a second time if the action completes after the timeout.
boolean ran = false;
- public void onCommitted() {
+ public void onCommitted(SurfaceControl.Transaction t) {
synchronized (mWm.mGlobalLock) {
if (ran) {
return;
}
mHandler.removeCallbacks(this);
ran = true;
- SurfaceControl.Transaction t = new SurfaceControl.Transaction();
for (WindowContainer wc : wcAwaitingCommit) {
wc.onSyncTransactionCommitted(t);
}
@@ -194,12 +193,12 @@
Slog.e(TAG, "WM sent Transaction to organized, but never received" +
" commit callback. Application ANR likely to follow.");
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
- onCommitted();
-
+ onCommitted(merged);
}
};
CommitCallback callback = new CommitCallback();
- merged.addTransactionCommittedListener((r) -> { r.run(); }, callback::onCommitted);
+ merged.addTransactionCommittedListener(Runnable::run,
+ () -> callback.onCommitted(new SurfaceControl.Transaction()));
mHandler.postDelayed(callback, BLAST_TIMEOUT_DURATION);
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "onTransactionReady");
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 3d00686..3551370 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -18,6 +18,7 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import static android.view.SurfaceControl.HIDDEN;
+import static android.window.TaskConstants.TASK_CHILD_LAYER_LETTERBOX_BACKGROUND;
import android.content.Context;
import android.graphics.Color;
@@ -361,7 +362,8 @@
.setCallsite("LetterboxSurface.createSurface")
.build();
- t.setLayer(mSurface, -1).setColorSpaceAgnostic(mSurface, true);
+ t.setLayer(mSurface, TASK_CHILD_LAYER_LETTERBOX_BACKGROUND)
+ .setColorSpaceAgnostic(mSurface, true);
}
void attachInput(WindowState win) {
diff --git a/services/core/java/com/android/server/wm/TransitionTracer.java b/services/core/java/com/android/server/wm/TransitionTracer.java
index a4c931c..6597d4c 100644
--- a/services/core/java/com/android/server/wm/TransitionTracer.java
+++ b/services/core/java/com/android/server/wm/TransitionTracer.java
@@ -154,6 +154,7 @@
}
outputStream.write(com.android.server.wm.shell.Transition.TYPE, transition.mType);
+ outputStream.write(com.android.server.wm.shell.Transition.FLAGS, transition.getFlags());
for (int i = 0; i < targets.size(); ++i) {
final long changeToken = outputStream
@@ -162,6 +163,7 @@
final Transition.ChangeInfo target = targets.get(i);
final int mode = target.getTransitMode(target.mContainer);
+ final int flags = target.getChangeFlags(target.mContainer);
final int layerId;
if (target.mContainer.mSurfaceControl.isValid()) {
layerId = target.mContainer.mSurfaceControl.getLayerId();
@@ -170,6 +172,7 @@
}
outputStream.write(com.android.server.wm.shell.Target.MODE, mode);
+ outputStream.write(com.android.server.wm.shell.Target.FLAGS, flags);
outputStream.write(com.android.server.wm.shell.Target.LAYER_ID, layerId);
if (mActiveTracingEnabled) {
diff --git a/services/core/jni/com_android_server_companion_virtual_InputController.cpp b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
index 4898d95..ad098b7 100644
--- a/services/core/jni/com_android_server_companion_virtual_InputController.cpp
+++ b/services/core/jni/com_android_server_companion_virtual_InputController.cpp
@@ -233,44 +233,50 @@
// Native methods for VirtualDpad
static bool nativeWriteDpadKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualDpad* virtualDpad = reinterpret_cast<VirtualDpad*>(ptr);
- return virtualDpad->writeDpadKeyEvent(androidKeyCode, action);
+ return virtualDpad->writeDpadKeyEvent(androidKeyCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualKeyboard
static bool nativeWriteKeyEvent(JNIEnv* env, jobject thiz, jlong ptr, jint androidKeyCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualKeyboard* virtualKeyboard = reinterpret_cast<VirtualKeyboard*>(ptr);
- return virtualKeyboard->writeKeyEvent(androidKeyCode, action);
+ return virtualKeyboard->writeKeyEvent(androidKeyCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualTouchscreen
static bool nativeWriteTouchEvent(JNIEnv* env, jobject thiz, jlong ptr, jint pointerId,
jint toolType, jint action, jfloat locationX, jfloat locationY,
- jfloat pressure, jfloat majorAxisSize) {
+ jfloat pressure, jfloat majorAxisSize, jlong eventTimeNanos) {
VirtualTouchscreen* virtualTouchscreen = reinterpret_cast<VirtualTouchscreen*>(ptr);
return virtualTouchscreen->writeTouchEvent(pointerId, toolType, action, locationX, locationY,
- pressure, majorAxisSize);
+ pressure, majorAxisSize,
+ std::chrono::nanoseconds(eventTimeNanos));
}
// Native methods for VirtualMouse
static bool nativeWriteButtonEvent(JNIEnv* env, jobject thiz, jlong ptr, jint buttonCode,
- jint action) {
+ jint action, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeButtonEvent(buttonCode, action);
+ return virtualMouse->writeButtonEvent(buttonCode, action,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static bool nativeWriteRelativeEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat relativeX,
- jfloat relativeY) {
+ jfloat relativeY, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeRelativeEvent(relativeX, relativeY);
+ return virtualMouse->writeRelativeEvent(relativeX, relativeY,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static bool nativeWriteScrollEvent(JNIEnv* env, jobject thiz, jlong ptr, jfloat xAxisMovement,
- jfloat yAxisMovement) {
+ jfloat yAxisMovement, jlong eventTimeNanos) {
VirtualMouse* virtualMouse = reinterpret_cast<VirtualMouse*>(ptr);
- return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement);
+ return virtualMouse->writeScrollEvent(xAxisMovement, yAxisMovement,
+ std::chrono::nanoseconds(eventTimeNanos));
}
static JNINativeMethod methods[] = {
@@ -283,12 +289,12 @@
{"nativeOpenUinputTouchscreen", "(Ljava/lang/String;IILjava/lang/String;II)J",
(void*)nativeOpenUinputTouchscreen},
{"nativeCloseUinput", "(J)V", (void*)nativeCloseUinput},
- {"nativeWriteDpadKeyEvent", "(JII)Z", (void*)nativeWriteDpadKeyEvent},
- {"nativeWriteKeyEvent", "(JII)Z", (void*)nativeWriteKeyEvent},
- {"nativeWriteButtonEvent", "(JII)Z", (void*)nativeWriteButtonEvent},
- {"nativeWriteTouchEvent", "(JIIIFFFF)Z", (void*)nativeWriteTouchEvent},
- {"nativeWriteRelativeEvent", "(JFF)Z", (void*)nativeWriteRelativeEvent},
- {"nativeWriteScrollEvent", "(JFF)Z", (void*)nativeWriteScrollEvent},
+ {"nativeWriteDpadKeyEvent", "(JIIJ)Z", (void*)nativeWriteDpadKeyEvent},
+ {"nativeWriteKeyEvent", "(JIIJ)Z", (void*)nativeWriteKeyEvent},
+ {"nativeWriteButtonEvent", "(JIIJ)Z", (void*)nativeWriteButtonEvent},
+ {"nativeWriteTouchEvent", "(JIIIFFFFJ)Z", (void*)nativeWriteTouchEvent},
+ {"nativeWriteRelativeEvent", "(JFFJ)Z", (void*)nativeWriteRelativeEvent},
+ {"nativeWriteScrollEvent", "(JFFJ)Z", (void*)nativeWriteScrollEvent},
};
int register_android_server_companion_virtual_InputController(JNIEnv* env) {
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 7e5d5aa..4d739d2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -160,6 +160,7 @@
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED;
+import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT;
import static android.app.admin.DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED;
import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_NOT_SUSPENDED;
import static android.app.admin.DevicePolicyManager.PERSONAL_APPS_SUSPENDED_EXPLICITLY;
@@ -533,7 +534,6 @@
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
@@ -1186,9 +1186,9 @@
// Resume logging if all remaining users are affiliated.
maybeResumeDeviceWideLoggingLocked();
}
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.handleUserRemoved(userHandle);
- }
+ }
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.handleUserRemoved(userHandle);
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
@@ -4157,8 +4157,9 @@
checkCanExecuteOrThrowUnsafe(DevicePolicyManager.OPERATION_REMOVE_ACTIVE_ADMIN);
enforceUserUnlocked(userHandle);
+ ActiveAdmin admin;
synchronized (getLockObject()) {
- ActiveAdmin admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
+ admin = getActiveAdminUncheckedLocked(adminReceiver, userHandle);
if (admin == null) {
return;
}
@@ -4169,14 +4170,13 @@
+ adminReceiver);
return;
}
-
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
- if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
- mDevicePolicyEngine.removePoliciesForAdmin(
- EnforcingAdmin.createEnterpriseEnforcingAdmin(
- adminReceiver, userHandle, admin));
- }
+ }
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ adminReceiver, userHandle, admin));
}
}
@@ -13804,8 +13804,6 @@
admin,
new BooleanPolicyValue(hidden),
userId);
- Boolean resolvedPolicy = mDevicePolicyEngine.getResolvedPolicy(
- PolicyDefinition.APPLICATION_HIDDEN(packageName), userId);
result = mInjector.binderWithCleanCallingIdentity(() -> {
try {
// This is a best effort to continue returning the same value that was
@@ -16549,11 +16547,13 @@
hasCallingOrSelfPermission(permission.NOTIFY_PENDING_SYSTEM_UPDATE),
"Only the system update service can broadcast update information");
- if (UserHandle.getCallingUserId() != UserHandle.USER_SYSTEM) {
- Slogf.w(LOG_TAG, "Only the system update service in the system user can broadcast "
- + "update information.");
- return;
- }
+ mInjector.binderWithCleanCallingIdentity(() -> {
+ if (!mUserManager.getUserInfo(UserHandle.getCallingUserId()).isMain()) {
+ Slogf.w(LOG_TAG, "Only the system update service in the main user can broadcast "
+ + "update information.");
+ return;
+ }
+ });
if (!mOwners.saveSystemUpdateInfo(info)) {
// Pending system update hasn't changed, don't send duplicate notification.
@@ -16661,8 +16661,9 @@
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
}
+ EnforcingAdmin enforcingAdmin;
if (isPermissionCheckFlagEnabled()) {
- EnforcingAdmin enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
+ enforcingAdmin = enforcePermissionAndGetEnforcingAdmin(
admin,
MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
callerPackage,
@@ -16686,17 +16687,6 @@
callback.sendResult(null);
return;
}
- // TODO(b/266924257): decide how to handle the internal state if the package doesn't
- // exist, or the permission isn't requested by the app, because we could end up with
- // inconsistent state between the policy engine and package manager. Also a package
- // might get removed or has it's permission updated after we've set the policy.
- mDevicePolicyEngine.setLocalPolicy(
- PolicyDefinition.PERMISSION_GRANT(packageName, permission),
- enforcingAdmin,
- new IntegerPolicyValue(grantState),
- caller.getUserId());
- // TODO: update javadoc to reflect that callback no longer return success/failure
- callback.sendResult(Bundle.EMPTY);
} else {
Preconditions.checkCallAuthorization((caller.hasAdminComponent()
&& (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
@@ -16704,51 +16694,81 @@
|| (caller.hasPackage() && isCallerDelegate(caller,
DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
- long ident = mInjector.binderClearCallingIdentity();
- try {
- boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
- >= android.os.Build.VERSION_CODES.Q;
- if (!isPostQAdmin) {
- // Legacy admins assume that they cannot control pre-M apps
- if (getTargetSdk(packageName, caller.getUserId())
- < android.os.Build.VERSION_CODES.M) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+ >= android.os.Build.VERSION_CODES.Q;
+ if (!isPostQAdmin) {
+ // Legacy admins assume that they cannot control pre-M apps
+ if (getTargetSdk(packageName, caller.getUserId())
+ < android.os.Build.VERSION_CODES.M) {
+ callback.sendResult(null);
+ return;
+ }
+ }
+ if (!isRuntimePermission(permission)) {
callback.sendResult(null);
return;
}
- }
- if (!isRuntimePermission(permission)) {
+ } catch (SecurityException e) {
+ Slogf.e(LOG_TAG, "Could not set permission grant state", e);
callback.sendResult(null);
- return;
+ } finally {
+ mInjector.binderRestoreCallingIdentity(ident);
}
- if (grantState == PERMISSION_GRANT_STATE_GRANTED
- || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
- || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
- AdminPermissionControlParams permissionParams =
- new AdminPermissionControlParams(packageName, permission,
- grantState,
- canAdminGrantSensorsPermissions());
- mInjector.getPermissionControllerManager(caller.getUserHandle())
- .setRuntimePermissionGrantStateByDeviceAdmin(
- caller.getPackageName(),
- permissionParams, mContext.getMainExecutor(),
- (permissionWasSet) -> {
- if (isPostQAdmin && !permissionWasSet) {
- callback.sendResult(null);
- return;
- }
-
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums
- .SET_PERMISSION_GRANT_STATE)
- .setAdmin(caller.getPackageName())
- .setStrings(permission)
- .setInt(grantState)
- .setBoolean(
- /* isDelegate */ isCallerDelegate(caller))
- .write();
-
- callback.sendResult(Bundle.EMPTY);
- });
+ }
+ }
+ // TODO(b/278710449): enable when we stop policy enforecer callback from blocking the main
+ // thread
+ if (false) {
+ // TODO(b/266924257): decide how to handle the internal state if the package doesn't
+ // exist, or the permission isn't requested by the app, because we could end up with
+ // inconsistent state between the policy engine and package manager. Also a package
+ // might get removed or has it's permission updated after we've set the policy.
+ if (grantState == PERMISSION_GRANT_STATE_DEFAULT) {
+ mDevicePolicyEngine.removeLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ caller.getUserId());
+ } else {
+ mDevicePolicyEngine.setLocalPolicy(
+ PolicyDefinition.PERMISSION_GRANT(packageName, permission),
+ enforcingAdmin,
+ new IntegerPolicyValue(grantState),
+ caller.getUserId());
+ }
+ int newState = mInjector.binderWithCleanCallingIdentity(() ->
+ getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId()));
+ if (newState == grantState) {
+ callback.sendResult(Bundle.EMPTY);
+ } else {
+ callback.sendResult(null);
+ }
+ } else {
+ synchronized (getLockObject()) {
+ long ident = mInjector.binderClearCallingIdentity();
+ try {
+ boolean isPostQAdmin = getTargetSdk(caller.getPackageName(), caller.getUserId())
+ >= android.os.Build.VERSION_CODES.Q;
+ if (grantState == PERMISSION_GRANT_STATE_GRANTED
+ || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED
+ || grantState == DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT) {
+ AdminPermissionControlParams permissionParams =
+ new AdminPermissionControlParams(packageName, permission,
+ grantState,
+ canAdminGrantSensorsPermissions());
+ mInjector.getPermissionControllerManager(caller.getUserHandle())
+ .setRuntimePermissionGrantStateByDeviceAdmin(
+ caller.getPackageName(),
+ permissionParams, mContext.getMainExecutor(),
+ (permissionWasSet) -> {
+ if (isPostQAdmin && !permissionWasSet) {
+ callback.sendResult(null);
+ return;
+ }
+ callback.sendResult(Bundle.EMPTY);
+ });
}
} catch (SecurityException e) {
Slogf.e(LOG_TAG, "Could not set permission grant state", e);
@@ -16759,6 +16779,12 @@
}
}
}
+ DevicePolicyEventLogger.createEvent(DevicePolicyEnums.SET_PERMISSION_GRANT_STATE)
+ .setAdmin(caller.getPackageName())
+ .setStrings(permission)
+ .setInt(grantState)
+ .setBoolean(/* isDelegate */ isCallerDelegate(caller))
+ .write();
}
private static final List<String> SENSOR_PERMISSIONS = new ArrayList<>();
@@ -16822,10 +16848,8 @@
if (isFinancedDeviceOwner(caller)) {
enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
- return mInjector.binderWithCleanCallingIdentity(() -> {
- return getPermissionGrantStateForUser(
- packageName, permission, caller, caller.getUserId());
- });
+ return mInjector.binderWithCleanCallingIdentity(() -> getPermissionGrantStateForUser(
+ packageName, permission, caller, caller.getUserId()));
}
}
@@ -18952,7 +18976,7 @@
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19016,7 +19040,7 @@
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19062,7 +19086,7 @@
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19114,7 +19138,7 @@
admin,
MANAGE_DEVICE_POLICY_RESET_PASSWORD,
caller.getPackageName(),
- UserHandle.USER_ALL);
+ userId);
Long currentTokenHandle = mDevicePolicyEngine.getLocalPolicySetByAdmin(
PolicyDefinition.RESET_PASSWORD_TOKEN,
enforcingAdmin,
@@ -19140,10 +19164,17 @@
}
if (result) {
- DevicePolicyEventLogger
- .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
- .setAdmin(caller.getComponentName())
- .write();
+ if (isPermissionCheckFlagEnabled()) {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
+ .setAdmin(callerPackageName)
+ .write();
+ } else {
+ DevicePolicyEventLogger
+ .createEvent(DevicePolicyEnums.RESET_PASSWORD_WITH_TOKEN)
+ .setAdmin(caller.getComponentName())
+ .write();
+ }
}
return result;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
index d65d366..12a8a75 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyEnforcerCallbacks.java
@@ -84,6 +84,7 @@
? DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT
: grantState;
+ // TODO(b/278710449): stop blocking in the main thread
BlockingCallback callback = new BlockingCallback();
// TODO: remove canAdminGrantSensorPermissions once we expose a new method in
// permissionController that doesn't need it.
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index ca857f1..c4aa0bb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -445,7 +445,7 @@
}
@Test
- public void testDisplayBrightnessFollowersRemoval() {
+ public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
DisplayPowerControllerHolder followerDpc = createDisplayPowerController(FOLLOWER_DISPLAY_ID,
FOLLOWER_UNIQUE_ID);
DisplayPowerControllerHolder secondFollowerDpc = createDisplayPowerController(
@@ -520,6 +520,78 @@
}
@Test
+ public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
+ DisplayPowerControllerHolder followerHolder =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
+ DisplayPowerControllerHolder secondFollowerHolder =
+ createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
+ SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+
+ // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for
+ // it to return to.
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener secondFollowerListener =
+ listenerCaptor.getValue();
+ final float initialFollowerBrightness = 0.3f;
+ when(followerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ followerListener.onBrightnessChanged(initialFollowerBrightness);
+ secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+
+ mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc);
+ mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc);
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+
+ // Validate both followers are correctly registered and receiving brightness updates
+ float brightness = 0.6f;
+ float nits = 600;
+ when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
+ when(followerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+
+ clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
+
+ // Stop the lead DPC and validate that the followers go back to their original brightness.
+ mHolder.dpc.stop();
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index 0b97c5c..415adbb 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -91,7 +91,7 @@
private static final int DISPLAY_ID = Display.DEFAULT_DISPLAY;
private static final String UNIQUE_ID = "unique_id_test123";
private static final int FOLLOWER_DISPLAY_ID = DISPLAY_ID + 1;
- private static final String FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_456";
+ private static final String FOLLOWER_UNIQUE_ID = "unique_id_456";
private static final int SECOND_FOLLOWER_DISPLAY_ID = FOLLOWER_DISPLAY_ID + 1;
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
@@ -279,7 +279,7 @@
@Test
public void testProximitySensorListenerNotRegisteredForNonDefaultDisplay() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
// send a display power request
@@ -298,7 +298,7 @@
@Test
public void testDisplayBrightnessFollowers_BothDpcsSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -344,7 +344,7 @@
@Test
public void testDisplayBrightnessFollowers_FollowerDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -372,7 +372,7 @@
@Test
public void testDisplayBrightnessFollowers_LeadDpcDoesNotSupportNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -398,7 +398,7 @@
@Test
public void testDisplayBrightnessFollowers_NeitherDpcSupportsNits() {
DisplayPowerControllerHolder followerDpc =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerRequest dpr = new DisplayPowerRequest();
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -449,9 +449,9 @@
}
@Test
- public void testDisplayBrightnessFollowersRemoval() {
+ public void testDisplayBrightnessFollowersRemoval_RemoveSingleFollower() {
DisplayPowerControllerHolder followerHolder =
- createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_DISPLAY_ID);
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
DisplayPowerControllerHolder secondFollowerHolder =
createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
@@ -525,6 +525,78 @@
}
@Test
+ public void testDisplayBrightnessFollowersRemoval_RemoveAllFollowers() {
+ DisplayPowerControllerHolder followerHolder =
+ createDisplayPowerController(FOLLOWER_DISPLAY_ID, FOLLOWER_UNIQUE_ID);
+ DisplayPowerControllerHolder secondFollowerHolder =
+ createDisplayPowerController(SECOND_FOLLOWER_DISPLAY_ID,
+ SECOND_FOLLOWER_UNIQUE_DISPLAY_ID);
+
+ DisplayPowerRequest dpr = new DisplayPowerRequest();
+ mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ followerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ secondFollowerHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
+ advanceTime(1); // Run updatePowerState
+
+ ArgumentCaptor<BrightnessSetting.BrightnessSettingListener> listenerCaptor =
+ ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(mHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener listener = listenerCaptor.getValue();
+
+ // Set the initial brightness on the DPCs we're going to remove so we have a fixed value for
+ // it to return to.
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(followerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener followerListener = listenerCaptor.getValue();
+ listenerCaptor = ArgumentCaptor.forClass(BrightnessSetting.BrightnessSettingListener.class);
+ verify(secondFollowerHolder.brightnessSetting).registerListener(listenerCaptor.capture());
+ BrightnessSetting.BrightnessSettingListener secondFollowerListener =
+ listenerCaptor.getValue();
+ final float initialFollowerBrightness = 0.3f;
+ when(followerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ when(secondFollowerHolder.brightnessSetting.getBrightness()).thenReturn(
+ initialFollowerBrightness);
+ followerListener.onBrightnessChanged(initialFollowerBrightness);
+ secondFollowerListener.onBrightnessChanged(initialFollowerBrightness);
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+
+ mHolder.dpc.addDisplayBrightnessFollower(followerHolder.dpc);
+ mHolder.dpc.addDisplayBrightnessFollower(secondFollowerHolder.dpc);
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+
+ // Validate both followers are correctly registered and receiving brightness updates
+ float brightness = 0.6f;
+ float nits = 600;
+ when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
+ when(followerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(secondFollowerHolder.automaticBrightnessController.convertToFloatScale(nits))
+ .thenReturn(brightness);
+ when(mHolder.brightnessSetting.getBrightness()).thenReturn(brightness);
+ listener.onBrightnessChanged(brightness);
+ advanceTime(1); // Send messages, run updatePowerState
+ verify(mHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(followerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(brightness), anyFloat(), anyFloat());
+
+ clearInvocations(mHolder.animator, followerHolder.animator, secondFollowerHolder.animator);
+
+ // Stop the lead DPC and validate that the followers go back to their original brightness.
+ mHolder.dpc.stop();
+ advanceTime(1);
+ verify(followerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ verify(secondFollowerHolder.animator).animateTo(eq(initialFollowerBrightness),
+ anyFloat(), anyFloat());
+ clearInvocations(followerHolder.animator, secondFollowerHolder.animator);
+ }
+
+ @Test
public void testDoesNotSetScreenStateForNonDefaultDisplayUntilBootCompleted() {
// We should still set screen state for the default display
DisplayPowerRequest dpr = new DisplayPowerRequest();
diff --git a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
index a140730..35d4ffd 100644
--- a/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/rollback/RollbackPackageHealthObserverTest.java
@@ -145,7 +145,7 @@
observer.onHealthCheckFailed(null,
PackageWatchdog.FAILURE_REASON_NATIVE_CRASH, 1));
// non-native crash for the package
- assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_30,
+ assertEquals(PackageWatchdog.PackageHealthObserverImpact.USER_IMPACT_LEVEL_60,
observer.onHealthCheckFailed(testFailedPackage,
PackageWatchdog.FAILURE_REASON_APP_CRASH, 1));
// non-native crash for a different package
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
index 7e2529a..fd55428 100644
--- a/services/tests/servicestests/res/xml/irq_device_map_3.xml
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -26,4 +26,10 @@
<device name="test.sound_trigger.device">
<subsystem>Sound_trigger</subsystem>
</device>
+ <device name="test.cellular_data.device">
+ <subsystem>Cellular_data</subsystem>
+ </device>
+ <device name="test.sensor.device">
+ <subsystem>Sensor</subsystem>
+ </device>
</irq-device-map>
\ No newline at end of file
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 a4a3e36..c8c1d6f 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
@@ -1160,6 +1160,7 @@
final int fd = 1;
final int keyCode = KeyEvent.KEYCODE_A;
final int action = VirtualKeyEvent.ACTION_UP;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_KEYBOARD, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1167,8 +1168,9 @@
mDeviceImpl.sendKeyEvent(BINDER, new VirtualKeyEvent.Builder()
.setKeyCode(keyCode)
.setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
.build());
- verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action);
+ verify(mNativeWrapperMock).writeKeyEvent(fd, keyCode, action, eventTimeNanos);
}
@Test
@@ -1188,14 +1190,17 @@
final int fd = 1;
final int buttonCode = VirtualMouseButtonEvent.BUTTON_BACK;
final int action = VirtualMouseButtonEvent.ACTION_BUTTON_PRESS;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
- .setAction(action).build());
- verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action);
+ .setAction(action)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeButtonEvent(fd, buttonCode, action, eventTimeNanos);
}
@Test
@@ -1229,13 +1234,17 @@
final int fd = 1;
final float x = -0.2f;
final float y = 0.7f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
- .setRelativeX(x).setRelativeY(y).build());
- verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
+ .setRelativeX(x)
+ .setRelativeY(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y, eventTimeNanos);
}
@Test
@@ -1270,14 +1279,17 @@
final int fd = 1;
final float x = 0.5f;
final float y = 1f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_MOUSE, DISPLAY_ID_1, PHYS, DEVICE_NAME_1,
INPUT_DEVICE_ID);
doReturn(DISPLAY_ID_1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
- .setYAxisMovement(y).build());
- verify(mNativeWrapperMock).writeScrollEvent(fd, x, y);
+ .setYAxisMovement(y)
+ .setEventTimeNanos(eventTimeNanos)
+ .build());
+ verify(mNativeWrapperMock).writeScrollEvent(fd, x, y, eventTimeNanos);
}
@Test
@@ -1318,6 +1330,7 @@
final float x = 100.5f;
final float y = 200.5f;
final int action = VirtualTouchEvent.ACTION_UP;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1327,9 +1340,10 @@
.setAction(action)
.setPointerId(pointerId)
.setToolType(toolType)
+ .setEventTimeNanos(eventTimeNanos)
.build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, Float.NaN,
- Float.NaN);
+ Float.NaN, eventTimeNanos);
}
@Test
@@ -1342,6 +1356,7 @@
final int action = VirtualTouchEvent.ACTION_UP;
final float pressure = 1.0f;
final float majorAxisSize = 10.0f;
+ final long eventTimeNanos = 5000L;
mInputController.addDeviceForTesting(BINDER, fd,
InputController.InputDeviceDescriptor.TYPE_TOUCHSCREEN, DISPLAY_ID_1, PHYS,
DEVICE_NAME_1, INPUT_DEVICE_ID);
@@ -1353,9 +1368,10 @@
.setToolType(toolType)
.setPressure(pressure)
.setMajorAxisSize(majorAxisSize)
+ .setEventTimeNanos(eventTimeNanos)
.build());
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
- majorAxisSize);
+ majorAxisSize, eventTimeNanos);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index dca67d6..76b6a82 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -17,6 +17,8 @@
package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SENSOR;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
@@ -50,6 +52,8 @@
private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
private static final String KERNEL_REASON_WIFI_IRQ = "130 test.wifi.device";
private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.device";
+ private static final String KERNEL_REASON_SENSOR_IRQ = "15 test.sensor.device";
+ private static final String KERNEL_REASON_CELLULAR_DATA_IRQ = "18 test.cellular_data.device";
private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
@@ -110,6 +114,83 @@
}
@Test
+ public void irqAttributionAllCombinations() {
+ final int[] subsystems = new int[] {
+ CPU_WAKEUP_SUBSYSTEM_ALARM,
+ CPU_WAKEUP_SUBSYSTEM_WIFI,
+ CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ CPU_WAKEUP_SUBSYSTEM_SENSOR,
+ CPU_WAKEUP_SUBSYSTEM_CELLULAR_DATA,
+ };
+
+ final String[] kernelReasons = new String[] {
+ KERNEL_REASON_ALARM_IRQ,
+ KERNEL_REASON_WIFI_IRQ,
+ KERNEL_REASON_SOUND_TRIGGER_IRQ,
+ KERNEL_REASON_SENSOR_IRQ,
+ KERNEL_REASON_CELLULAR_DATA_IRQ,
+ };
+
+ final int[] uids = new int[] {
+ TEST_UID_2, TEST_UID_3, TEST_UID_4, TEST_UID_1, TEST_UID_5
+ };
+
+ final int[] procStates = new int[] {
+ TEST_PROC_STATE_2,
+ TEST_PROC_STATE_3,
+ TEST_PROC_STATE_4,
+ TEST_PROC_STATE_1,
+ TEST_PROC_STATE_5
+ };
+
+ final int total = subsystems.length;
+ assertThat(kernelReasons.length).isEqualTo(total);
+ assertThat(uids.length).isEqualTo(total);
+ assertThat(procStates.length).isEqualTo(total);
+
+ for (int mask = 1; mask < (1 << total); mask++) {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3,
+ mHandler);
+ populateDefaultProcStates(obj);
+
+ final long wakeupTime = mRandom.nextLong(123456);
+
+ String combinedKernelReason = null;
+ for (int i = 0; i < total; i++) {
+ if ((mask & (1 << i)) != 0) {
+ combinedKernelReason = (combinedKernelReason == null)
+ ? kernelReasons[i]
+ : String.join(":", combinedKernelReason, kernelReasons[i]);
+ }
+
+ obj.noteWakingActivity(subsystems[i], wakeupTime + 2, uids[i]);
+ }
+ obj.noteWakeupTimeAndReason(wakeupTime, 1, combinedKernelReason);
+
+ assertThat(obj.mWakeupAttribution.size()).isEqualTo(1);
+
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution.size()).isEqualTo(Integer.bitCount(mask));
+
+ for (int i = 0; i < total; i++) {
+ if ((mask & (1 << i)) == 0) {
+ assertThat(attribution.contains(subsystems[i])).isFalse();
+ continue;
+ }
+ assertThat(attribution.contains(subsystems[i])).isTrue();
+ assertThat(attribution.get(subsystems[i]).get(uids[i])).isEqualTo(procStates[i]);
+
+ for (int j = 0; j < total; j++) {
+ if (i == j) {
+ continue;
+ }
+ assertThat(attribution.get(subsystems[i]).indexOfKey(uids[j])).isLessThan(0);
+ }
+ }
+ }
+ }
+
+ @Test
public void alarmIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
index 8fcbf2f..541739d 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ManagedServicesTest.java
@@ -96,6 +96,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
public class ManagedServicesTest extends UiServiceTestCase {
@@ -1920,6 +1921,18 @@
assertTrue(service.isBound(cn_disallowed, 0));
}
+ @Test
+ public void isComponentEnabledForCurrentProfiles_isThreadSafe() throws InterruptedException {
+ for (UserInfo userInfo : mUm.getUsers()) {
+ mService.addApprovedList("pkg1/cmp1:pkg2/cmp2:pkg3/cmp3", userInfo.id, true);
+ }
+ testThreadSafety(() -> {
+ mService.rebindServices(false, 0);
+ assertThat(mService.isComponentEnabledForCurrentProfiles(
+ new ComponentName("pkg1", "cmp1"))).isTrue();
+ }, 20, 30);
+ }
+
private void mockServiceInfoWithMetaData(List<ComponentName> componentNames,
ManagedServices service, ArrayMap<ComponentName, Bundle> metaDatas)
throws RemoteException {
@@ -2298,4 +2311,38 @@
return false;
}
}
+
+ /**
+ * Helper method to test the thread safety of some operations.
+ *
+ * <p>Runs the supplied {@code operationToTest}, {@code nRunsPerThread} times,
+ * concurrently using {@code nThreads} threads, and waits for all of them to finish.
+ */
+ private static void testThreadSafety(Runnable operationToTest, int nThreads,
+ int nRunsPerThread) throws InterruptedException {
+ final CountDownLatch startLatch = new CountDownLatch(1);
+ final CountDownLatch doneLatch = new CountDownLatch(nThreads);
+
+ for (int i = 0; i < nThreads; i++) {
+ Runnable threadRunnable = () -> {
+ try {
+ startLatch.await();
+ for (int j = 0; j < nRunsPerThread; j++) {
+ operationToTest.run();
+ }
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ } finally {
+ doneLatch.countDown();
+ }
+ };
+ new Thread(threadRunnable, "Test Thread #" + i).start();
+ }
+
+ // Ready set go
+ startLatch.countDown();
+
+ // Wait for all test threads to be done.
+ doneLatch.await();
+ }
}
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index f12b53a..fe7cd4a 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -89,6 +89,12 @@
<activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
+ <activity android:name="android.server.wm.scvh.SurfaceSyncGroupActivity"
+ android:screenOrientation="locked"
+ android:turnScreenOn="true"
+ android:theme="@style/WhiteBackgroundTheme"
+ android:exported="true"/>
+
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java
new file mode 100644
index 0000000..9db647a
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceSyncGroupTests.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.window.SurfaceSyncGroup.TRANSACTION_READY_TIMEOUT;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.app.Instrumentation;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.platform.test.annotations.Presubmit;
+import android.server.wm.scvh.SurfaceSyncGroupActivity;
+import android.view.SurfaceControl;
+import android.view.View;
+import android.view.ViewTreeObserver;
+import android.view.WindowManager;
+import android.view.cts.surfacevalidator.BitmapPixelChecker;
+import android.window.SurfaceSyncGroup;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+@Presubmit
+public class SurfaceSyncGroupTests {
+ private static final String TAG = "SurfaceSyncGroupTests";
+
+ @Rule
+ public ActivityTestRule<SurfaceSyncGroupActivity> mActivityRule = new ActivityTestRule<>(
+ SurfaceSyncGroupActivity.class);
+
+ private SurfaceSyncGroupActivity mActivity;
+
+ Instrumentation mInstrumentation;
+
+ private final HandlerThread mHandlerThread = new HandlerThread("applyTransaction");
+ private Handler mHandler;
+
+ @Before
+ public void setup() {
+ mActivity = mActivityRule.getActivity();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ mHandlerThread.start();
+ mHandler = mHandlerThread.getThreadHandler();
+ }
+
+ @Test
+ public void testOverlappingSyncsEnsureOrder_WhenTimeout() throws InterruptedException {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.format = PixelFormat.TRANSLUCENT;
+
+ CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
+ CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
+ final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first");
+ final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
+ final SurfaceSyncGroup infiniteSsg = new SurfaceSyncGroup(TAG + "-infinite");
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ firstSsg.addTransaction(t);
+
+ View backgroundView = mActivity.getBackgroundView();
+ firstSsg.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
+
+ addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
+
+ assertTrue("Failed to draw two frames",
+ secondDrawCompleteLatch.await(5, TimeUnit.SECONDS));
+
+ mHandler.postDelayed(() -> {
+ // Don't add a markSyncReady for the first sync group until after it's added to another
+ // SSG to ensure the timeout is longer than the second frame's timeout. The infinite SSG
+ // will never complete to ensure it reaches the timeout, but only after the second SSG
+ // had a chance to reach its timeout.
+ infiniteSsg.add(firstSsg, null /* runnable */);
+ firstSsg.markSyncReady();
+ }, 200);
+
+ assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
+ bothSyncGroupsComplete.await(5, TimeUnit.SECONDS));
+
+ validateScreenshot();
+ }
+
+ @Test
+ public void testOverlappingSyncsEnsureOrder_WhileHoldingTransaction()
+ throws InterruptedException {
+ WindowManager.LayoutParams params = new WindowManager.LayoutParams();
+ params.format = PixelFormat.TRANSLUCENT;
+
+ CountDownLatch secondDrawCompleteLatch = new CountDownLatch(1);
+ CountDownLatch bothSyncGroupsComplete = new CountDownLatch(2);
+
+ final SurfaceSyncGroup firstSsg = new SurfaceSyncGroup(TAG + "-first",
+ transaction -> mHandler.postDelayed(() -> {
+ try {
+ assertTrue("Failed to draw two frames",
+ secondDrawCompleteLatch.await(5, TimeUnit.SECONDS));
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ transaction.apply();
+ }, TRANSACTION_READY_TIMEOUT + 200));
+ final SurfaceSyncGroup secondSsg = new SurfaceSyncGroup(TAG + "-second");
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ firstSsg.addTransaction(t);
+
+ View backgroundView = mActivity.getBackgroundView();
+ firstSsg.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(() -> backgroundView.setBackgroundColor(Color.RED)));
+ firstSsg.markSyncReady();
+
+ addSecondSyncGroup(secondSsg, secondDrawCompleteLatch, bothSyncGroupsComplete);
+
+ assertTrue("Failed to wait for both SurfaceSyncGroups to apply",
+ bothSyncGroupsComplete.await(5, TimeUnit.SECONDS));
+
+ validateScreenshot();
+ }
+
+ private void addSecondSyncGroup(SurfaceSyncGroup surfaceSyncGroup,
+ CountDownLatch waitForSecondDraw, CountDownLatch bothSyncGroupsComplete) {
+ View backgroundView = mActivity.getBackgroundView();
+ ViewTreeObserver viewTreeObserver = backgroundView.getViewTreeObserver();
+ viewTreeObserver.registerFrameCommitCallback(() -> mHandler.post(() -> {
+ surfaceSyncGroup.add(backgroundView.getRootSurfaceControl(),
+ () -> mActivity.runOnUiThread(
+ () -> backgroundView.setBackgroundColor(Color.BLUE)));
+
+ SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.addTransactionCommittedListener(Runnable::run, bothSyncGroupsComplete::countDown);
+ surfaceSyncGroup.addTransaction(t);
+ surfaceSyncGroup.markSyncReady();
+ viewTreeObserver.registerFrameCommitCallback(waitForSecondDraw::countDown);
+ }));
+ }
+
+ private void validateScreenshot() {
+ Bitmap screenshot = mInstrumentation.getUiAutomation().takeScreenshot(
+ mActivity.getWindow());
+ assertNotNull("Failed to generate a screenshot", screenshot);
+ Bitmap swBitmap = screenshot.copy(Bitmap.Config.ARGB_8888, false);
+ screenshot.recycle();
+
+ BitmapPixelChecker pixelChecker = new BitmapPixelChecker(Color.BLUE);
+ int halfWidth = swBitmap.getWidth() / 2;
+ int halfHeight = swBitmap.getHeight() / 2;
+ // We don't need to check all the pixels since we only care that at least some of them are
+ // blue. If the buffers were submitted out of order, all the pixels will be red.
+ Rect bounds = new Rect(halfWidth, halfHeight, halfWidth + 10, halfHeight + 10);
+ int numMatchingPixels = pixelChecker.getNumMatchingPixels(swBitmap, bounds);
+ assertEquals("Expected 100 received " + numMatchingPixels + " matching pixels", 100,
+ numMatchingPixels);
+
+ swBitmap.recycle();
+ }
+}