Merge "Do not wake up all displays" into tm-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 528be3c..a09f39f 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -348,7 +348,8 @@
new SparseArray<>();
private AppStateTrackerImpl mAppStateTracker;
- private boolean mAppStandbyParole;
+ @VisibleForTesting
+ boolean mAppStandbyParole;
/**
* A container to keep rolling window history of previous times when an alarm was sent to
@@ -1891,6 +1892,9 @@
(AppStateTrackerImpl) LocalServices.getService(AppStateTracker.class);
mAppStateTracker.addListener(mForceAppStandbyListener);
+ final BatteryManager bm = getContext().getSystemService(BatteryManager.class);
+ mAppStandbyParole = bm.isCharging();
+
mClockReceiver.scheduleTimeTickEvent();
mClockReceiver.scheduleDateChangedEvent();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index 3ba4df8..dfd019d 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -265,14 +265,6 @@
)
);
- /**
- * This array essentially stores the state of mActiveServices array.
- * The ith index stores the job present on the ith JobServiceContext.
- * We manipulate this array until we arrive at what jobs should be running on
- * what JobServiceContext.
- */
- JobStatus[] mRecycledAssignContextIdToJobMap = new JobStatus[MAX_JOB_CONTEXTS_COUNT];
-
private final ArraySet<ContextAssignment> mRecycledChanged = new ArraySet<>();
private final ArraySet<ContextAssignment> mRecycledIdle = new ArraySet<>();
private final ArraySet<ContextAssignment> mRecycledPreferredUidOnly = new ArraySet<>();
@@ -580,7 +572,6 @@
final List<JobServiceContext> activeServices = mActiveServices;
// To avoid GC churn, we recycle the arrays.
- JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
final ArraySet<ContextAssignment> changed = mRecycledChanged;
final ArraySet<ContextAssignment> idle = mRecycledIdle;
final ArraySet<ContextAssignment> preferredUidOnly = mRecycledPreferredUidOnly;
@@ -638,7 +629,7 @@
idle.add(assignment);
}
if (DEBUG) {
- Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs initial"));
+ Slog.d(TAG, printAssignments("running jobs initial", stoppable, preferredUidOnly));
}
mWorkCountTracker.onCountDone();
@@ -747,7 +738,8 @@
}
}
if (DEBUG) {
- Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
+ Slog.d(TAG, printAssignments("running jobs final",
+ stoppable, preferredUidOnly, changed));
Slog.d(TAG, "assignJobsToContexts: " + mWorkCountTracker.toString());
}
@@ -1283,13 +1275,26 @@
return s.toString();
}
- private static String printContextIdToJobMap(JobStatus[] map, String initial) {
- StringBuilder s = new StringBuilder(initial + ": ");
- for (int i=0; i<map.length; i++) {
- s.append("(")
- .append(map[i] == null? -1: map[i].getJobId())
- .append(map[i] == null? -1: map[i].getUid())
- .append(")" );
+ private static String printAssignments(String header, ArraySet<ContextAssignment>... list) {
+ final StringBuilder s = new StringBuilder(header + ": ");
+ for (int l = 0; l < list.length; ++l) {
+ ArraySet<ContextAssignment> assignments = list[l];
+ for (int c = 0; c < assignments.size(); ++c) {
+ final ContextAssignment assignment = assignments.valueAt(c);
+ final JobStatus job = assignment.newJob == null
+ ? assignment.context.getRunningJobLocked() : assignment.newJob;
+
+ if (l > 0 || c > 0) {
+ s.append(" ");
+ }
+ s.append("(").append(assignment.context.getId()).append("=");
+ if (job == null) {
+ s.append("nothing");
+ } else {
+ s.append(job.getJobId()).append("/").append(job.getUid());
+ }
+ s.append(")");
+ }
}
return s.toString();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
index f91472b..daf1ee1 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/PendingJobQueue.java
@@ -182,8 +182,10 @@
if (earliestQueue != null) {
final JobStatus job = earliestQueue.next();
// Change the front of the queue if we've pulled pullLimit jobs from the current head
+ // or we're dealing with test jobs
// or the current head has no more jobs to provide.
if (++mPullCount >= pullLimit
+ || (job != null && earliestQueue.peekNextOverrideState() != job.overrideState)
|| earliestQueue.peekNextTimestamp() == AppJobQueue.NO_NEXT_TIMESTAMP) {
mOrderedQueues.poll();
if (earliestQueue.peekNextTimestamp() != AppJobQueue.NO_NEXT_TIMESTAMP) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index 7db358c..80f3fea 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -623,7 +623,8 @@
* @param elapsedRealtime current time
* @param screenTimeThresholds Array of screen times, in ascending order, first one is 0
* @param elapsedTimeThresholds Array of elapsed time, in ascending order, first one is 0
- * @return The index whose values the app's used time exceeds (in both arrays)
+ * @return The index whose values the app's used time exceeds (in both arrays) or {@code -1} to
+ * indicate that the app has never been used.
*/
int getThresholdIndex(String packageName, int userId, long elapsedRealtime,
long[] screenTimeThresholds, long[] elapsedTimeThresholds) {
@@ -631,14 +632,13 @@
AppUsageHistory appUsageHistory = getPackageHistory(userHistory, packageName,
elapsedRealtime, false);
// If we don't have any state for the app, assume never used
- if (appUsageHistory == null) return screenTimeThresholds.length - 1;
+ if (appUsageHistory == null || appUsageHistory.lastUsedElapsedTime < 0
+ || appUsageHistory.lastUsedScreenTime < 0) {
+ return -1;
+ }
- long screenOnDelta = appUsageHistory.lastUsedScreenTime >= 0
- ? getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime
- : Long.MAX_VALUE;
- long elapsedDelta = appUsageHistory.lastUsedElapsedTime >= 0
- ? getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime
- : Long.MAX_VALUE;
+ long screenOnDelta = getScreenOnTime(elapsedRealtime) - appUsageHistory.lastUsedScreenTime;
+ long elapsedDelta = getElapsedTime(elapsedRealtime) - appUsageHistory.lastUsedElapsedTime;
if (DEBUG) Slog.d(TAG, packageName
+ " lastUsedScreen=" + appUsageHistory.lastUsedScreenTime
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 0839faa..1e4ecc2 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -898,11 +898,9 @@
}
}
- final long elapsedLastUsedByUserTimeDelta = app.lastUsedByUserElapsedTime >= 0
- ? elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
- : Long.MAX_VALUE;
- if (app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
- && elapsedLastUsedByUserTimeDelta
+ if (app.lastUsedByUserElapsedTime >= 0
+ && app.lastRestrictAttemptElapsedTime > app.lastUsedByUserElapsedTime
+ && elapsedTimeAdjusted - app.lastUsedByUserElapsedTime
>= mInjector.getAutoRestrictedBucketDelayMs()) {
newBucket = STANDBY_BUCKET_RESTRICTED;
reason = app.lastRestrictReason;
@@ -974,7 +972,7 @@
long elapsedRealtime) {
int bucketIndex = mAppIdleHistory.getThresholdIndex(packageName, userId,
elapsedRealtime, mAppStandbyScreenThresholds, mAppStandbyElapsedThresholds);
- return THRESHOLD_BUCKETS[bucketIndex];
+ return bucketIndex >= 0 ? THRESHOLD_BUCKETS[bucketIndex] : STANDBY_BUCKET_NEVER;
}
private void notifyBatteryStats(String packageName, int userId, boolean idle) {
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index b28db82..e5e13a8 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -1108,7 +1108,7 @@
method @RequiresPermission("android.permission.NOTIFY_PENDING_SYSTEM_UPDATE") public void notifyPendingSystemUpdate(long, boolean);
method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public boolean packageHasActiveAdmins(String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS, android.Manifest.permission.PROVISION_DEMO_DEVICE}) public void provisionFullyManagedDevice(@NonNull android.app.admin.FullyManagedDeviceProvisioningParams) throws android.app.admin.ProvisioningException;
- method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
+ method @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index 9c8639d..95ac799 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -630,8 +630,9 @@
/**
* Delete uid from the ActivityManagerService PendingStartActivityUids list.
* @param uid uid
+ * @param nowElapsed starting time of updateOomAdj
*/
- public abstract void deletePendingTopUid(int uid);
+ public abstract void deletePendingTopUid(int uid, long nowElapsed);
/**
* Is the uid in ActivityManagerService PendingStartActivityUids list?
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index ac979c4..3cc2b56 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -381,6 +381,10 @@
* Force the global system in or out of touch mode. This can be used if your
* instrumentation relies on the UI being in one more or the other when it starts.
*
+ * <p><b>Note:</b> Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method
+ * will only take effect if the instrumentation was sourced from a process with
+ * {@code MODIFY_TOUCH_MODE_STATE} internal permission granted (shell already have it).
+ *
* @param inTouch Set to true to be in touch mode, false to be in focus mode.
*/
public void setInTouchMode(boolean inTouch) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 82e5ebf..df6627c 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -6197,7 +6197,7 @@
* organization-owned managed profile.
*
* <p>The caller must hold the
- * {@link android.Manifest.permission#SEND_LOST_MODE_LOCATION_UPDATES} permission.
+ * {@link android.Manifest.permission#TRIGGER_LOST_MODE} permission.
*
* <p> Not for use by third-party applications.
*
@@ -6207,7 +6207,7 @@
* @hide
*/
@SystemApi
- @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES)
+ @RequiresPermission(android.Manifest.permission.TRIGGER_LOST_MODE)
public void sendLostModeLocationUpdate(@NonNull @CallbackExecutor Executor executor,
@NonNull Consumer<Boolean> callback) {
throwIfParentInstance("sendLostModeLocationUpdate");
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index a1983ca..914b321 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -469,6 +469,10 @@
/**
* Called when the top activity is changed.
*
+ * <p>Note: When there are no activities running on the virtual display, the
+ * {@link #onDisplayEmpty(int)} will be called. If the value topActivity is cached, it
+ * should be cleared when {@link #onDisplayEmpty(int)} is called.
+ *
* @param displayId The display ID on which the activity change happened.
* @param topActivity The component name of the top activity.
*/
diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java
index 450e09a..88ef054 100644
--- a/core/java/android/content/pm/PackageInstaller.java
+++ b/core/java/android/content/pm/PackageInstaller.java
@@ -419,8 +419,8 @@
public @interface FileLocation{}
/**
- * The installer did not call SessionParams#setPackageSource(int) to specify the package
- * source.
+ * The installer did not call {@link PackageInstaller.SessionParams#setPackageSource(int)} to
+ * specify the package source.
*/
public static final int PACKAGE_SOURCE_UNSPECIFIED = 0;
@@ -444,8 +444,8 @@
/**
* Code indicating that the package being installed comes from a file that was downloaded to
- * the device by the user. For use in place of PACKAGE_SOURCE_LOCAL_FILE when the installer
- * knows the package was downloaded.
+ * the device by the user. For use in place of {@link #PACKAGE_SOURCE_LOCAL_FILE} when the
+ * installer knows the package was downloaded.
*/
public static final int PACKAGE_SOURCE_DOWNLOADED_FILE = 4;
@@ -1984,7 +1984,13 @@
}
/**
- * Sets the apk package installation source.
+ * Optionally indicate the package source of the app being installed. This is
+ * informational and may be used as a signal by the system.
+ *
+ * An installer should specify {@link #PACKAGE_SOURCE_OTHER} if no other package source
+ * constant adequately reflects the source for this session.
+ *
+ * The default value is {@link #PACKAGE_SOURCE_UNSPECIFIED}.
*/
public void setPackageSource(@PackageSourceType int packageSource) {
this.packageSource = packageSource;
@@ -2991,7 +2997,8 @@
}
/**
- * Gets the apk package installation source.
+ * Get the package source that was set in
+ * {@link PackageInstaller.SessionParams#setPackageSource(int)}.
*/
public @PackageSourceType int getPackageSource() {
return packageSource;
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index a13579d..406281d 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -401,7 +401,7 @@
public void relayout(WindowManager.LayoutParams attrs,
WindowlessWindowManager.ResizeCompleteCallback callback) {
mViewRoot.setLayoutParams(attrs, false);
- mViewRoot.setReportNextDraw();
+ mViewRoot.setReportNextDraw(true /* syncBuffer */);
mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback);
}
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
index 4138556..c04b096 100644
--- a/core/java/android/view/SurfaceView.java
+++ b/core/java/android/view/SurfaceView.java
@@ -50,6 +50,7 @@
import com.android.internal.view.SurfaceCallbackHelper;
import java.util.ArrayList;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;
@@ -203,8 +204,6 @@
private int mSurfaceFlags = SurfaceControl.HIDDEN;
- private int mPendingReportDraws;
-
/**
* Transaction that should be used from the render thread. This transaction is only thread safe
* with other calls directly from the render thread.
@@ -212,11 +211,6 @@
private final SurfaceControl.Transaction mRtTransaction = new SurfaceControl.Transaction();
/**
- * Used on the main thread to set the transaction that will be synced with the main window.
- */
- private final Transaction mSyncTransaction = new Transaction();
-
- /**
* Transaction that should be used whe
* {@link HardwareRenderer.FrameDrawingCallback#onFrameDraw} is invoked. All
* frame callbacks can use the same transaction since they will be thread safe
@@ -391,31 +385,12 @@
}
}
- private void performDrawFinished(@Nullable Transaction t) {
- if (t != null) {
- mSyncTransaction.merge(t);
+ private void performDrawFinished() {
+ mDrawFinished = true;
+ if (mAttachedToWindow) {
+ mParent.requestTransparentRegion(SurfaceView.this);
+ invalidate();
}
-
- if (mPendingReportDraws > 0) {
- mDrawFinished = true;
- if (mAttachedToWindow) {
- mParent.requestTransparentRegion(SurfaceView.this);
- notifyDrawFinished();
- invalidate();
- }
- } else {
- Log.e(TAG, System.identityHashCode(this) + "finished drawing"
- + " but no pending report draw (extra call"
- + " to draw completion runnable?)");
- }
- }
-
- void notifyDrawFinished() {
- ViewRootImpl viewRoot = getViewRootImpl();
- if (viewRoot != null) {
- viewRoot.pendingDrawFinished(mSyncTransaction);
- }
- mPendingReportDraws--;
}
@Override
@@ -438,10 +413,6 @@
mGlobalListenersAdded = false;
}
- while (mPendingReportDraws > 0) {
- notifyDrawFinished();
- }
-
mRequestedVisible = false;
updateSurface();
@@ -993,10 +964,17 @@
return;
}
- final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
- translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
final boolean redrawNeeded = sizeChanged || creating || hintChanged
|| (mVisible && !mDrawFinished);
+ final TransactionCallback transactionCallback =
+ redrawNeeded ? new TransactionCallback() : null;
+ if (redrawNeeded && viewRoot.wasRelayoutRequested() && viewRoot.isInSync()) {
+ mBlastBufferQueue.syncNextTransaction(
+ false /* acquireSingleBuffer */,
+ transactionCallback::onTransactionReady);
+ }
+ final boolean realSizeChanged = performSurfaceTransaction(viewRoot,
+ translator, creating, sizeChanged, hintChanged, surfaceUpdateTransaction);
try {
SurfaceHolder.Callback[] callbacks = null;
@@ -1015,9 +993,7 @@
mIsCreating = true;
if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
+ "visibleChanged -- surfaceCreated");
- if (callbacks == null) {
- callbacks = getSurfaceCallbacks();
- }
+ callbacks = getSurfaceCallbacks();
for (SurfaceHolder.Callback c : callbacks) {
c.surfaceCreated(mSurfaceHolder);
}
@@ -1035,32 +1011,7 @@
}
}
if (redrawNeeded) {
- if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
- + "surfaceRedrawNeeded");
- if (callbacks == null) {
- callbacks = getSurfaceCallbacks();
- }
-
- final boolean wasRelayoutRequested = viewRoot.wasRelayoutRequested();
- if (wasRelayoutRequested && (mBlastBufferQueue != null)) {
- mBlastBufferQueue.syncNextTransaction(
- false /* acquireSingleBuffer */,
- this::onDrawFinished);
- }
- mPendingReportDraws++;
- viewRoot.drawPending();
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() -> {
- if (mBlastBufferQueue != null) {
- mBlastBufferQueue.stopContinuousSyncTransaction();
- }
- // If relayout was requested, then a callback from BBQ will
- // be invoked with the sync transaction. onDrawFinished will be
- // called in there
- if (!wasRelayoutRequested) {
- onDrawFinished(null);
- }
- });
- sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+ redrawNeeded(callbacks, transactionCallback);
}
}
} finally {
@@ -1079,6 +1030,64 @@
}
}
+ private void redrawNeeded(SurfaceHolder.Callback[] callbacks,
+ @Nullable TransactionCallback transactionCallback) {
+ if (DEBUG) {
+ Log.i(TAG, System.identityHashCode(this) + " surfaceRedrawNeeded");
+ }
+ final SurfaceHolder.Callback[] capturedCallbacks =
+ callbacks == null ? getSurfaceCallbacks() : callbacks;
+
+ ViewRootImpl viewRoot = getViewRootImpl();
+ boolean isVriSync = viewRoot.addToSync(syncBufferCallback ->
+ redrawNeededAsync(capturedCallbacks, () -> {
+ if (mBlastBufferQueue != null) {
+ mBlastBufferQueue.stopContinuousSyncTransaction();
+ }
+
+ Transaction t = null;
+ if (transactionCallback != null && mBlastBufferQueue != null) {
+ t = transactionCallback.waitForTransaction();
+ }
+ // If relayout was requested, then a callback from BBQ will
+ // be invoked with the sync transaction. onDrawFinished will be
+ // called in there
+ syncBufferCallback.onBufferReady(t);
+ onDrawFinished();
+ }));
+
+ // If isVriSync, then everything was setup in the addToSync.
+ if (isVriSync) {
+ return;
+ }
+
+ redrawNeededAsync(capturedCallbacks, this::onDrawFinished);
+ }
+
+ private void redrawNeededAsync(SurfaceHolder.Callback[] callbacks,
+ Runnable callbacksCollected) {
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(callbacksCollected);
+ sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
+ }
+
+ private static class TransactionCallback {
+ private final CountDownLatch mCountDownLatch = new CountDownLatch(1);
+ private Transaction mTransaction;
+
+ Transaction waitForTransaction() {
+ try {
+ mCountDownLatch.await();
+ } catch (InterruptedException e) {
+ }
+ return mTransaction;
+ }
+
+ void onTransactionReady(Transaction t) {
+ mTransaction = t;
+ mCountDownLatch.countDown();
+ }
+ }
+
/**
* Copy the Surface from the SurfaceControl or the blast adapter.
*
@@ -1189,13 +1198,13 @@
mBlastBufferQueue.update(mBlastSurfaceControl, mSurfaceWidth, mSurfaceHeight, mFormat);
}
- private void onDrawFinished(@Nullable Transaction t) {
+ private void onDrawFinished() {
if (DEBUG) {
Log.i(TAG, System.identityHashCode(this) + " "
+ "finishedDrawing");
}
- runOnUiThread(() -> performDrawFinished(t));
+ runOnUiThread(this::performDrawFinished);
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 2613c1a..a3e6945 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -587,11 +587,16 @@
boolean mReportNextDraw;
+
/**
- * Set whether the draw should use blast sync. This is in case the draw is canceled,
- * but will be rescheduled. We still want the next draw to be sync.
+ * Set whether the draw should send the buffer to system server. When set to true, VRI will
+ * create a sync transaction with BBQ and send the resulting buffer to system server. If false,
+ * VRI will not try to sync a buffer in BBQ, but still report when a draw occurred.
*/
- boolean mNextDrawUseBlastSync;
+ private boolean mSyncBuffer = false;
+
+ int mSyncSeqId = 0;
+ int mLastSyncSeqId = 0;
boolean mFullRedrawNeeded;
boolean mNewSurfaceNeeded;
@@ -812,6 +817,10 @@
return mHandwritingInitiator;
}
+ private final SurfaceSyncer mSurfaceSyncer = new SurfaceSyncer();
+ private int mLastSyncId = -1;
+ private SurfaceSyncer.SyncBufferCallback mSyncBufferCallback;
+
/**
* Keeps track of the last frame number that was attempted to draw. Should only be accessed on
* the RenderThread.
@@ -849,8 +858,6 @@
* integer back over relayout.
*/
private Bundle mRelayoutBundle = new Bundle();
- private int mSyncSeqId = 0;
- private int mLastSyncSeqId = 0;
private String mTag = TAG;
@@ -2654,7 +2661,6 @@
private void performTraversals() {
// cache mView since it is used so much below...
final View host = mView;
-
if (DBG) {
System.out.println("======================================");
System.out.println("performTraversals");
@@ -2899,8 +2905,6 @@
mView.onSystemBarAppearanceChanged(mDispatchedSystemBarAppearance);
}
}
- final boolean wasReportNextDraw = mReportNextDraw;
- boolean useBlastSync = mNextDrawUseBlastSync;
if (mFirst || windowShouldResize || viewVisibilityChanged || params != null
|| mForceNextWindowRelayout) {
@@ -2939,9 +2943,7 @@
Log.d(mTag, "Relayout called with blastSync");
}
reportNextDraw();
- if (isHardwareEnabled()) {
- useBlastSync = true;
- }
+ mSyncBuffer = true;
}
final boolean surfaceControlChanged =
@@ -3177,7 +3179,7 @@
// done to achieve a more hermetic fix for S, but it's entirely
// possible that checking the most recent value is actually more
// correct here.
- if (!mStopped || wasReportNextDraw) {
+ if (!mStopped || mReportNextDraw) {
if (mWidth != host.getMeasuredWidth() || mHeight != host.getMeasuredHeight()
|| dispatchApplyInsets || updatedConfiguration) {
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width,
@@ -3246,7 +3248,7 @@
prepareSurfaces();
}
- final boolean didLayout = layoutRequested && (!mStopped || wasReportNextDraw);
+ final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);
boolean triggerGlobalLayoutListener = didLayout
|| mAttachInfo.mRecomputeGlobalAttributes;
if (didLayout) {
@@ -3439,51 +3441,37 @@
mImeFocusController.onTraversal(hasWindowFocus, mWindowAttributes);
- // Remember if we must report the next draw.
if ((relayoutResult & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
reportNextDraw();
}
- boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;
- if (mBLASTDrawConsumer != null) {
- useBlastSync = true;
+ boolean cancelAndRedraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw();
+ if (!cancelAndRedraw) {
+ createSyncIfNeeded();
}
- if (!cancelDraw) {
+ if (!isViewVisible) {
+ if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
+ for (int i = 0; i < mPendingTransitions.size(); ++i) {
+ mPendingTransitions.get(i).endChangingAnimations();
+ }
+ mPendingTransitions.clear();
+ }
+
+ if (mSyncBufferCallback != null) {
+ mSyncBufferCallback.onBufferReady(null);
+ }
+ } else if (cancelAndRedraw) {
+ // Try again
+ scheduleTraversals();
+ } else {
if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
for (int i = 0; i < mPendingTransitions.size(); ++i) {
mPendingTransitions.get(i).startChangingAnimations();
}
mPendingTransitions.clear();
}
- performDraw(useBlastSync);
- mNextDrawUseBlastSync = false;
- } else {
- if (isViewVisible) {
- // Try again
- mNextDrawUseBlastSync = useBlastSync;
- scheduleTraversals();
- } else {
- if (mPendingTransitions != null && mPendingTransitions.size() > 0) {
- for (int i = 0; i < mPendingTransitions.size(); ++i) {
- mPendingTransitions.get(i).endChangingAnimations();
- }
- mPendingTransitions.clear();
- }
-
- // We may never draw since it's not visible. Report back that we're finished
- // drawing.
- if (!wasReportNextDraw && mReportNextDraw) {
- mReportNextDraw = false;
- pendingDrawFinished();
- }
-
- // Make sure the consumer is not waiting if the view root was just made invisible.
- if (mBLASTDrawConsumer != null) {
- mBLASTDrawConsumer.accept(null);
- mBLASTDrawConsumer = null;
- }
- }
+ performDraw();
}
if (mAttachInfo.mContentCaptureEvents != null) {
@@ -3492,6 +3480,48 @@
mIsInTraversal = false;
mRelayoutRequested = false;
+
+ if (!cancelAndRedraw) {
+ mReportNextDraw = false;
+ mSyncBufferCallback = null;
+ mSyncBuffer = false;
+ if (mLastSyncId != -1) {
+ mSurfaceSyncer.markSyncReady(mLastSyncId);
+ mLastSyncId = -1;
+ }
+ }
+ }
+
+ private void createSyncIfNeeded() {
+ // Started a sync already.
+ if (mLastSyncId != -1) {
+ return;
+ }
+
+ Consumer<Transaction> syncConsumer = null;
+ final int seqId = mSyncSeqId;
+
+ if (mBLASTDrawConsumer != null) {
+ syncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+ } else if (mReportNextDraw) {
+ syncConsumer = transaction -> {
+ mSurfaceChangedTransaction.merge(transaction);
+ reportDrawFinished(seqId);
+ };
+ }
+
+ if (syncConsumer != null) {
+ final Consumer<Transaction> capturedSyncConsumer = syncConsumer;
+ mLastSyncId = mSurfaceSyncer.setupSync(transaction -> {
+ // Callback will be invoked on executor thread so post to main thread.
+ mHandler.postAtFrontOfQueue(() -> capturedSyncConsumer.accept(transaction));
+ });
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setup new sync id=" + mLastSyncId);
+ }
+ mSurfaceSyncer.addToSync(mLastSyncId, mSyncTarget);
+ }
}
private void notifyContentCatpureEvents() {
@@ -4105,57 +4135,13 @@
}
}
- /**
- * A count of the number of calls to pendingDrawFinished we
- * require to notify the WM drawing is complete.
- */
- int mDrawsNeededToReport = 0;
-
- /**
- * Delay notifying WM of draw finished until
- * a balanced call to pendingDrawFinished.
- */
- void drawPending() {
- mDrawsNeededToReport++;
- }
-
- void pendingDrawFinished(Transaction t) {
- if (mDrawsNeededToReport == 0) {
- throw new RuntimeException("Unbalanced drawPending/pendingDrawFinished calls");
- }
-
- if (t != null) {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Merging transaction into main window transaction");
- }
- mSurfaceChangedTransaction.merge(t);
- }
-
- mDrawsNeededToReport--;
- if (mDrawsNeededToReport == 0) {
- reportDrawFinished();
- } else if (DEBUG_BLAST) {
- Log.d(mTag, "pendingDrawFinished. Waiting on draw reported mDrawsNeededToReport="
- + mDrawsNeededToReport);
- }
- }
-
- void pendingDrawFinished() {
- pendingDrawFinished(null);
- }
-
- private void postDrawFinished() {
- mHandler.sendEmptyMessage(MSG_DRAW_FINISHED);
- }
-
- private void reportDrawFinished() {
+ private void reportDrawFinished(int seqId) {
if (DEBUG_BLAST) {
- Log.d(mTag, "reportDrawFinished");
+ Log.d(mTag, "reportDrawFinished " + Debug.getCallers(5));
}
- mDrawsNeededToReport = 0;
try {
- mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction, Integer.MAX_VALUE);
+ mWindowSession.finishDrawing(mWindow, mSurfaceChangedTransaction, seqId);
} catch (RemoteException e) {
Log.e(mTag, "Unable to report draw finished", e);
mSurfaceChangedTransaction.apply();
@@ -4171,6 +4157,19 @@
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
}
+ boolean addToSync(SurfaceSyncer.SyncTarget syncable) {
+ if (mLastSyncId == -1) {
+ return false;
+ }
+ mSurfaceSyncer.addToSync(mLastSyncId, syncable);
+ return true;
+ }
+
+
+ public boolean isInSync() {
+ return mLastSyncId != -1;
+ }
+
private void addFrameCommitCallbackIfNeeded() {
if (!isHardwareEnabled()) {
return;
@@ -4201,188 +4200,82 @@
});
}
- private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync(
- boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) {
- return didProduceBuffer -> {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameCommittedCallback "
- + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum
- + " didProduceBuffer=" + didProduceBuffer);
- }
-
- // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
- // draw attempt. The next transaction and transaction complete callback were only set
- // for the current draw attempt.
- final Transaction pendingTransactions;
- if (!didProduceBuffer) {
- mBlastBufferQueue.syncNextTransaction(null);
- // Get the transactions that were sent to mergeWithNextTransaction since the
- // frame didn't draw on this vsync. It's possible the frame will draw later, but
- // it's better to not be sync than to block on a frame that may never come.
- pendingTransactions = mBlastBufferQueue.gatherPendingTransactions(
- mRtLastAttemptedDrawFrameNum);
- if (!useBlastSync && !reportNextDraw) {
- pendingTransactions.apply();
- }
- } else {
- pendingTransactions = null;
- }
- // Post at front of queue so the buffer can be processed immediately and allow RT
- // to continue processing new buffers. If RT tries to process buffers before the sync
- // buffer is applied, the new buffers will not get acquired and could result in a
- // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free
- // buffer.
- mHandler.postAtFrontOfQueue(() -> {
- if (!didProduceBuffer && useBlastSync) {
- mSurfaceChangedTransaction.merge(pendingTransactions);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
- }
- }
-
- // This is to ensure pendingDrawFinished is only called exactly one time per draw
- // attempt when reportNextDraw is true. Since, we sometimes create a sync
- // transaction callback, the callback will handle calling pendingDrawFinished.
- // However, there are cases where the transaction callback may not be called.
- // 1. If useBlastSync is false, then we know that a sync transaction callback was
- // not created so we won't invoke pendingDrawFinished there.
- // 2. If the draw didn't produce a frame, didProduceBuffer == false, then we know
- // the sync transaction callback will not be invoked even if one was set up.
- if (reportNextDraw && (!didProduceBuffer || !useBlastSync)) {
- pendingDrawFinished();
- }
- });
-
- };
- }
-
@Nullable
- private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync,
- boolean reportNextDraw) {
+ private void registerFrameDrawingCallbackForBlur() {
if (!isHardwareEnabled()) {
- return null;
+ return;
}
final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
- if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw && !mHasPendingTransactions) {
- return null;
- }
-
- final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
-
- if (DEBUG_BLAST) {
- Log.d(mTag, "Creating frameDrawingCallback"
- + " nextDrawUseBlastSync=" + useBlastSync
- + " reportNextDraw=" + reportNextDraw
- + " hasBlurUpdates=" + hasBlurUpdates
- + " hasBlastSyncConsumer=" + (blastSyncConsumer != null)
- + " mHasPendingTransactions=" + mHasPendingTransactions);
+ if (!needsCallbackForBlur) {
+ return;
}
final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
- needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
- final boolean hasPendingTransactions = mHasPendingTransactions;
- mHasPendingTransactions = false;
+ mBlurRegionAggregator.getBlurRegionsCopyForRT();
// The callback will run on the render thread.
- return new FrameDrawingCallback() {
+ registerRtFrameCallback((frame) -> mBlurRegionAggregator
+ .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates));
+ }
+
+ private void registerCallbackForPendingTransactions() {
+ registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ mBlastBufferQueue.applyPendingTransactions(frame);
+ return null;
+ }
+
+ return didProduceBuffer -> {
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.applyPendingTransactions(frame);
+ }
+ };
+
+ }
+
@Override
public void onFrameDraw(long frame) {
}
-
- @Override
- public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
- if (DEBUG_BLAST) {
- Log.d(mTag,
- "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
- + frame + ".");
- }
-
- mRtLastAttemptedDrawFrameNum = frame;
-
- if (needsCallbackForBlur) {
- mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame,
- blurRegionsForFrame, hasBlurUpdates);
- }
-
- if (mBlastBufferQueue == null) {
- return null;
- }
-
- if (!useBlastSync && !reportNextDraw && !hasPendingTransactions) {
- return null;
- }
-
- // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
- // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
- // any blast sync or commit callback, and the code should directly call
- // pendingDrawFinished.
- if ((syncResult
- & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
- if (reportNextDraw) {
- mHandler.postAtFrontOfQueue(() -> pendingDrawFinished());
- }
- return null;
- }
-
- if (DEBUG_BLAST) {
- Log.d(mTag, "Setting up sync and frameCommitCallback");
- }
-
- if (useBlastSync) {
- // Frame callbacks will always occur after submitting draw requests and before
- // the draw actually occurs. This will ensure that we set the next transaction
- // for the frame that's about to get drawn and not on a previous frame.
- mBlastBufferQueue.syncNextTransaction(
- t -> {
- mHandler.postAtFrontOfQueue(() -> {
- mSurfaceChangedTransaction.merge(t);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
- }
-
- if (reportNextDraw) {
- pendingDrawFinished();
- }
- });
- });
- }
-
- return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw,
- blastSyncConsumer);
- }
- };
+ });
}
- private void performDraw(boolean useBlastSync) {
+ private void performDraw() {
if (mAttachInfo.mDisplayState == Display.STATE_OFF && !mReportNextDraw) {
return;
} else if (mView == null) {
return;
}
- final boolean fullRedrawNeeded = mFullRedrawNeeded || mReportNextDraw || useBlastSync;
+ final boolean fullRedrawNeeded = mFullRedrawNeeded || mSyncBufferCallback != null;
mFullRedrawNeeded = false;
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync,
- mReportNextDraw);
- if (frameDrawingCallback != null) {
- mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback);
- }
+ registerFrameDrawingCallbackForBlur();
addFrameCommitCallbackIfNeeded();
- boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw);
+
+ boolean usingAsyncReport = isHardwareEnabled() && mSyncBufferCallback != null;
+ if (usingAsyncReport) {
+ registerCallbacksForSync(mSyncBuffer, mSyncBufferCallback);
+ } else if (mHasPendingTransactions) {
+ // These callbacks are only needed if there's no sync involved and there were calls to
+ // applyTransactionOnDraw. These callbacks check if the draw failed for any reason and
+ // apply those transactions directly so they don't get stuck forever.
+ registerCallbackForPendingTransactions();
+ }
+ mHasPendingTransactions = false;
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
mAttachInfo.mThreadedRenderer.setFrameCallback(null);
usingAsyncReport = false;
- mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback);
}
} finally {
mIsDrawing = false;
@@ -4400,7 +4293,6 @@
}
if (mReportNextDraw) {
- mReportNextDraw = false;
// if we're using multi-thread renderer, wait for the window frame draws
if (mWindowDrawCountDown != null) {
@@ -4421,7 +4313,11 @@
}
if (mSurfaceHolder != null && mSurface.isValid()) {
- SurfaceCallbackHelper sch = new SurfaceCallbackHelper(this::postDrawFinished);
+ final SurfaceSyncer.SyncBufferCallback syncBufferCallback = mSyncBufferCallback;
+ SurfaceCallbackHelper sch = new SurfaceCallbackHelper(() ->
+ mHandler.post(() -> syncBufferCallback.onBufferReady(null)));
+ mSyncBufferCallback = null;
+
SurfaceHolder.Callback callbacks[] = mSurfaceHolder.getCallbacks();
sch.dispatchSurfaceRedrawNeededAsync(mSurfaceHolder, callbacks);
@@ -4429,9 +4325,11 @@
if (mAttachInfo.mThreadedRenderer != null) {
mAttachInfo.mThreadedRenderer.fence();
}
- pendingDrawFinished();
}
}
+ if (mSyncBufferCallback != null && !usingAsyncReport) {
+ mSyncBufferCallback.onBufferReady(null);
+ }
if (mPerformContentCapture) {
performContentCaptureInitialReport();
}
@@ -5440,7 +5338,6 @@
private static final int MSG_REQUEST_KEYBOARD_SHORTCUTS = 26;
private static final int MSG_UPDATE_POINTER_ICON = 27;
private static final int MSG_POINTER_CAPTURE_CHANGED = 28;
- private static final int MSG_DRAW_FINISHED = 29;
private static final int MSG_INSETS_CHANGED = 30;
private static final int MSG_INSETS_CONTROL_CHANGED = 31;
private static final int MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED = 32;
@@ -5503,8 +5400,6 @@
return "MSG_UPDATE_POINTER_ICON";
case MSG_POINTER_CAPTURE_CHANGED:
return "MSG_POINTER_CAPTURE_CHANGED";
- case MSG_DRAW_FINISHED:
- return "MSG_DRAW_FINISHED";
case MSG_INSETS_CHANGED:
return "MSG_INSETS_CHANGED";
case MSG_INSETS_CONTROL_CHANGED:
@@ -5735,9 +5630,6 @@
final boolean hasCapture = msg.arg1 != 0;
handlePointerCaptureChanged(hasCapture);
} break;
- case MSG_DRAW_FINISHED: {
- pendingDrawFinished();
- } break;
case MSG_SYSTEM_GESTURE_EXCLUSION_CHANGED: {
systemGestureExclusionChanged();
} break;
@@ -8066,7 +7958,6 @@
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
-
mRelayoutRequested = true;
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
@@ -8639,7 +8530,8 @@
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private void dispatchResized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, boolean forceLayout,
- boolean alwaysConsumeSystemBars, int displayId, int seqId, int resizeMode) {
+ boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, int resizeMode) {
+
Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT : MSG_RESIZED);
SomeArgs args = SomeArgs.obtain();
final boolean sameProcessCall = (Binder.getCallingPid() == android.os.Process.myPid());
@@ -8649,8 +8541,9 @@
args.argi1 = forceLayout ? 1 : 0;
args.argi2 = alwaysConsumeSystemBars ? 1 : 0;
args.argi3 = displayId;
- args.argi4 = seqId;
+ args.argi4 = syncSeqId;
args.argi5 = resizeMode;
+
msg.obj = args;
mHandler.sendMessage(msg);
}
@@ -9942,8 +9835,8 @@
}
private void reportNextDraw() {
- if (mReportNextDraw == false) {
- drawPending();
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "reportNextDraw " + Debug.getCallers(5));
}
mReportNextDraw = true;
}
@@ -9954,9 +9847,14 @@
* This method is only supposed to be used to speed up the interaction from SystemUI and window
* manager when waiting for the first frame to be drawn when turning on the screen. DO NOT USE
* unless you fully understand this interaction.
+ *
+ * @param syncBuffer If true, the transaction that contains the buffer from the draw should be
+ * sent to system to be synced. If false, VRI will not try to sync the buffer,
+ * but only report back that a buffer was drawn.
* @hide
*/
- public void setReportNextDraw() {
+ public void setReportNextDraw(boolean syncBuffer) {
+ mSyncBuffer = syncBuffer;
reportNextDraw();
invalidate();
}
@@ -10035,11 +9933,11 @@
@Override
public void resized(ClientWindowFrames frames, boolean reportDraw,
MergedConfiguration mergedConfiguration, boolean forceLayout,
- boolean alwaysConsumeSystemBars, int displayId, int seqId, int resizeMode) {
+ boolean alwaysConsumeSystemBars, int displayId, int syncSeqId, int resizeMode) {
final ViewRootImpl viewAncestor = mViewAncestor.get();
if (viewAncestor != null) {
viewAncestor.dispatchResized(frames, reportDraw, mergedConfiguration, forceLayout,
- alwaysConsumeSystemBars, displayId, seqId, resizeMode);
+ alwaysConsumeSystemBars, displayId, syncSeqId, resizeMode);
}
}
@@ -10982,14 +10880,15 @@
return mWindowSession;
}
- private void registerCallbacksForSync(
+ private void registerCallbacksForSync(boolean syncBuffer,
final SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
if (!isHardwareEnabled()) {
- // TODO: correctly handle when hardware disabled
- syncBufferCallback.onBufferReady(null);
return;
}
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "registerCallbacksForSync syncBuffer=" + syncBuffer);
+ }
mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
@Override
public void onFrameDraw(long frame) {
@@ -11018,7 +10917,9 @@
Log.d(mTag, "Setting up sync and frameCommitCallback");
}
- mBlastBufferQueue.syncNextTransaction(t -> syncBufferCallback.onBufferReady(t));
+ if (syncBuffer) {
+ mBlastBufferQueue.syncNextTransaction(syncBufferCallback::onBufferReady);
+ }
return didProduceBuffer -> {
if (DEBUG_BLAST) {
@@ -11032,18 +10933,40 @@
// were only set for the current draw attempt.
if (!didProduceBuffer) {
mBlastBufferQueue.syncNextTransaction(null);
+
// Gather the transactions that were sent to mergeWithNextTransaction
// since the frame didn't draw on this vsync. It's possible the frame will
// draw later, but it's better to not be sync than to block on a frame that
// may never come.
syncBufferCallback.onBufferReady(
mBlastBufferQueue.gatherPendingTransactions(frame));
+ return;
+ }
+
+ // If we didn't request to sync a buffer, then we won't get the
+ // syncNextTransaction callback. Instead, just report back to the Syncer so it
+ // knows that this sync request is complete.
+ if (!syncBuffer) {
+ syncBufferCallback.onBufferReady(null);
}
};
}
});
}
- public final SurfaceSyncer.SyncTarget mSyncTarget =
- syncBufferCallback -> registerCallbacksForSync(syncBufferCallback);
+ public final SurfaceSyncer.SyncTarget mSyncTarget = this::readyToSync;
+
+ private void readyToSync(SurfaceSyncer.SyncBufferCallback syncBufferCallback) {
+ if (mSyncBufferCallback != null) {
+ Log.d(mTag, "Already set sync for the next draw.");
+ mSyncBufferCallback.onBufferReady(null);
+ }
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting syncFrameCallback");
+ }
+ mSyncBufferCallback = syncBufferCallback;
+ if (!mIsInTraversal && !mTraversalScheduled) {
+ scheduleTraversals();
+ }
+ }
}
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 07db91f..6fa6d39 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -3077,11 +3077,26 @@
}
/**
- * Like {@link #showAutofillDialog(View)} but for virtual views.
+ * If autofill suggestions for a
+ * <a href="{@docRoot}reference/android/service/autofill/Dataset.html#FillDialogUI">
+ * dialog-style UI</a> are available for virtual {@code view}, shows a dialog allowing the user
+ * to select a suggestion and returns {@code true}.
+ * <p>
+ * The dialog may not be shown if the autofill service does not support it, if the autofill
+ * request has not returned a response yet, if the dialog was shown previously, or if the
+ * input method is already shown.
+ * <p>
+ * It is recommended apps to call this method the first time a user focuses on
+ * an autofill-able form, and to avoid showing the input method if the dialog is shown. If
+ * this method returns {@code false}, you should then instead show the input method (assuming
+ * that is how the view normally handles the focus event). If the user re-focuses on the view,
+ * you should not call this method again so as to not disrupt usage of the input method.
*
- * @param virtualId id identifying the virtual child inside the parent view.
+ * @param view the view hosting the virtual view hierarchy which is used to show autofill
+ * suggestions.
+ * @param virtualId id identifying the virtual view inside the host view.
+ * @return {@code true} if the autofill dialog is being shown
*/
- // TODO(b/210926084): Consider whether to include the one-time show logic within this method.
public boolean showAutofillDialog(@NonNull View view, int virtualId) {
Objects.requireNonNull(view);
if (shouldShowAutofillDialog(getAutofillId(view, virtualId))) {
diff --git a/core/java/android/widget/MediaController.java b/core/java/android/widget/MediaController.java
index 9c9baf3..f1dc5e7 100644
--- a/core/java/android/widget/MediaController.java
+++ b/core/java/android/widget/MediaController.java
@@ -16,6 +16,7 @@
package android.widget;
+import android.annotation.NonNull;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.res.Resources;
@@ -30,10 +31,14 @@
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewRootImpl;
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityManager;
import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.window.OnBackInvokedCallback;
+import android.window.OnBackInvokedDispatcher;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.policy.PhoneWindow;
@@ -115,6 +120,27 @@
private CharSequence mPlayDescription;
private CharSequence mPauseDescription;
private final AccessibilityManager mAccessibilityManager;
+ private boolean mBackCallbackRegistered;
+ /** Handles back invocation */
+ private final OnBackInvokedCallback mBackCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ hide();
+ }
+ };
+ /** Handles decor view attach state change */
+ private final OnAttachStateChangeListener mAttachStateListener =
+ new OnAttachStateChangeListener() {
+ @Override
+ public void onViewAttachedToWindow(@NonNull View v) {
+ registerOnBackInvokedCallback();
+ }
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull View v) {
+ unregisterOnBackInvokedCallback();
+ }
+ };
public MediaController(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -151,6 +177,7 @@
mWindow.requestFeature(Window.FEATURE_NO_TITLE);
mDecor = mWindow.getDecorView();
mDecor.setOnTouchListener(mTouchListener);
+ mDecor.addOnAttachStateChangeListener(mAttachStateListener);
mWindow.setContentView(this);
mWindow.setBackgroundDrawableResource(android.R.color.transparent);
@@ -395,6 +422,7 @@
removeCallbacks(mFadeOut);
postDelayed(mFadeOut, timeout);
}
+ registerOnBackInvokedCallback();
}
public boolean isShowing() {
@@ -416,6 +444,7 @@
Log.w("MediaController", "already removed");
}
mShowing = false;
+ unregisterOnBackInvokedCallback();
}
}
@@ -718,6 +747,35 @@
}
}
+ private void unregisterOnBackInvokedCallback() {
+ if (!mBackCallbackRegistered) {
+ return;
+ }
+ ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
+ if (viewRootImpl != null
+ && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(
+ viewRootImpl.mContext)) {
+ viewRootImpl.getOnBackInvokedDispatcher()
+ .unregisterOnBackInvokedCallback(mBackCallback);
+ }
+ mBackCallbackRegistered = false;
+ }
+
+ private void registerOnBackInvokedCallback() {
+ if (mBackCallbackRegistered) {
+ return;
+ }
+
+ ViewRootImpl viewRootImpl = mDecor.getViewRootImpl();
+ if (viewRootImpl != null
+ && WindowOnBackInvokedDispatcher.isOnBackInvokedCallbackEnabled(
+ viewRootImpl.mContext)) {
+ viewRootImpl.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ OnBackInvokedDispatcher.PRIORITY_DEFAULT, mBackCallback);
+ mBackCallbackRegistered = true;
+ }
+ }
+
public interface MediaPlayerControl {
void start();
void pause();
diff --git a/core/java/com/android/internal/app/BlockedAppStreamingActivity.java b/core/java/com/android/internal/app/BlockedAppStreamingActivity.java
index 31c3822..2d6c77f 100644
--- a/core/java/com/android/internal/app/BlockedAppStreamingActivity.java
+++ b/core/java/com/android/internal/app/BlockedAppStreamingActivity.java
@@ -56,7 +56,11 @@
CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE);
if (!TextUtils.isEmpty(streamedDeviceName)) {
- mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel);
+ mAlertParams.mTitle =
+ TextUtils.equals(activityInfo.packageName,
+ getPackageManager().getPermissionControllerPackageName())
+ ? getString(R.string.app_streaming_blocked_title_for_permission_dialog)
+ : getString(R.string.app_streaming_blocked_title, appLabel);
mAlertParams.mMessage =
getString(R.string.app_streaming_blocked_message, streamedDeviceName);
} else {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index f623a73..4b20347 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -69,6 +69,7 @@
import android.os.connectivity.WifiActivityEnergyInfo;
import android.os.connectivity.WifiBatteryStats;
import android.provider.Settings;
+import android.telephony.AccessNetworkConstants;
import android.telephony.Annotation.NetworkType;
import android.telephony.CellSignalStrength;
import android.telephony.CellSignalStrengthLte;
@@ -6830,6 +6831,27 @@
}
}
+ @RadioAccessTechnology
+ private static int mapRadioAccessNetworkTypeToRadioAccessTechnology(
+ @AccessNetworkConstants.RadioAccessNetworkType int dataType) {
+ switch (dataType) {
+ case AccessNetworkConstants.AccessNetworkType.NGRAN:
+ return RADIO_ACCESS_TECHNOLOGY_NR;
+ case AccessNetworkConstants.AccessNetworkType.EUTRAN:
+ return RADIO_ACCESS_TECHNOLOGY_LTE;
+ case AccessNetworkConstants.AccessNetworkType.UNKNOWN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.GERAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.UTRAN: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.CDMA2000: //fallthrough
+ case AccessNetworkConstants.AccessNetworkType.IWLAN:
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
+ default:
+ Slog.w(TAG,
+ "Unhandled RadioAccessNetworkType (" + dataType + "), mapping to OTHER");
+ return RADIO_ACCESS_TECHNOLOGY_OTHER;
+ }
+ }
+
@GuardedBy("this")
public void noteWifiOnLocked() {
noteWifiOnLocked(mClock.elapsedRealtime(), mClock.uptimeMillis());
@@ -13721,66 +13743,7 @@
mTmpRailStats.resetCellularTotalEnergyUsed();
}
- // Proportionally smear Rx and Tx times across each RAt
- final int levelCount = CellSignalStrength.getNumSignalStrengthLevels();
- long[] perSignalStrengthActiveTimeMs = new long[levelCount];
- long totalActiveTimeMs = 0;
-
- for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
- final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
- if (ratStats == null) continue;
-
- final int freqCount = ratStats.getFrequencyRangeCount();
- for (int freq = 0; freq < freqCount; freq++) {
- for (int level = 0; level < levelCount; level++) {
- final long durationMs = ratStats.getTimeSinceMark(freq, level,
- elapsedRealtimeMs);
- perSignalStrengthActiveTimeMs[level] += durationMs;
- totalActiveTimeMs += durationMs;
- }
- }
- }
-
- if (totalActiveTimeMs != 0) {
- // Smear the provided Tx/Rx durations across each RAT, frequency, and signal
- // strength.
- for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
- final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
- if (ratStats == null) continue;
-
- final int freqCount = ratStats.getFrequencyRangeCount();
- for (int freq = 0; freq < freqCount; freq++) {
- long frequencyDurationMs = 0;
- for (int level = 0; level < levelCount; level++) {
- final long durationMs = ratStats.getTimeSinceMark(freq, level,
- elapsedRealtimeMs);
- final long totalLvlDurationMs =
- perSignalStrengthActiveTimeMs[level];
- if (totalLvlDurationMs == 0) continue;
- final long totalTxLvlDurations =
- deltaInfo.getTransmitDurationMillisAtPowerLevel(level);
- // Smear HAL provided Tx power level duration based on active modem
- // duration in a given state. (Add totalLvlDurationMs / 2 before
- // the integer division with totalLvlDurationMs for rounding.)
- final long proportionalTxDurationMs =
- (durationMs * totalTxLvlDurations
- + (totalLvlDurationMs / 2)) / totalLvlDurationMs;
- ratStats.incrementTxDuration(freq, level, proportionalTxDurationMs);
- frequencyDurationMs += durationMs;
- }
- final long totalRxDuration = deltaInfo.getReceiveTimeMillis();
- // Smear HAL provided Rx power duration based on active modem
- // duration in a given state. (Add totalActiveTimeMs / 2 before the
- // integer division with totalActiveTimeMs for rounding.)
- final long proportionalRxDurationMs =
- (frequencyDurationMs * totalRxDuration + (totalActiveTimeMs
- / 2)) / totalActiveTimeMs;
- ratStats.incrementRxDuration(freq, proportionalRxDurationMs);
- }
-
- ratStats.setMark(elapsedRealtimeMs);
- }
- }
+ incrementPerRatDataLocked(deltaInfo, elapsedRealtimeMs);
}
long totalAppRadioTimeUs = mMobileRadioActivePerAppTimer.getTimeSinceMarkLocked(
elapsedRealtimeMs * 1000);
@@ -13931,6 +13894,100 @@
}
}
+ @GuardedBy("this")
+ private void incrementPerRatDataLocked(ModemActivityInfo deltaInfo, long elapsedRealtimeMs) {
+ final int infoSize = deltaInfo.getSpecificInfoLength();
+ if (infoSize == 1 && deltaInfo.getSpecificInfoRat(0)
+ == AccessNetworkConstants.AccessNetworkType.UNKNOWN
+ && deltaInfo.getSpecificInfoFrequencyRange(0)
+ == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Specific info data unavailable. Proportionally smear Rx and Tx times across each RAT.
+ final int levelCount = CellSignalStrength.getNumSignalStrengthLevels();
+ long[] perSignalStrengthActiveTimeMs = new long[levelCount];
+ long totalActiveTimeMs = 0;
+
+ for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
+ if (ratStats == null) continue;
+
+ final int freqCount = ratStats.getFrequencyRangeCount();
+ for (int freq = 0; freq < freqCount; freq++) {
+ for (int level = 0; level < levelCount; level++) {
+ final long durationMs = ratStats.getTimeSinceMark(freq, level,
+ elapsedRealtimeMs);
+ perSignalStrengthActiveTimeMs[level] += durationMs;
+ totalActiveTimeMs += durationMs;
+ }
+ }
+ }
+ if (totalActiveTimeMs != 0) {
+ // Smear the provided Tx/Rx durations across each RAT, frequency, and signal
+ // strength.
+ for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
+ if (ratStats == null) continue;
+
+ final int freqCount = ratStats.getFrequencyRangeCount();
+ for (int freq = 0; freq < freqCount; freq++) {
+ long frequencyDurationMs = 0;
+ for (int level = 0; level < levelCount; level++) {
+ final long durationMs = ratStats.getTimeSinceMark(freq, level,
+ elapsedRealtimeMs);
+ final long totalLvlDurationMs =
+ perSignalStrengthActiveTimeMs[level];
+ if (totalLvlDurationMs == 0) continue;
+ final long totalTxLvlDurations =
+ deltaInfo.getTransmitDurationMillisAtPowerLevel(level);
+ // Smear HAL provided Tx power level duration based on active modem
+ // duration in a given state. (Add totalLvlDurationMs / 2 before
+ // the integer division with totalLvlDurationMs for rounding.)
+ final long proportionalTxDurationMs =
+ (durationMs * totalTxLvlDurations
+ + (totalLvlDurationMs / 2)) / totalLvlDurationMs;
+ ratStats.incrementTxDuration(freq, level, proportionalTxDurationMs);
+ frequencyDurationMs += durationMs;
+ }
+ final long totalRxDuration = deltaInfo.getReceiveTimeMillis();
+ // Smear HAL provided Rx power duration based on active modem
+ // duration in a given state. (Add totalActiveTimeMs / 2 before the
+ // integer division with totalActiveTimeMs for rounding.)
+ final long proportionalRxDurationMs =
+ (frequencyDurationMs * totalRxDuration + (totalActiveTimeMs
+ / 2)) / totalActiveTimeMs;
+ ratStats.incrementRxDuration(freq, proportionalRxDurationMs);
+ }
+
+ }
+ }
+ } else {
+ // Specific data available.
+ for (int index = 0; index < infoSize; index++) {
+ final int rat = deltaInfo.getSpecificInfoRat(index);
+ final int freq = deltaInfo.getSpecificInfoFrequencyRange(index);
+
+ // Map RadioAccessNetworkType to course grain RadioAccessTechnology.
+ final int ratBucket = mapRadioAccessNetworkTypeToRadioAccessTechnology(rat);
+ final RadioAccessTechnologyBatteryStats ratStats = getRatBatteryStatsLocked(
+ ratBucket);
+
+ final long rxTimeMs = deltaInfo.getReceiveTimeMillis(rat, freq);
+ final int[] txTimesMs = deltaInfo.getTransmitTimeMillis(rat, freq);
+
+ ratStats.incrementRxDuration(freq, rxTimeMs);
+ final int numTxLvl = txTimesMs.length;
+ for (int lvl = 0; lvl < numTxLvl; lvl++) {
+ ratStats.incrementTxDuration(freq, lvl, txTimesMs[lvl]);
+ }
+ }
+ }
+
+ for (int rat = 0; rat < RADIO_ACCESS_TECHNOLOGY_COUNT; rat++) {
+ final RadioAccessTechnologyBatteryStats ratStats = mPerRatBatteryStats[rat];
+ if (ratStats == null) continue;
+ ratStats.setMark(elapsedRealtimeMs);
+ }
+ }
+
/**
* Add modem tx power to history
* Device is said to be in high cellular transmit power when it has spent most of the transmit
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index c371ccd..d30980f 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5464,6 +5464,8 @@
<!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string>
+ <!-- Title of the dialog shown when the permissioncontroller is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
+ <string name="app_streaming_blocked_title_for_permission_dialog">Permission needed</string>
<!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
<string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string>
<!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 2bb3e65..f1a8e56 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -3300,6 +3300,7 @@
<java-symbol type="string" name="app_blocked_message" />
<java-symbol type="string" name="app_streaming_blocked_title" />
+ <java-symbol type="string" name="app_streaming_blocked_title_for_permission_dialog" />
<java-symbol type="string" name="app_streaming_blocked_message" />
<!-- Used internally for assistant to launch activity transitions -->
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
index dbe1e81..87c45dc 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryStatsNoteTest.java
@@ -32,12 +32,15 @@
import android.app.ActivityManager;
import android.app.usage.NetworkStatsManager;
+import android.hardware.radio.V1_5.AccessNetwork;
import android.os.BatteryStats;
import android.os.BatteryStats.HistoryItem;
import android.os.BatteryStats.Uid.Sensor;
import android.os.Process;
import android.os.UserHandle;
import android.os.WorkSource;
+import android.telephony.AccessNetworkConstants;
+import android.telephony.ActivityStatsTechSpecificInfo;
import android.telephony.Annotation;
import android.telephony.CellSignalStrength;
import android.telephony.DataConnectionRealTimeInfo;
@@ -58,8 +61,10 @@
import org.mockito.Mock;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.function.IntConsumer;
@@ -1322,7 +1327,7 @@
}
}
- final ModemAndBatteryState state = new ModemAndBatteryState(bi, null);
+ final ModemAndBatteryState state = new ModemAndBatteryState(bi, null, null);
IntConsumer incrementTime = inc -> {
state.currentTimeMs += inc;
@@ -1469,32 +1474,16 @@
final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
for (int rat = 0; rat < ratCount; rat++) {
for (int freq = 0; freq < frequencyCount; freq++) {
- if (rat != RADIO_ACCESS_TECHNOLOGY_NR
- && freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
- // Only the NR RAT should have per frequency data.
- expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE;
- } else {
- expectedRxDurationsMs[rat][freq] = 0;
- }
expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE;
for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
- expectedDurationsMs[rat][freq][txLvl] = 0;
-
- if (rat != RADIO_ACCESS_TECHNOLOGY_NR
- && freq != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
- // Only the NR RAT should have per frequency data.
- expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE;
- } else {
- expectedTxDurationsMs[rat][freq][txLvl] = 0;
- }
expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE;
}
}
}
final ModemActivityInfo mai = new ModemActivityInfo(0L, 0L, 0L, new int[txLevelCount], 0L);
- final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai);
+ final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai, null);
IntConsumer incrementTime = inc -> {
state.currentTimeMs += inc;
@@ -1711,6 +1700,288 @@
expectedTxDurationsMs, bi, state.currentTimeMs);
}
+ @SmallTest
+ public void testGetPerStateActiveRadioDurationMs_withSpecificInfoModemActivity() {
+ final MockClock clock = new MockClock(); // holds realtime and uptime in ms
+ final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clock);
+ bi.setPowerProfile(mock(PowerProfile.class));
+ final int ratCount = RADIO_ACCESS_TECHNOLOGY_COUNT;
+ final int frequencyCount = ServiceState.FREQUENCY_RANGE_MMWAVE + 1;
+ final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
+
+ List<ActivityStatsTechSpecificInfo> specificInfoList = new ArrayList();
+
+ final long[][][] expectedDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ final long[][] expectedRxDurationsMs = new long[ratCount][frequencyCount];
+ final long[][][] expectedTxDurationsMs = new long[ratCount][frequencyCount][txLevelCount];
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Initialize available specific Modem info
+ specificInfoList.add(
+ new ActivityStatsTechSpecificInfo(rat, freq, new int[txLevelCount], 0));
+ }
+ expectedRxDurationsMs[rat][freq] = POWER_DATA_UNAVAILABLE;
+
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ expectedTxDurationsMs[rat][freq][txLvl] = POWER_DATA_UNAVAILABLE;
+ }
+ }
+ }
+
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.UNKNOWN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.GERAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.UTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.EUTRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.CDMA2000,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.IWLAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.NGRAN,
+ ServiceState.FREQUENCY_RANGE_UNKNOWN, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.NGRAN,
+ ServiceState.FREQUENCY_RANGE_LOW, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MID, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.NGRAN,
+ ServiceState.FREQUENCY_RANGE_HIGH, new int[txLevelCount], 0));
+ specificInfoList.add(new ActivityStatsTechSpecificInfo(AccessNetwork.NGRAN,
+ ServiceState.FREQUENCY_RANGE_MMWAVE, new int[txLevelCount], 0));
+
+ final ActivityStatsTechSpecificInfo[] specificInfos = specificInfoList.toArray(
+ new ActivityStatsTechSpecificInfo[specificInfoList.size()]);
+ final ModemActivityInfo mai = new ModemActivityInfo(0L, 0L, 0L, specificInfos);
+ final ModemAndBatteryState state = new ModemAndBatteryState(bi, mai, specificInfos);
+
+ IntConsumer incrementTime = inc -> {
+ state.currentTimeMs += inc;
+ clock.realtime = clock.uptime = state.currentTimeMs;
+
+ // If the device is not on battery, no timers should increment.
+ if (!state.onBattery) return;
+ // If the modem is not active, no timers should increment.
+ if (!state.modemActive) return;
+
+ final int currRat = state.currentRat;
+ final int currRant = state.currentRadioAccessNetworkType;
+ final int currFreqRange =
+ currRat == RADIO_ACCESS_TECHNOLOGY_NR ? state.currentFrequencyRange : 0;
+ int currSignalStrength = state.currentSignalStrengths.get(currRat);
+
+ expectedDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+
+ // Evaluate the HAL provided time in states.
+ final ActivityStatsTechSpecificInfo info = state.getSpecificInfo(currRant,
+ currFreqRange);
+ switch (state.modemState) {
+ case SLEEP:
+ long sleepMs = state.modemActivityInfo.getSleepTimeMillis();
+ state.modemActivityInfo.setSleepTimeMillis(sleepMs + inc);
+ break;
+ case IDLE:
+ long idleMs = state.modemActivityInfo.getIdleTimeMillis();
+ state.modemActivityInfo.setIdleTimeMillis(idleMs + inc);
+ break;
+ case RECEIVING:
+ long rxMs = info.getReceiveTimeMillis();
+ info.setReceiveTimeMillis(rxMs + inc);
+ expectedRxDurationsMs[currRat][currFreqRange] += inc;
+ break;
+ case TRANSMITTING:
+ int[] txMs = info.getTransmitTimeMillis().clone();
+ txMs[currSignalStrength] += inc;
+ info.setTransmitTimeMillis(txMs);
+ expectedTxDurationsMs[currRat][currFreqRange][currSignalStrength] += inc;
+ break;
+ }
+ };
+
+ state.setOnBattery(false);
+ state.setModemActive(false);
+ state.setRatType(TelephonyManager.NETWORK_TYPE_UNKNOWN,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ AccessNetworkConstants.AccessNetworkType.UNKNOWN);
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_UNKNOWN);
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // While not on battery, the timers should not increase.
+ state.setModemActive(true);
+ incrementTime.accept(100);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ AccessNetworkConstants.AccessNetworkType.NGRAN);
+ incrementTime.accept(200);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_MMWAVE);
+ incrementTime.accept(300);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setRatType(TelephonyManager.NETWORK_TYPE_LTE,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ AccessNetworkConstants.AccessNetworkType.EUTRAN);
+ incrementTime.accept(400);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_MODERATE);
+ incrementTime.accept(500);
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Data will now be available.
+ for (int rat = 0; rat < ratCount; rat++) {
+ for (int freq = 0; freq < frequencyCount; freq++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedRxDurationsMs[rat][freq] = 0;
+ }
+ for (int txLvl = 0; txLvl < txLevelCount; txLvl++) {
+ if (rat == RADIO_ACCESS_TECHNOLOGY_NR
+ || freq == ServiceState.FREQUENCY_RANGE_UNKNOWN) {
+ // Only the NR RAT should have per frequency data.
+ expectedTxDurationsMs[rat][freq][txLvl] = 0;
+ }
+ }
+ }
+ }
+
+ // When set on battery, currently active state (RAT:LTE, Signal Strength:Moderate) should
+ // start counting up.
+ state.setOnBattery(true);
+ state.noteModemControllerActivity();
+ incrementTime.accept(300);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(500);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(600);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ // Changing LTE signal strength should be tracked.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(300);
+ state.setModemState(ModemState.SLEEP);
+ incrementTime.accept(1000);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(700);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN);
+ incrementTime.accept(800);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(222);
+ state.setModemState(ModemState.IDLE);
+ incrementTime.accept(111);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(7777);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GOOD);
+ incrementTime.accept(88);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(900);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_LTE,
+ CellSignalStrength.SIGNAL_STRENGTH_GREAT);
+ incrementTime.accept(123);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(333);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(1000);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(555);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Change in the signal strength of nonactive RAT should not affect anything.
+ state.setSignalStrength(BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ CellSignalStrength.SIGNAL_STRENGTH_POOR);
+ incrementTime.accept(631);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(321);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(99);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Changing to OTHER Rat should start tracking the poor signal strength.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_CDMA,
+ BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER,
+ AccessNetworkConstants.AccessNetworkType.CDMA2000);
+ incrementTime.accept(1200);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Noting frequency change should not affect non NR Rat.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_HIGH);
+ incrementTime.accept(444);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(1300);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Now the NR Rat, HIGH frequency range, good signal strength should start counting.
+ state.setRatType(TelephonyManager.NETWORK_TYPE_NR, BatteryStats.RADIO_ACCESS_TECHNOLOGY_NR,
+ AccessNetworkConstants.AccessNetworkType.NGRAN);
+ incrementTime.accept(1400);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Frequency changed to low.
+ state.setFrequencyRange(ServiceState.FREQUENCY_RANGE_LOW);
+ incrementTime.accept(852);
+ state.setModemState(ModemState.RECEIVING);
+ incrementTime.accept(157);
+ state.setModemState(ModemState.TRANSMITTING);
+ incrementTime.accept(1500);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+
+ // Modem no longer active, should not be tracking any more.
+ state.setModemActive(false);
+ incrementTime.accept(1500);
+ state.noteModemControllerActivity();
+ checkPerStateActiveRadioDurations(expectedDurationsMs, expectedRxDurationsMs,
+ expectedTxDurationsMs, bi, state.currentTimeMs);
+ }
+
private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
// Note that noteUidProcessStateLocked uses ActivityManager process states.
if (fgOn) {
@@ -1841,17 +2112,22 @@
public int currentNetworkDataType = TelephonyManager.NETWORK_TYPE_UNKNOWN;
@BatteryStats.RadioAccessTechnology
public int currentRat = BatteryStats.RADIO_ACCESS_TECHNOLOGY_OTHER;
+ @AccessNetworkConstants.RadioAccessNetworkType
+ public int currentRadioAccessNetworkType = AccessNetworkConstants.AccessNetworkType.UNKNOWN;
@ServiceState.FrequencyRange
public int currentFrequencyRange = ServiceState.FREQUENCY_RANGE_UNKNOWN;
public SparseIntArray currentSignalStrengths = new SparseIntArray();
public ModemState modemState = ModemState.SLEEP;
public ModemActivityInfo modemActivityInfo;
+ public ActivityStatsTechSpecificInfo[] specificInfo;
private final MockBatteryStatsImpl mBsi;
- ModemAndBatteryState(MockBatteryStatsImpl bsi, ModemActivityInfo mai) {
+ ModemAndBatteryState(MockBatteryStatsImpl bsi, ModemActivityInfo mai,
+ ActivityStatsTechSpecificInfo[] astsi) {
mBsi = bsi;
modemActivityInfo = mai;
+ specificInfo = astsi;
}
void setOnBattery(boolean onBattery) {
@@ -1871,6 +2147,13 @@
}
void setRatType(@Annotation.NetworkType int dataType,
+ @BatteryStats.RadioAccessTechnology int rat,
+ @AccessNetworkConstants.RadioAccessNetworkType int halDataType) {
+ currentRadioAccessNetworkType = halDataType;
+ setRatType(dataType, rat);
+ }
+
+ void setRatType(@Annotation.NetworkType int dataType,
@BatteryStats.RadioAccessTechnology int rat) {
currentNetworkDataType = dataType;
currentRat = rat;
@@ -1895,13 +2178,45 @@
modemState = state;
}
+ ActivityStatsTechSpecificInfo getSpecificInfo(@BatteryStats.RadioAccessTechnology int rat,
+ @ServiceState.FrequencyRange int frequency) {
+ if (specificInfo == null) return null;
+ for (ActivityStatsTechSpecificInfo info : specificInfo) {
+ if (info.getRat() == rat && info.getFrequencyRange() == frequency) {
+ return info;
+ }
+ }
+ return null;
+ }
+
void noteModemControllerActivity() {
if (modemActivityInfo == null) return;
modemActivityInfo.setTimestamp(currentTimeMs);
- ModemActivityInfo copy = new ModemActivityInfo(modemActivityInfo.getTimestampMillis(),
- modemActivityInfo.getSleepTimeMillis(), modemActivityInfo.getIdleTimeMillis(),
- modemActivityInfo.getTransmitTimeMillis().clone(),
- modemActivityInfo.getReceiveTimeMillis());
+ final ModemActivityInfo copy;
+ if (specificInfo == null) {
+ copy = new ModemActivityInfo(
+ modemActivityInfo.getTimestampMillis(),
+ modemActivityInfo.getSleepTimeMillis(),
+ modemActivityInfo.getIdleTimeMillis(),
+ modemActivityInfo.getTransmitTimeMillis().clone(),
+ modemActivityInfo.getReceiveTimeMillis());
+ } else {
+ // Deep copy specificInfo
+ final ActivityStatsTechSpecificInfo[] infoCopies =
+ new ActivityStatsTechSpecificInfo[specificInfo.length];
+ for (int i = 0; i < specificInfo.length; i++) {
+ final ActivityStatsTechSpecificInfo info = specificInfo[i];
+ infoCopies[i] = new ActivityStatsTechSpecificInfo(info.getRat(),
+ info.getFrequencyRange(), info.getTransmitTimeMillis().clone(),
+ (int) info.getReceiveTimeMillis());
+ }
+
+ copy = new ModemActivityInfo(
+ modemActivityInfo.getTimestampMillis(),
+ modemActivityInfo.getSleepTimeMillis(),
+ modemActivityInfo.getIdleTimeMillis(),
+ infoCopies);
+ }
mBsi.noteModemControllerActivity(copy, POWER_DATA_UNAVAILABLE,
currentTimeMs, currentTimeMs, mNetworkStatsManager);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
index da8308e..10ff2fb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleExpandedView.java
@@ -692,6 +692,7 @@
* @param bubblePosition the x position of the bubble if showing on top, the y position of
* the bubble if showing vertically.
* @param onLeft whether the stack was on the left side of the screen when expanded.
+ * @param animate whether the pointer should animate to this position.
*/
public void setPointerPosition(float bubblePosition, boolean onLeft, boolean animate) {
// Pointer gets drawn in the padding
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
index 8a120b9..97e5ee3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubblePositioner.java
@@ -579,11 +579,10 @@
// Showing vertically: might need to translate the bubbles above the IME.
// Subtract spacing here to provide a margin between top of IME and bottom of bubble row.
- final float bottomInset = getImeHeight() + mInsets.bottom - (mSpacingBetweenBubbles * 2);
+ final float bottomHeight = getImeHeight() + mInsets.bottom - (mSpacingBetweenBubbles * 2);
+ final float bottomInset = mScreenRect.bottom - bottomHeight;
final float expandedStackSize = getExpandedStackSize(numberOfBubbles);
- final float centerPosition = showBubblesVertically()
- ? mPositionRect.centerY()
- : mPositionRect.centerX();
+ final float centerPosition = mPositionRect.centerY();
final float rowBottom = centerPosition + (expandedStackSize / 2f);
final float rowTop = centerPosition - (expandedStackSize / 2f);
float rowTopForIme = rowTop;
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 33115e9..ad5d85c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -96,7 +96,7 @@
* Manages the picture-in-picture (PIP) UI and states for Phones.
*/
public class PipController implements PipTransitionController.PipTransitionCallback,
- RemoteCallable<PipController>, DisplayController.OnDisplaysChangedListener {
+ RemoteCallable<PipController> {
private static final String TAG = "PipController";
private Context mContext;
@@ -234,6 +234,14 @@
onDisplayChanged(mDisplayController.getDisplayLayout(displayId),
true /* saveRestoreSnapFraction */);
}
+
+ @Override
+ public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
+ Set<Rect> unrestricted) {
+ if (mPipBoundsState.getDisplayId() == displayId) {
+ mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
+ }
+ }
};
/**
@@ -463,14 +471,6 @@
return mMainExecutor;
}
- @Override
- public void onKeepClearAreasChanged(int displayId, Set<Rect> restricted,
- Set<Rect> unrestricted) {
- if (mPipBoundsState.getDisplayId() == displayId) {
- mPipBoundsState.setKeepClearAreas(restricted, unrestricted);
- }
- }
-
private void onConfigurationChanged(Configuration newConfig) {
mPipBoundsAlgorithm.onConfigurationChanged(mContext);
mTouchHandler.onConfigurationChanged();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
index af6e5d4..aef298e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/pip/phone/PipControllerTest.java
@@ -217,7 +217,8 @@
final Rect keepClearArea = new Rect(0, 0, 10, 10);
when(mMockPipBoundsState.getDisplayId()).thenReturn(displayId);
- mPipController.onKeepClearAreasChanged(displayId, Set.of(keepClearArea), Set.of());
+ mPipController.mDisplaysChangedListener.onKeepClearAreasChanged(
+ displayId, Set.of(keepClearArea), Set.of());
verify(mMockPipBoundsState).setKeepClearAreas(Set.of(keepClearArea), Set.of());
}
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 395062e..491a5cd 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -8402,6 +8402,12 @@
* {@link #addAssistantServicesUids(int[])} and not yet removed with
* {@link #removeAssistantServicesUids(int[])}
*
+ * <p> Note that during native audioserver crash and after boot up the list of assistant
+ * UIDs will be reset to an empty list (i.e. no UID will be considered as assistant)
+ * Just after user switch, the list of assistant will also reset to empty.
+ * In both cases,The component's UID of the assistiant role or assistant setting will be
+ * automitically added to the list by the audio service.
+ *
* @return array of assistants UIDs
*
* @hide
diff --git a/media/packages/BluetoothMidiService/AndroidManifest.xml b/media/packages/BluetoothMidiService/AndroidManifest.xml
index 858f85f..6928b94 100644
--- a/media/packages/BluetoothMidiService/AndroidManifest.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifest.xml
@@ -20,8 +20,6 @@
xmlns:tools="http://schemas.android.com/tools"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
-
<uses-feature android:name="android.hardware.bluetooth_le"
android:required="true"/>
<uses-feature android:name="android.software.midi"
diff --git a/media/packages/BluetoothMidiService/AndroidManifestBase.xml b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
index 99dcaaa..468f6ee 100644
--- a/media/packages/BluetoothMidiService/AndroidManifestBase.xml
+++ b/media/packages/BluetoothMidiService/AndroidManifestBase.xml
@@ -19,7 +19,6 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.bluetoothmidiservice"
>
- <uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<application
android:label="BluetoothMidi"
android:defaultToDeviceProtectedStorage="true"
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 955aee9..03384a2 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -110,7 +110,7 @@
<uses-permission android:name="android.permission.ACCESS_NOTIFICATION_POLICY" />
<uses-permission android:name="android.permission.READ_INSTALL_SESSIONS" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
- <uses-permission android:name="android.permission.SEND_LOST_MODE_LOCATION_UPDATES" />
+ <uses-permission android:name="android.permission.TRIGGER_LOST_MODE" />
<!-- ACCESS_BACKGROUND_LOCATION is needed for testing purposes only. -->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!-- ACCESS_MTP is needed for testing purposes only. -->
diff --git a/packages/SystemUI/docs/broadcasts.md b/packages/SystemUI/docs/broadcasts.md
index e75ae29..0a35725 100644
--- a/packages/SystemUI/docs/broadcasts.md
+++ b/packages/SystemUI/docs/broadcasts.md
@@ -21,6 +21,7 @@
* The `IntentFilter` may or may not contain categories.
* The `IntentFilter` **does not** contain data types, data schemes, data authorities or data paths.
* The broadcast **is not** gated behind a permission.
+* The broadcast **is not** ordered and doesn't need to set result data.
Additionally, the dispatcher supports the following:
@@ -107,3 +108,8 @@
```
Unregistering can be done even if the `BroadcastReceiver` has never been registered with `BroadcastDispatcher`. In that case, it is a No-Op.
+
+### A note on goAsync()
+
+If you're processing a broadcast in a background thread, you shouldn't call `goAsync()` and
+`finish()`. The system will keep sysui alive regardless, so it isn't needed.
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
index 8ad2009..75d95e6 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/qs/QS.java
@@ -123,6 +123,11 @@
default void setScrollListener(ScrollListener scrollListener) {}
/**
+ * Sets the amount of vertical over scroll that should be performed on QS.
+ */
+ default void setOverScrollAmount(int overScrollAmount) {}
+
+ /**
* Callback for when QSPanel container is scrolled
*/
@ProvidesInterface(version = ScrollListener.VERSION)
diff --git a/packages/SystemUI/res/drawable/ic_media_pause.xml b/packages/SystemUI/res/drawable/ic_media_pause.xml
index 1f4b2cf..0009b6c 100644
--- a/packages/SystemUI/res/drawable/ic_media_pause.xml
+++ b/packages/SystemUI/res/drawable/ic_media_pause.xml
@@ -1,26 +1,91 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="14dp"
- android:height="16dp"
- android:viewportWidth="14"
- android:viewportHeight="16">
- <path
- android:pathData="M9.1818,15.6363H13.5455V0.3635H9.1818V15.6363ZM0.4546,15.6363H4.8182V0.3635H0.4546V15.6363Z"
- android:fillColor="#FFFFFF"
- android:fillType="evenOdd"/>
-</vector>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "/>
+ </group>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueTo="M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "
+ android:valueTo="M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_pause_container.xml b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
new file mode 100644
index 0000000..b92e635
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_pause_container.xml
@@ -0,0 +1,73 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5"/>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffddb3"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
+ android:valueTo="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.526,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play.xml b/packages/SystemUI/res/drawable/ic_media_play.xml
index 0eac1ad..eb32470 100644
--- a/packages/SystemUI/res/drawable/ic_media_play.xml
+++ b/packages/SystemUI/res/drawable/ic_media_play.xml
@@ -1,26 +1,91 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ Copyright (C) 2022 The Android Open Source Project
- ~
- ~ Licensed under the Apache License, Version 2.0 (the "License");
- ~ you may not use this file except in compliance with the License.
- ~ You may obtain a copy of the License at
- ~
- ~ http://www.apache.org/licenses/LICENSE-2.0
- ~
- ~ Unless required by applicable law or agreed to in writing, software
- ~ distributed under the License is distributed on an "AS IS" BASIS,
- ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- ~ See the License for the specific language governing permissions and
- ~ limitations under the License
- -->
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
- <path
- android:pathData="M20,12L6,21V3L20,12ZM15.26,12L8.55,7.68V16.32L15.26,12Z"
- android:fillColor="#FFFFFF"
- android:fillType="evenOdd"/>
-</vector>
\ No newline at end of file
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="24dp"
+ android:width="24dp"
+ android:viewportHeight="24"
+ android:viewportWidth="24">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_1_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_1_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "/>
+ </group>
+ <group android:name="_R_G_L_0_G"
+ android:translateX="12"
+ android:translateY="12.125">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffffff"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_1_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-6 7 C-6,7 -2,3.94 -2,3.94 C-2,3.94 -2,-4.37 -2,-4.37 C-2,-4.37 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueTo="M-6 7 C-6,7 -2,7 -2,7 C-2,7 -2,-7 -2,-7 C-2,-7 -6,-7 -6,-7 C-6,-7 -6,7 -6,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="333"
+ android:startOffset="0"
+ android:valueFrom="M-5.62 7 C-5.62,7 -4.75,7 -4.75,7 C-4.75,7 6,-0.12 6,-0.12 C6,-0.12 -4.44,-7 -4.44,-7 C-4.44,-7 -5.62,-7 -5.62,-7 C-5.62,-7 -5.62,7 -5.62,7c "
+ android:valueTo="M2 7 C2,7 6,7 6,7 C6,7 6,-0.12 6,-0.12 C6,-0.12 6,-7 6,-7 C6,-7 2,-7 2,-7 C2,-7 2,7 2,7c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.2,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/ic_media_play_container.xml b/packages/SystemUI/res/drawable/ic_media_play_container.xml
new file mode 100644
index 0000000..2fc9fc8
--- /dev/null
+++ b/packages/SystemUI/res/drawable/ic_media_play_container.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2022 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+-->
+<animated-vector xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:aapt="http://schemas.android.com/aapt">
+ <aapt:attr name="android:drawable">
+ <vector android:height="48dp"
+ android:width="48dp"
+ android:viewportHeight="48"
+ android:viewportWidth="48">
+ <group android:name="_R_G">
+ <group android:name="_R_G_L_0_G"
+ android:translateX="24"
+ android:translateY="24"
+ android:scaleX="0.5"
+ android:scaleY="0.5">
+ <path android:name="_R_G_L_0_G_D_0_P_0"
+ android:fillColor="#ffddb3"
+ android:fillAlpha="1"
+ android:fillType="nonZero"
+ android:pathData=" M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "/>
+ </group>
+ </group>
+ <group android:name="time_group"/>
+ </vector>
+ </aapt:attr>
+ <target android:name="_R_G_L_0_G_D_0_P_0">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="pathData"
+ android:duration="500"
+ android:startOffset="0"
+ android:valueFrom="M48 0.25 C48,0.25 48,0 48,0 C47.75,26 31.25,48 0,48 C0,48 0,48 0,48 C-30,48 -48,25.75 -48,-0.25 C-48,-0.25 -48,-0.25 -48,-0.25 C-47.75,-23.5 -32.25,-47.75 0.5,-48 C0.5,-48 0.5,-48 0.5,-48 C28,-47.75 47.75,-29.75 48,0.25c "
+ android:valueTo="M48 -16 C48,-16 48,16 48,16 C48,33.67 33.67,48 16,48 C16,48 -16,48 -16,48 C-33.67,48 -48,33.67 -48,16 C-48,16 -48,-16 -48,-16 C-48,-33.67 -33.67,-48 -16,-48 C-16,-48 16,-48 16,-48 C33.67,-48 48,-33.67 48,-16c "
+ android:valueType="pathType">
+ <aapt:attr name="android:interpolator">
+ <pathInterpolator android:pathData="M 0.0,0.0 c0.518,0 0,1 1.0,1.0"/>
+ </aapt:attr>
+ </objectAnimator>
+ </set>
+ </aapt:attr>
+ </target>
+ <target android:name="time_group">
+ <aapt:attr name="android:animation">
+ <set android:ordering="together">
+ <objectAnimator android:propertyName="translateX"
+ android:duration="517"
+ android:startOffset="0"
+ android:valueFrom="0"
+ android:valueTo="1"
+ android:valueType="floatType"/>
+ </set>
+ </aapt:attr>
+ </target>
+</animated-vector>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index 1117356..e1f3eca 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -145,8 +145,7 @@
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_padding"
- android:layout_marginEnd="@dimen/qs_media_padding"
- />
+ android:layout_marginEnd="@dimen/qs_media_padding" />
<!-- See comment in media_session_collapsed.xml for how these barriers are used -->
<androidx.constraintlayout.widget.Barrier
@@ -184,7 +183,7 @@
<!-- Button visibility will be controlled in code -->
<ImageButton
android:id="@+id/actionPrev"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="4dp"
@@ -211,7 +210,7 @@
<ImageButton
android:id="@+id/actionNext"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="0dp"
@@ -221,7 +220,7 @@
<ImageButton
android:id="@+id/action0"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -231,7 +230,7 @@
<ImageButton
android:id="@+id/action1"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -241,7 +240,7 @@
<ImageButton
android:id="@+id/action2"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -251,7 +250,7 @@
<ImageButton
android:id="@+id/action3"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
@@ -261,7 +260,7 @@
<ImageButton
android:id="@+id/action4"
- style="@style/MediaPlayer.SessionAction"
+ style="@style/MediaPlayer.SessionAction.Secondary"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_marginStart="@dimen/qs_media_action_spacing"
diff --git a/packages/SystemUI/res/values-sw600dp-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 9f14d2f2..76780ff 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -38,6 +38,8 @@
<dimen name="status_bar_header_height_keyguard">42dp</dimen>
+ <dimen name="lockscreen_shade_max_over_scroll_amount">32dp</dimen>
+
<!-- Distance that the full shade transition takes in order to complete by tapping on a button
like "expand". -->
<dimen name="lockscreen_shade_transition_by_tap_distance">200dp</dimen>
diff --git a/packages/SystemUI/res/values-sw600dp-land/integers.xml b/packages/SystemUI/res/values-sw600dp-land/integers.xml
new file mode 100644
index 0000000..919d605
--- /dev/null
+++ b/packages/SystemUI/res/values-sw600dp-land/integers.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2006, 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.
+*/
+-->
+<resources>
+ <integer name="lockscreen_shade_over_scroll_release_duration">500</integer>
+</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index cff660d..0512d3c 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -26,4 +26,6 @@
<dimen name="status_bar_header_height_keyguard">56dp</dimen>
<dimen name="qs_media_session_height_expanded">251dp</dimen>
+
+ <dimen name="lockscreen_shade_max_over_scroll_amount">42dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index a309cc4..f39b5ef 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1174,9 +1174,8 @@
the shade -->
<dimen name="lockscreen_shade_media_transition_distance">120dp</dimen>
- <!-- Maximum overshoot for the topPadding of notifications when transitioning to the full
- shade -->
- <dimen name="lockscreen_shade_notification_movement">24dp</dimen>
+ <!-- Maximum over scroll amount for the shade when transition to the full shade. -->
+ <dimen name="lockscreen_shade_max_over_scroll_amount">24dp</dimen>
<!-- Maximum overshoot for the pulse expansion -->
<dimen name="pulse_expansion_max_top_overshoot">32dp</dimen>
diff --git a/packages/SystemUI/res/values/integers.xml b/packages/SystemUI/res/values/integers.xml
index f0f7a19..3164ed1 100644
--- a/packages/SystemUI/res/values/integers.xml
+++ b/packages/SystemUI/res/values/integers.xml
@@ -25,4 +25,7 @@
See com.android.systemui.volume.VolumeDialogImpl.
Value 21 corresponds to RIGHT|CENTER_VERTICAL. -->
<integer name="volume_dialog_gravity">21</integer>
+
+ <!-- The time it takes for the over scroll release animation to complete, in milli seconds. -->
+ <integer name="lockscreen_shade_over_scroll_release_duration">0</integer>
</resources>
\ No newline at end of file
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a61eda8..4776587 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -616,7 +616,6 @@
parent="@android:style/Widget.Material.Button.Borderless.Small">
<item name="android:background">@drawable/qs_media_light_source</item>
<item name="android:tint">?android:attr/textColorPrimary</item>
- <item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
<item name="android:paddingTop">12dp</item>
<item name="android:paddingStart">12dp</item>
<item name="android:paddingEnd">12dp</item>
@@ -629,6 +628,10 @@
<item name="android:backgroundTint">@color/media_player_solid_button_bg</item>
</style>
+ <style name="MediaPlayer.SessionAction.Secondary" parent="MediaPlayer.SessionAction">
+ <item name="android:stateListAnimator">@anim/media_button_state_list_animator</item>
+ </style>
+
<style name="MediaPlayer.OutlineButton">
<item name="android:background">@drawable/qs_media_outline_button</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 57997d8..1a325d3 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -233,6 +233,13 @@
mSecurityViewFlipperController.reloadColors();
}
};
+ private final KeyguardUpdateMonitorCallback mKeyguardUpdateMonitorCallback =
+ new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onDevicePolicyManagerStateChanged() {
+ showPrimarySecurityScreen(false);
+ }
+ };
private KeyguardSecurityContainerController(KeyguardSecurityContainer view,
AdminSecondaryLockScreenController.Factory adminSecondaryLockScreenControllerFactory,
@@ -279,6 +286,7 @@
@Override
protected void onViewAttached() {
+ mUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mView.setSwipeListener(mSwipeListener);
mView.addMotionEventListener(mGlobalTouchListener);
mConfigurationController.addCallback(mConfigurationListener);
@@ -286,6 +294,7 @@
@Override
protected void onViewDetached() {
+ mUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
mConfigurationController.removeCallback(mConfigurationListener);
mView.removeMotionEventListener(mGlobalTouchListener);
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 5602f3d..fcabbbc 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -2723,12 +2723,20 @@
}
/**
- * Handle {@link #MSG_DPM_STATE_CHANGED}
+ * Handle {@link #MSG_DPM_STATE_CHANGED} which can change primary authentication methods to
+ * pin/pattern/password/none.
*/
private void handleDevicePolicyManagerStateChanged(int userId) {
Assert.isMainThread();
updateFingerprintListeningState(BIOMETRIC_ACTION_UPDATE);
updateSecondaryLockscreenRequirement(userId);
+
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+ if (cb != null) {
+ cb.onDevicePolicyManagerStateChanged();
+ }
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index ad2053c..9373ea8 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -300,6 +300,11 @@
public void onSecondaryLockscreenRequirementChanged(int userId) { }
/**
+ * Called when device policy manager state changes.
+ */
+ public void onDevicePolicyManagerStateChanged() { }
+
+ /**
* Called when notifying user to unlock in order to use NFC.
*/
public void onRequireUnlockForNfc() { }
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
index 4c00735..085bcfa 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/SidefpsController.kt
@@ -33,6 +33,8 @@
import android.hardware.fingerprint.ISidefpsController
import android.os.Handler
import android.util.Log
+import android.view.View.AccessibilityDelegate
+import android.view.accessibility.AccessibilityEvent
import android.view.Display
import android.view.Gravity
import android.view.LayoutInflater
@@ -181,6 +183,23 @@
}
lottie.addOverlayDynamicColor(context)
+ /**
+ * Intercepts TYPE_WINDOW_STATE_CHANGED accessibility event, preventing Talkback from
+ * speaking @string/accessibility_fingerprint_label twice when sensor location indicator
+ * is in focus
+ */
+ view.setAccessibilityDelegate(object : AccessibilityDelegate() {
+ override fun dispatchPopulateAccessibilityEvent(
+ host: View,
+ event: AccessibilityEvent
+ ): Boolean {
+ return if (event.getEventType() === AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
+ true
+ } else {
+ super.dispatchPopulateAccessibilityEvent(host, event)
+ }
+ }
+ })
return view
}
diff --git a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
index bbb75c3..0ff5805 100644
--- a/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
+++ b/packages/SystemUI/src/com/android/systemui/broadcast/BroadcastDispatcher.kt
@@ -60,6 +60,8 @@
* Use only for IntentFilters with actions and optionally categories. It does not support,
* permissions, schemes, data types, data authorities or priority different than 0.
* Cannot be used for getting sticky broadcasts (either as return of registering or as re-delivery).
+ * Broadcast handling may be asynchronous *without* calling goAsync(), as it's running within sysui
+ * and doesn't need to worry about being killed.
*/
open class BroadcastDispatcher constructor (
private val context: Context,
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index aa43383..4065a25 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -540,13 +540,15 @@
/**
* Removes a complication by {@link ComplicationId}.
*/
- public void removeComplication(ComplicationId id) {
- if (!mEntries.containsKey(id)) {
+ public boolean removeComplication(ComplicationId id) {
+ final ViewEntry entry = mEntries.remove(id);
+
+ if (entry == null) {
Log.e(TAG, "could not find id:" + id);
- return;
+ return false;
}
- final ViewEntry entry = mEntries.get(id);
entry.remove();
+ return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index d36bb72..758609a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -2373,7 +2373,8 @@
// Hack level over 9000: To speed up wake-and-unlock sequence, force it to report
// the next draw from here, so we don't have to wait for window manager to signal
// this to our ViewRootImpl.
- mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw();
+ mKeyguardViewControllerLazy.get().getViewRootImpl().setReportNextDraw(
+ false /* syncBuffer */);
mScreenOnCoordinator.setWakeAndUnlocking(false);
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index d2f953f..b8da46e 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -30,13 +30,14 @@
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Rect;
+import android.graphics.drawable.Animatable;
+import android.graphics.drawable.Animatable2;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Process;
-import android.text.Layout;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -67,6 +68,7 @@
import com.android.systemui.util.time.SystemClock;
import java.net.URISyntaxException;
+import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -637,9 +639,34 @@
private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
ConstraintSet collapsedSet, ConstraintSet expandedSet, boolean showInCompact) {
+ AnimationBindHandler animHandler;
+ if (button.getTag() == null) {
+ animHandler = new AnimationBindHandler();
+ button.setTag(animHandler);
+ } else {
+ animHandler = (AnimationBindHandler) button.getTag();
+ }
+
+ animHandler.tryExecute(() -> {
+ bindSemanticButton(animHandler, button, mediaAction,
+ collapsedSet, expandedSet, showInCompact);
+ });
+ }
+
+ private void bindSemanticButton(final AnimationBindHandler animHandler,
+ final ImageButton button, MediaAction mediaAction, ConstraintSet collapsedSet,
+ ConstraintSet expandedSet, boolean showInCompact) {
+
+ animHandler.unregisterAll();
if (mediaAction != null) {
- button.setImageIcon(mediaAction.getIcon());
+ final Drawable icon = mediaAction.getIcon();
+ button.setImageDrawable(icon);
button.setContentDescription(mediaAction.getContentDescription());
+ final Drawable bgDrawable = mediaAction.getBackground();
+ button.setBackground(bgDrawable);
+
+ animHandler.tryRegister(icon);
+ animHandler.tryRegister(bgDrawable);
Runnable action = mediaAction.getAction();
if (action == null) {
@@ -651,19 +678,75 @@
logSmartspaceCardReported(SMARTSPACE_CARD_CLICK_EVENT,
/* isRecommendationCard */ false);
action.run();
+
+ if (icon instanceof Animatable) {
+ ((Animatable) icon).start();
+ }
+ if (bgDrawable instanceof Animatable) {
+ ((Animatable) bgDrawable).start();
+ }
}
});
}
} else {
- button.setImageIcon(null);
+ button.setImageDrawable(null);
button.setContentDescription(null);
button.setEnabled(false);
+ button.setBackground(mContext.getDrawable(R.drawable.qs_media_round_button_background));
}
setVisibleAndAlpha(collapsedSet, button.getId(), mediaAction != null && showInCompact);
setVisibleAndAlpha(expandedSet, button.getId(), mediaAction != null);
}
+ private static class AnimationBindHandler extends Animatable2.AnimationCallback {
+ private ArrayList<Runnable> mOnAnimationsComplete = new ArrayList<>();
+ private ArrayList<Animatable2> mRegistrations = new ArrayList<>();
+
+ public void tryRegister(Drawable drawable) {
+ if (drawable instanceof Animatable2) {
+ Animatable2 anim = (Animatable2) drawable;
+ anim.registerAnimationCallback(this);
+ mRegistrations.add(anim);
+ }
+ }
+
+ public void unregisterAll() {
+ for (Animatable2 anim : mRegistrations) {
+ anim.unregisterAnimationCallback(this);
+ }
+ mRegistrations.clear();
+ }
+
+ public boolean isAnimationRunning() {
+ for (Animatable2 anim : mRegistrations) {
+ if (anim.isRunning()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void tryExecute(Runnable action) {
+ if (isAnimationRunning()) {
+ mOnAnimationsComplete.add(action);
+ } else {
+ action.run();
+ }
+ }
+
+ @Override
+ public void onAnimationEnd(Drawable drawable) {
+ super.onAnimationEnd(drawable);
+ if (!isAnimationRunning()) {
+ for (Runnable action : mOnAnimationsComplete) {
+ action.run();
+ }
+ mOnAnimationsComplete.clear();
+ }
+ }
+ }
+
@Nullable
private ActivityLaunchAnimator.Controller buildLaunchAnimatorController(
TransitionLayout player) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
index f1712db..47a0991 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaData.kt
@@ -170,9 +170,10 @@
/** State of a media action. */
data class MediaAction(
- val icon: Icon?,
+ val icon: Drawable?,
val action: Runnable?,
- val contentDescription: CharSequence?
+ val contentDescription: CharSequence?,
+ val background: Drawable?
)
/** State of the media device. */
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index f457ae7..5c36cab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -695,11 +695,12 @@
Icon.createWithResource(sbn.packageName, action.getIcon()!!.getResId())
} else {
action.getIcon()
- }.setTint(themeText)
+ }.setTint(themeText).loadDrawable(context)
val mediaAction = MediaAction(
mediaActionIcon,
runnable,
- action.title)
+ action.title,
+ context.getDrawable(R.drawable.qs_media_round_button_background))
actionIcons.add(mediaAction)
}
}
@@ -789,30 +790,34 @@
return when (action) {
PlaybackState.ACTION_PLAY -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play),
+ context.getDrawable(R.drawable.ic_media_play),
{ controller.transportControls.play() },
- context.getString(R.string.controls_media_button_play)
+ context.getString(R.string.controls_media_button_play),
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
PlaybackState.ACTION_PAUSE -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_pause),
+ context.getDrawable(R.drawable.ic_media_pause),
{ controller.transportControls.pause() },
- context.getString(R.string.controls_media_button_pause)
+ context.getString(R.string.controls_media_button_pause),
+ context.getDrawable(R.drawable.ic_media_pause_container)
)
}
PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_prev),
+ context.getDrawable(R.drawable.ic_media_prev),
{ controller.transportControls.skipToPrevious() },
- context.getString(R.string.controls_media_button_prev)
+ context.getString(R.string.controls_media_button_prev),
+ context.getDrawable(R.drawable.qs_media_round_button_background)
)
}
PlaybackState.ACTION_SKIP_TO_NEXT -> {
MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_next),
+ context.getDrawable(R.drawable.ic_media_next),
{ controller.transportControls.skipToNext() },
- context.getString(R.string.controls_media_button_next)
+ context.getString(R.string.controls_media_button_next),
+ context.getDrawable(R.drawable.qs_media_round_button_background)
)
}
else -> null
@@ -835,9 +840,10 @@
val it = state.customActions[index]
return MediaAction(
- Icon.createWithResource(packageName, it.icon),
+ Icon.createWithResource(packageName, it.icon).loadDrawable(context),
{ controller.transportControls.sendCustomAction(it, it.extras) },
- it.name
+ it.name,
+ context.getDrawable(R.drawable.ic_media_pause_container)
)
}
@@ -900,9 +906,11 @@
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- Icon.createWithResource(context, R.drawable.ic_media_play).setTint(themeText),
+ Icon.createWithResource(context, R.drawable.ic_media_play)
+ .setTint(themeText).loadDrawable(context),
action,
- context.getString(R.string.controls_media_resume)
+ context.getString(R.string.controls_media_resume),
+ context.getDrawable(R.drawable.ic_media_play_container)
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 4e63104..e751d54 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -92,6 +92,7 @@
private float mLastPanelFraction;
private float mSquishinessFraction = 1;
private boolean mQsDisabled;
+ private int[] mTemp = new int[2];
private final RemoteInputQuickSettingsDisabler mRemoteInputQuickSettingsDisabler;
private final MediaHost mQsMediaHost;
@@ -141,6 +142,8 @@
*/
private float mFullShadeProgress;
+ private boolean mOverScrolling;
+
@Inject
public QSFragment(RemoteInputQuickSettingsDisabler remoteInputQsDisabler,
QSTileHost qsTileHost,
@@ -498,6 +501,12 @@
}
@Override
+ public void setOverScrollAmount(int overScrollAmount) {
+ mOverScrolling = overScrollAmount != 0;
+ getView().setTranslationY(overScrollAmount);
+ }
+
+ @Override
public int getHeightDiff() {
return mQSPanelScrollView.getBottom() - mHeader.getBottom()
+ mHeader.getPaddingBottom();
@@ -515,7 +524,7 @@
? 1 : QSAnimator.SHORT_PARALLAX_AMOUNT) * (expansion - 1);
boolean onKeyguard = isKeyguardState();
boolean onKeyguardAndExpanded = onKeyguard && !mShowCollapsedOnKeyguard;
- if (!mHeaderAnimating && !headerWillBeAnimating()) {
+ if (!mHeaderAnimating && !headerWillBeAnimating() && !mOverScrolling) {
getView().setTranslationY(
onKeyguardAndExpanded
? translationScaleY * mHeader.getHeight()
@@ -600,8 +609,11 @@
}
mQSPanelScrollView.setClipBounds(mQsBounds);
- mQsMediaHost.getCurrentClipping().set(0, 0, getView().getMeasuredWidth(),
- mQSPanelScrollView.getMeasuredHeight() - mQSPanelScrollView.getPaddingBottom());
+ mQSPanelScrollView.getLocationOnScreen(mTemp);
+ int top = mTemp[1];
+ mQsMediaHost.getCurrentClipping().set(0, top, getView().getMeasuredWidth(),
+ top + mQSPanelScrollView.getMeasuredHeight()
+ - mQSPanelScrollView.getPaddingBottom());
}
private void updateMediaPositions() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt
new file mode 100644
index 0000000..de37a38
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockScreenShadeOverScroller.kt
@@ -0,0 +1,8 @@
+package com.android.systemui.statusbar
+
+/** Represents an over scroller for the transition to full shade on lock screen. */
+interface LockScreenShadeOverScroller {
+
+ /** The amount in pixels that the user has dragged to expand the shade. */
+ var expansionDragDownAmount: Float
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
index 310eb4f..8f78feb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/LockscreenShadeTransitionController.kt
@@ -64,6 +64,8 @@
private val scrimController: ScrimController,
private val depthController: NotificationShadeDepthController,
private val context: Context,
+ private val splitShadeOverScrollerFactory: SplitShadeLockScreenOverScroller.Factory,
+ private val singleShadeOverScrollerFactory: SingleShadeLockScreenOverScroller.Factory,
wakefulnessLifecycle: WakefulnessLifecycle,
configurationController: ConfigurationController,
falsingManager: FalsingManager,
@@ -194,6 +196,27 @@
*/
val touchHelper = DragDownHelper(falsingManager, falsingCollector, this, context)
+ private val splitShadeOverScroller: SplitShadeLockScreenOverScroller by lazy {
+ splitShadeOverScrollerFactory.create(qS, nsslController)
+ }
+
+ private val phoneShadeOverScroller: SingleShadeLockScreenOverScroller by lazy {
+ singleShadeOverScrollerFactory.create(nsslController)
+ }
+
+ /**
+ * [LockScreenShadeOverScroller] property that delegates to either
+ * [SingleShadeLockScreenOverScroller] or [SplitShadeLockScreenOverScroller].
+ *
+ * There are currently two different implementations, as the over scroll behavior is different
+ * on single shade and split shade.
+ *
+ * On single shade, only notifications are over scrolled, whereas on split shade, everything is
+ * over scrolled.
+ */
+ private val shadeOverScroller: LockScreenShadeOverScroller
+ get() = if (useSplitShade) splitShadeOverScroller else phoneShadeOverScroller
+
init {
updateResources()
configurationController.addCallback(object : ConfigurationController.ConfigurationListener {
@@ -410,7 +433,7 @@
if (!nsslController.isInLockedDownShade() || field == 0f || forceApplyAmount) {
val notificationShelfProgress =
MathUtils.saturate(dragDownAmount / notificationShelfTransitionDistance)
- nsslController.setTransitionToFullShadeAmount(field, notificationShelfProgress)
+ nsslController.setTransitionToFullShadeAmount(notificationShelfProgress)
qSDragProgress = MathUtils.saturate(dragDownAmount / qsTransitionDistance)
qS.setTransitionToFullShadeAmount(field, qSDragProgress)
@@ -422,6 +445,7 @@
transitionToShadeAmountScrim(field)
transitionToShadeAmountCommon(field)
transitionToShadeAmountKeyguard(field)
+ shadeOverScroller.expansionDragDownAmount = dragDownAmount
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
new file mode 100644
index 0000000..575f354
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScroller.kt
@@ -0,0 +1,74 @@
+package com.android.systemui.statusbar
+
+import android.content.Context
+import android.content.res.Configuration
+import android.util.MathUtils
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class SingleShadeLockScreenOverScroller
+@AssistedInject
+constructor(
+ configurationController: ConfigurationController,
+ private val context: Context,
+ private val statusBarStateController: SysuiStatusBarStateController,
+ @Assisted private val nsslController: NotificationStackScrollLayoutController
+) : LockScreenShadeOverScroller {
+
+ private var maxOverScrollAmount = 0
+ private var totalDistanceForFullShadeTransition = 0
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ }
+
+ private fun updateResources() {
+ val resources = context.resources
+ totalDistanceForFullShadeTransition =
+ resources.getDimensionPixelSize(R.dimen.lockscreen_shade_qs_transition_distance)
+ maxOverScrollAmount =
+ resources.getDimensionPixelSize(R.dimen.lockscreen_shade_max_over_scroll_amount)
+ }
+
+ override var expansionDragDownAmount: Float = 0f
+ set(value) {
+ if (value == field) {
+ return
+ }
+ field = value
+ overScroll()
+ }
+
+ private fun overScroll() {
+ var extraTopInset = 0.0f
+ if (statusBarStateController.state == StatusBarState.KEYGUARD) {
+ val viewHeight = nsslController.height
+ val overallProgress = MathUtils.saturate(expansionDragDownAmount / viewHeight)
+ val transitionProgress =
+ Interpolators.getOvershootInterpolation(
+ overallProgress,
+ 0.6f,
+ totalDistanceForFullShadeTransition.toFloat() / viewHeight.toFloat())
+ extraTopInset = transitionProgress * maxOverScrollAmount
+ }
+ nsslController.setOverScrollAmount(extraTopInset.toInt())
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(
+ nsslController: NotificationStackScrollLayoutController
+ ): SingleShadeLockScreenOverScroller
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
new file mode 100644
index 0000000..96ce6b4
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScroller.kt
@@ -0,0 +1,129 @@
+package com.android.systemui.statusbar
+
+import android.animation.Animator
+import android.animation.ValueAnimator
+import android.content.Context
+import android.content.res.Configuration
+import android.util.MathUtils
+import android.view.animation.PathInterpolator
+import com.android.internal.annotations.VisibleForTesting
+import com.android.systemui.R
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import dagger.assisted.Assisted
+import dagger.assisted.AssistedFactory
+import dagger.assisted.AssistedInject
+
+class SplitShadeLockScreenOverScroller
+@AssistedInject
+constructor(
+ configurationController: ConfigurationController,
+ private val context: Context,
+ private val scrimController: ScrimController,
+ private val statusBarStateController: SysuiStatusBarStateController,
+ @Assisted private val qS: QS,
+ @Assisted private val nsslController: NotificationStackScrollLayoutController
+) : LockScreenShadeOverScroller {
+
+ private var releaseOverScrollAnimator: Animator? = null
+ private var transitionToFullShadeDistance = 0
+ private var releaseOverScrollDuration = 0L
+ private var maxOverScrollAmount = 0
+ private var previousOverscrollAmount = 0
+
+ init {
+ updateResources()
+ configurationController.addCallback(
+ object : ConfigurationController.ConfigurationListener {
+ override fun onConfigChanged(newConfig: Configuration?) {
+ updateResources()
+ }
+ })
+ }
+
+ private fun updateResources() {
+ val resources = context.resources
+ transitionToFullShadeDistance =
+ resources.getDimensionPixelSize(R.dimen.lockscreen_shade_full_transition_distance)
+ maxOverScrollAmount =
+ resources.getDimensionPixelSize(R.dimen.lockscreen_shade_max_over_scroll_amount)
+ releaseOverScrollDuration =
+ resources.getInteger(R.integer.lockscreen_shade_over_scroll_release_duration).toLong()
+ }
+
+ override var expansionDragDownAmount: Float = 0f
+ set(dragDownAmount) {
+ if (field == dragDownAmount) {
+ return
+ }
+ field = dragDownAmount
+ if (shouldOverscroll()) {
+ overScroll(dragDownAmount)
+ } else if (shouldReleaseOverscroll()) {
+ releaseOverScroll()
+ }
+ }
+
+ private fun shouldOverscroll() = statusBarStateController.state == StatusBarState.KEYGUARD
+
+ private fun shouldReleaseOverscroll() = !shouldOverscroll() && previousOverscrollAmount != 0
+
+ private fun overScroll(dragDownAmount: Float) {
+ val overscrollAmount: Int = calculateOverscrollAmount(dragDownAmount)
+ applyOverscroll(overscrollAmount)
+ previousOverscrollAmount = overscrollAmount
+ }
+
+ private fun applyOverscroll(overscrollAmount: Int) {
+ qS.setOverScrollAmount(overscrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overscrollAmount)
+ nsslController.setOverScrollAmount(overscrollAmount)
+ }
+
+ private fun calculateOverscrollAmount(dragDownAmount: Float): Int {
+ val fullHeight: Int = nsslController.height
+ val fullHeightProgress: Float = MathUtils.saturate(dragDownAmount / fullHeight)
+ val overshootStart: Float = transitionToFullShadeDistance / fullHeight.toFloat()
+ val overShootTransitionProgress: Float =
+ Interpolators.getOvershootInterpolation(
+ fullHeightProgress, OVER_SHOOT_AMOUNT, overshootStart)
+ return (overShootTransitionProgress * maxOverScrollAmount).toInt()
+ }
+
+ private fun releaseOverScroll() {
+ val animator = ValueAnimator.ofInt(previousOverscrollAmount, 0)
+ animator.addUpdateListener {
+ val overScrollAmount = it.animatedValue as Int
+ qS.setOverScrollAmount(overScrollAmount)
+ scrimController.setNotificationsOverScrollAmount(overScrollAmount)
+ nsslController.setOverScrollAmount(overScrollAmount)
+ }
+ animator.interpolator = RELEASE_OVER_SCROLL_INTERPOLATOR
+ animator.duration = releaseOverScrollDuration
+ animator.start()
+ releaseOverScrollAnimator = animator
+ previousOverscrollAmount = 0
+ }
+
+ @VisibleForTesting
+ internal fun finishAnimations() {
+ releaseOverScrollAnimator?.end()
+ releaseOverScrollAnimator = null
+ }
+
+ @AssistedFactory
+ fun interface Factory {
+ fun create(
+ qS: QS,
+ nsslController: NotificationStackScrollLayoutController
+ ): SplitShadeLockScreenOverScroller
+ }
+
+ companion object {
+ private const val OVER_SHOOT_AMOUNT = 0.6f
+ private val RELEASE_OVER_SCROLL_INTERPOLATOR = PathInterpolator(0.17f, 0f, 0f, 1f)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index d98f8a7..5bc50ae 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -41,7 +41,6 @@
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
import android.util.Log;
-import android.util.MathUtils;
import android.util.Pair;
import android.view.Display;
import android.view.LayoutInflater;
@@ -65,7 +64,6 @@
import com.android.systemui.Gefingerpoken;
import com.android.systemui.R;
import com.android.systemui.SwipeHelper;
-import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.colorextraction.SysuiColorExtractor;
@@ -205,18 +203,6 @@
private ColorExtractor.OnColorsChangedListener mOnColorsChangedListener;
- /**
- * The total distance in pixels that the full shade transition takes to transition entirely to
- * the full shade.
- */
- private int mTotalDistanceForFullShadeTransition;
-
- /**
- * The amount of movement the notifications do when transitioning to the full shade before
- * reaching the overstrech
- */
- private int mNotificationDragDownMovement;
-
@VisibleForTesting
final View.OnAttachStateChangeListener mOnAttachStateChangeListener =
new View.OnAttachStateChangeListener() {
@@ -304,10 +290,6 @@
private NotifStats mNotifStats = NotifStats.getEmpty();
private void updateResources() {
- mNotificationDragDownMovement = mResources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_notification_movement);
- mTotalDistanceForFullShadeTransition = mResources.getDimensionPixelSize(
- R.dimen.lockscreen_shade_qs_transition_distance);
mNotificationStackSizeCalculator.updateResources();
}
@@ -1537,8 +1519,6 @@
}
/**
- * @param amount The amount of pixels we have currently dragged down
- * for the lockscreen to shade transition. 0f for all other states.
* @param fraction The fraction of lockscreen to shade transition.
* 0f for all other states.
*
@@ -1546,18 +1526,15 @@
* LockscreenShadeTransitionController resets amount and fraction to 0, where they remain
* until the next lockscreen-to-shade transition.
*/
- public void setTransitionToFullShadeAmount(float amount, float fraction) {
+ public void setTransitionToFullShadeAmount(float fraction) {
mView.setFractionToShade(fraction);
+ }
- float extraTopInset = 0.0f;
- if (mStatusBarStateController.getState() == KEYGUARD) {
- float overallProgress = MathUtils.saturate(amount / mView.getHeight());
- float transitionProgress = Interpolators.getOvershootInterpolation(overallProgress,
- 0.6f,
- (float) mTotalDistanceForFullShadeTransition / (float) mView.getHeight());
- extraTopInset = transitionProgress * mNotificationDragDownMovement;
- }
- mView.setExtraTopInsetForFullShadeTransition(extraTopInset);
+ /**
+ * Sets the amount of vertical over scroll that should be performed on NSSL.
+ */
+ public void setOverScrollAmount(int overScrollAmount) {
+ mView.setExtraTopInsetForFullShadeTransition(overScrollAmount);
}
/** */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 7882fff..999ebc1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -3210,12 +3210,14 @@
@Override
protected void onUnlockHintFinished() {
super.onUnlockHintFinished();
+ mScrimController.setExpansionAffectsAlpha(true);
mNotificationStackScrollLayoutController.setUnlockHintRunning(false);
}
@Override
protected void onUnlockHintStarted() {
super.onUnlockHintStarted();
+ mScrimController.setExpansionAffectsAlpha(false);
mNotificationStackScrollLayoutController.setUnlockHintRunning(true);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 7a69dad..7e22510 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -645,6 +645,13 @@
}
/**
+ * Sets the amount of vertical over scroll that should be performed on the notifications scrim.
+ */
+ public void setNotificationsOverScrollAmount(int overScrollAmount) {
+ mNotificationsScrim.setTranslationY(overScrollAmount);
+ }
+
+ /**
* Current state of the QuickSettings when pulling it from the top.
*
* @param expansionFraction From 0 to 1 where 0 means collapsed and 1 expanded.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index cfac9651..42c3c7f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -82,7 +82,6 @@
import com.android.systemui.util.concurrency.FakeExecution;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -100,7 +99,6 @@
import javax.inject.Provider;
-@Ignore
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index d1d9ec3..35bcfcd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -18,6 +18,7 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -414,4 +415,37 @@
assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
});
}
+
+ /**
+ * Ensures a second removal of a complication is a no-op.
+ */
+ @Test
+ public void testDoubleRemoval() {
+ final ComplicationLayoutEngine engine =
+ new ComplicationLayoutEngine(mLayout, 0, mTouchSession, 0, 0);
+
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ engine.addComplication(firstViewInfo.id, firstViewInfo.view, firstViewInfo.lp,
+ firstViewInfo.category);
+ verify(mLayout).addView(firstViewInfo.view);
+
+ assertThat(engine.removeComplication(firstViewInfo.id)).isTrue();
+ verify(firstViewInfo.view).getParent();
+ verify(mLayout).removeView(firstViewInfo.view);
+
+ Mockito.clearInvocations(mLayout, firstViewInfo.view);
+ assertThat(engine.removeComplication(firstViewInfo.id)).isFalse();
+ verify(firstViewInfo.view, never()).getParent();
+ verify(mLayout, never()).removeView(firstViewInfo.view);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index d0f2816..04609ad 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -19,8 +19,9 @@
import org.mockito.Mockito.`when` as whenever
import android.content.Intent
import android.graphics.Color
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.GradientDrawable
-import android.graphics.drawable.Icon
import android.graphics.drawable.RippleDrawable
import android.media.MediaMetadata
import android.media.session.MediaSession
@@ -60,6 +61,7 @@
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.Mock
+import org.mockito.Mockito.any
import org.mockito.Mockito.mock
import org.mockito.Mockito.never
import org.mockito.Mockito.times
@@ -290,15 +292,15 @@
@Test
fun bindSemanticActions() {
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
val semanticActions = MediaButton(
- playOrPause = MediaAction(icon, Runnable {}, "play"),
- nextOrCustom = MediaAction(icon, Runnable {}, "next"),
- custom0 = MediaAction(icon, null, "custom 0"),
- custom1 = MediaAction(icon, null, "custom 1")
+ playOrPause = MediaAction(icon, Runnable {}, "play", bg),
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", bg),
+ custom0 = MediaAction(icon, null, "custom 0", bg),
+ custom1 = MediaAction(icon, null, "custom 1", bg)
)
val state = mediaData.copy(semanticActions = semanticActions)
-
player.attachPlayer(viewHolder)
player.bindPlayer(state, PACKAGE)
@@ -338,10 +340,10 @@
fun bind_seekBarDisabled_seekBarVisibilityIsSetToInvisible() {
whenever(seekBarViewModel.getEnabled()).thenReturn(false)
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
val semanticActions = MediaButton(
- playOrPause = MediaAction(icon, Runnable {}, "play"),
- nextOrCustom = MediaAction(icon, Runnable {}, "next")
+ playOrPause = MediaAction(icon, Runnable {}, "play", null),
+ nextOrCustom = MediaAction(icon, Runnable {}, "next", null)
)
val state = mediaData.copy(semanticActions = semanticActions)
@@ -365,13 +367,14 @@
@Test
fun bindNotificationActions() {
- val icon = Icon.createWithResource(context, android.R.drawable.ic_media_play)
+ val icon = context.getDrawable(android.R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.qs_media_round_button_background)
val actions = listOf(
- MediaAction(icon, Runnable {}, "previous"),
- MediaAction(icon, Runnable {}, "play"),
- MediaAction(icon, null, "next"),
- MediaAction(icon, null, "custom 0"),
- MediaAction(icon, Runnable {}, "custom 1")
+ MediaAction(icon, Runnable {}, "previous", bg),
+ MediaAction(icon, Runnable {}, "play", bg),
+ MediaAction(icon, null, "next", bg),
+ MediaAction(icon, null, "custom 0", bg),
+ MediaAction(icon, Runnable {}, "custom 1", bg)
)
val state = mediaData.copy(actions = actions,
actionsToShowInCompact = listOf(1, 2),
@@ -413,6 +416,72 @@
}
@Test
+ fun bindAnimatedSemanticActions() {
+ val mockAvd0 = mock(AnimatedVectorDrawable::class.java)
+ val mockAvd1 = mock(AnimatedVectorDrawable::class.java)
+ val mockAvd2 = mock(AnimatedVectorDrawable::class.java)
+ whenever(mockAvd0.mutate()).thenReturn(mockAvd0)
+ whenever(mockAvd1.mutate()).thenReturn(mockAvd1)
+ whenever(mockAvd2.mutate()).thenReturn(mockAvd2)
+
+ val icon = context.getDrawable(R.drawable.ic_media_play)
+ val bg = context.getDrawable(R.drawable.ic_media_play_container)
+ val semanticActions0 = MediaButton(
+ playOrPause = MediaAction(mockAvd0, Runnable {}, "play", null))
+ val semanticActions1 = MediaButton(
+ playOrPause = MediaAction(mockAvd1, Runnable {}, "pause", null))
+ val semanticActions2 = MediaButton(
+ playOrPause = MediaAction(mockAvd2, Runnable {}, "loading", null))
+ val state0 = mediaData.copy(semanticActions = semanticActions0)
+ val state1 = mediaData.copy(semanticActions = semanticActions1)
+ val state2 = mediaData.copy(semanticActions = semanticActions2)
+
+ player.attachPlayer(viewHolder)
+ player.bindPlayer(state0, PACKAGE)
+
+ // Validate first binding
+ assertThat(actionPlayPause.isEnabled()).isTrue()
+ assertThat(actionPlayPause.contentDescription).isEqualTo("play")
+ verify(collapsedSet).setVisibility(R.id.actionPlayPause, ConstraintSet.VISIBLE)
+ assertThat(actionPlayPause.hasOnClickListeners()).isTrue()
+
+ // Trigger animation & update mock
+ actionPlayPause.performClick()
+ verify(mockAvd0, times(1)).start()
+ whenever(mockAvd0.isRunning()).thenReturn(true)
+
+ // Validate states no longer bind
+ player.bindPlayer(state1, PACKAGE)
+ player.bindPlayer(state2, PACKAGE)
+ assertThat(actionPlayPause.contentDescription).isEqualTo("play")
+
+ // Complete animation and run callbacks
+ whenever(mockAvd0.isRunning()).thenReturn(false)
+ val captor = ArgumentCaptor.forClass(Animatable2.AnimationCallback::class.java)
+ verify(mockAvd0, times(1)).registerAnimationCallback(captor.capture())
+ verify(mockAvd1, never())
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, never())
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ captor.getValue().onAnimationEnd(mockAvd0)
+
+ // Validate correct state was bound
+ assertThat(actionPlayPause.contentDescription).isEqualTo("loading")
+ verify(mockAvd0, times(1))
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, times(1)
+ ).registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, times(1))
+ .registerAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd0, times(1))
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd1, times(1))
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ verify(mockAvd2, never())
+ .unregisterAnimationCallback(any(Animatable2.AnimationCallback::class.java))
+ }
+
+ @Test
fun bindText() {
player.attachPlayer(viewHolder)
player.bindPlayer(mediaData, PACKAGE)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index 1d2a0ca..52c8a55 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -44,6 +44,7 @@
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.junit.MockitoJUnit
private fun <T> anyObject(): T {
@@ -75,6 +76,8 @@
@Mock lateinit var expandHelperCallback: ExpandHelper.Callback
@Mock lateinit var mCentralSurfaces: CentralSurfaces
@Mock lateinit var qS: QS
+ @Mock lateinit var singleShadeOverScroller: SingleShadeLockScreenOverScroller
+ @Mock lateinit var splitShadeOverScroller: SplitShadeLockScreenOverScroller
@JvmField @Rule val mockito = MockitoJUnit.rule()
private val configurationController = FakeConfigurationController()
@@ -104,7 +107,9 @@
context = context,
configurationController = configurationController,
falsingManager = falsingManager,
- dumpManager = dumpManager
+ dumpManager = dumpManager,
+ splitShadeOverScrollerFactory = { _, _ -> splitShadeOverScroller },
+ singleShadeOverScrollerFactory = { singleShadeOverScroller }
)
whenever(nsslController.view).thenReturn(stackscroller)
whenever(nsslController.expandHelperCallback).thenReturn(expandHelperCallback)
@@ -229,7 +234,7 @@
fun testDragDownAmountDoesntCallOutInLockedDownShade() {
whenever(nsslController.isInLockedDownShade).thenReturn(true)
transitionController.dragDownAmount = 10f
- verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
+ verify(nsslController, never()).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager, never()).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController, never()).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(notificationPanelController, never()).setTransitionToFullShadeAmount(anyFloat(),
@@ -240,7 +245,7 @@
@Test
fun testDragDownAmountCallsOut() {
transitionController.dragDownAmount = 10f
- verify(nsslController).setTransitionToFullShadeAmount(anyFloat(), anyFloat())
+ verify(nsslController).setTransitionToFullShadeAmount(anyFloat())
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(anyFloat())
verify(scrimController).setTransitionToFullShadeProgress(anyFloat(), anyFloat())
verify(notificationPanelController).setTransitionToFullShadeAmount(anyFloat(),
@@ -388,6 +393,26 @@
verify(mediaHierarchyManager).setTransitionToFullShadeAmount(10f)
}
+ @Test
+ fun setDragAmount_notInSplitShade_forwardsToSingleShadeOverScroller() {
+ disableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(singleShadeOverScroller).expansionDragDownAmount = 10f
+ verifyZeroInteractions(splitShadeOverScroller)
+ }
+
+ @Test
+ fun setDragAmount_inSplitShade_forwardsToSplitShadeOverScroller() {
+ enableSplitShade()
+
+ transitionController.dragDownAmount = 10f
+
+ verify(splitShadeOverScroller).expansionDragDownAmount = 10f
+ verifyZeroInteractions(singleShadeOverScroller)
+ }
+
private fun enableSplitShade() {
setSplitShadeEnabled(true)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
new file mode 100644
index 0000000..2606be5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SingleShadeLockScreenOverScrollerTest.kt
@@ -0,0 +1,56 @@
+package com.android.systemui.statusbar
+
+import org.mockito.Mockito.`when` as whenever
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.intThat
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class SingleShadeLockScreenOverScrollerTest : SysuiTestCase() {
+
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+
+ private lateinit var overScroller: SingleShadeLockScreenOverScroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ whenever(nsslController.height).thenReturn(1800)
+ overScroller =
+ SingleShadeLockScreenOverScroller(
+ FakeConfigurationController(),
+ context,
+ statusBarStateController,
+ nsslController
+ )
+ }
+
+ @Test
+ fun setDragDownAmount_onKeyguard_overScrolls() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ overScroller.expansionDragDownAmount = 10f
+
+ verify(nsslController).setOverScrollAmount(intThat { it > 0 })
+ }
+
+ @Test
+ fun setDragDownAmount_notOnKeyguard_doesNotOverScroll() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+
+ overScroller.expansionDragDownAmount = 10f
+
+ verify(nsslController).setOverScrollAmount(0)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
new file mode 100644
index 0000000..9d5099c
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/SplitShadeLockScreenOverScrollerTest.kt
@@ -0,0 +1,124 @@
+package com.android.systemui.statusbar
+
+import org.mockito.Mockito.`when` as whenever
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.qs.QS
+import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
+import com.android.systemui.statusbar.phone.ScrimController
+import com.android.systemui.statusbar.policy.FakeConfigurationController
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.atLeast
+import org.mockito.Mockito.intThat
+import org.mockito.Mockito.reset
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.verifyNoMoreInteractions
+import org.mockito.Mockito.verifyZeroInteractions
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class SplitShadeLockScreenOverScrollerTest : SysuiTestCase() {
+
+ private val configurationController = FakeConfigurationController()
+
+ @Mock private lateinit var scrimController: ScrimController
+ @Mock private lateinit var statusBarStateController: SysuiStatusBarStateController
+ @Mock private lateinit var qS: QS
+ @Mock private lateinit var nsslController: NotificationStackScrollLayoutController
+
+ private lateinit var overScroller: SplitShadeLockScreenOverScroller
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+
+ whenever(nsslController.height).thenReturn(1800)
+
+ overScroller =
+ SplitShadeLockScreenOverScroller(
+ configurationController,
+ context,
+ scrimController,
+ statusBarStateController,
+ qS,
+ nsslController)
+ }
+
+ @Test
+ fun setDragDownAmount_onKeyguard_appliesOverScroll() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+
+ setDragAmount(1000f)
+
+ verifyOverScrollPerformed()
+ }
+
+ @Test
+ fun setDragDownAmount_notOnKeyguard_doesNotApplyOverScroll() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+
+ setDragAmount(1000f)
+
+ verifyZeroInteractions(qS)
+ verifyZeroInteractions(scrimController)
+ verifyZeroInteractions(nsslController)
+ }
+
+ @Test
+ fun setDragAmount_onKeyguard_thenNotOnKeyguard_resetsOverScrollToZero() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ setDragAmount(1000f)
+ verifyOverScrollPerformed()
+ reset(qS, scrimController, nsslController)
+
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ setDragAmount(999f)
+ verifyOverScrollResetToZero()
+ }
+
+ @Test
+ fun setDragAmount_onKeyguard_thenNotOnKeyguard_multipleTimes_resetsOverScrollToZeroOnlyOnce() {
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.KEYGUARD)
+ setDragAmount(1000f)
+ whenever(statusBarStateController.state).thenReturn(StatusBarState.SHADE)
+ setDragAmount(999f)
+ reset(qS, scrimController, nsslController)
+
+ setDragAmount(998f)
+ setDragAmount(997f)
+ setDragAmount(996f)
+ verifyNoMoreOverScrollChanges()
+ }
+
+ private fun verifyOverScrollPerformed() {
+ verify(qS).setOverScrollAmount(intThat { it > 0 })
+ verify(scrimController).setNotificationsOverScrollAmount(intThat { it > 0 })
+ verify(nsslController).setOverScrollAmount(intThat { it > 0 })
+ }
+
+ private fun verifyOverScrollResetToZero() {
+ // Might be more than once as the animator might have multiple values close to zero that
+ // round down to zero.
+ verify(qS, atLeast(1)).setOverScrollAmount(0)
+ verify(scrimController, atLeast(1)).setNotificationsOverScrollAmount(0)
+ verify(nsslController, atLeast(1)).setOverScrollAmount(0)
+ }
+
+ private fun verifyNoMoreOverScrollChanges() {
+ verifyNoMoreInteractions(qS)
+ verifyNoMoreInteractions(scrimController)
+ verifyNoMoreInteractions(nsslController)
+ }
+
+ private fun setDragAmount(dragDownAmount: Float) {
+ overScroller.expansionDragDownAmount = dragDownAmount
+ overScroller.finishAnimations()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index 21d03ad..5add2f2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -930,6 +930,14 @@
verify(mLargeScreenShadeHeaderController).setActive(false);
}
+ @Test
+ public void testUnlockAnimationDoesNotAffectScrim() {
+ mNotificationPanelViewController.onUnlockHintStarted();
+ verify(mScrimController).setExpansionAffectsAlpha(false);
+ mNotificationPanelViewController.onUnlockHintFinished();
+ verify(mScrimController).setExpansionAffectsAlpha(true);
+ }
+
private void triggerPositionClockAndNotifications() {
mNotificationPanelViewController.closeQs();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 92c0e1a..134ad4b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -1296,6 +1296,33 @@
assertThat(mNotificationsScrim.getViewAlpha()).isEqualTo(notifProgress);
}
+ @Test
+ public void setNotificationsOverScrollAmount_setsTranslationYOnNotificationsScrim() {
+ int overScrollAmount = 10;
+
+ mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
+
+ assertThat(mNotificationsScrim.getTranslationY()).isEqualTo(overScrollAmount);
+ }
+
+ @Test
+ public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnBehindScrim() {
+ int overScrollAmount = 10;
+
+ mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
+
+ assertThat(mScrimBehind.getTranslationY()).isEqualTo(0);
+ }
+
+ @Test
+ public void setNotificationsOverScrollAmount_doesNotSetTranslationYOnFrontScrim() {
+ int overScrollAmount = 10;
+
+ mScrimController.setNotificationsOverScrollAmount(overScrollAmount);
+
+ assertThat(mScrimInFront.getTranslationY()).isEqualTo(0);
+ }
+
private void assertAlphaAfterExpansion(ScrimView scrim, float expectedAlpha, float expansion) {
mScrimController.setRawPanelExpansionFraction(expansion);
finishAnimationsImmediately();
diff --git a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
index 61e3da8..22c77e9 100644
--- a/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
+++ b/services/accessibility/java/com/android/server/accessibility/AccessibilityManagerService.java
@@ -4290,11 +4290,15 @@
}
private void onDoubleTapInternal(int displayId) {
+ AccessibilityInputFilter inputFilter = null;
synchronized (mLock) {
if (mHasInputFilter && mInputFilter != null) {
- mInputFilter.onDoubleTap(displayId);
+ inputFilter = mInputFilter;
}
}
+ if (inputFilter != null) {
+ inputFilter.onDoubleTap(displayId);
+ }
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index 3a26c46..f4c24a8 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -165,7 +165,10 @@
@Override
public void onTopActivityChanged(ComponentName topActivity, int uid) {
- if (mActivityListener != null) {
+ // Don't send onTopActivityChanged() callback when topActivity is null because it's defined
+ // as @NonNull in ActivityListener interface. Sends onDisplayEmpty() callback instead when
+ // there is no activity running on virtual display.
+ if (mActivityListener != null && topActivity != null) {
// Post callback on the main thread so it doesn't block activity launching
mHandler.post(() ->
mActivityListener.onTopActivityChanged(Display.INVALID_DISPLAY, topActivity));
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index eeff0de..f27ccb76 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -17265,8 +17265,8 @@
}
@Override
- public void deletePendingTopUid(int uid) {
- mPendingStartActivityUids.delete(uid);
+ public void deletePendingTopUid(int uid, long nowElapsed) {
+ mPendingStartActivityUids.delete(uid, nowElapsed);
}
@Override
diff --git a/services/core/java/com/android/server/am/OomAdjuster.java b/services/core/java/com/android/server/am/OomAdjuster.java
index 800f3ad..d635152 100644
--- a/services/core/java/com/android/server/am/OomAdjuster.java
+++ b/services/core/java/com/android/server/am/OomAdjuster.java
@@ -1254,7 +1254,7 @@
mService.mServices.foregroundServiceProcStateChangedLocked(uidRec);
}
}
- mService.mInternal.deletePendingTopUid(uidRec.getUid());
+ mService.mInternal.deletePendingTopUid(uidRec.getUid(), nowElapsed);
}
if (mLocalPowerManager != null) {
mLocalPowerManager.finishUidChanges();
diff --git a/services/core/java/com/android/server/am/PendingStartActivityUids.java b/services/core/java/com/android/server/am/PendingStartActivityUids.java
index 455c75b..bd60057 100644
--- a/services/core/java/com/android/server/am/PendingStartActivityUids.java
+++ b/services/core/java/com/android/server/am/PendingStartActivityUids.java
@@ -55,9 +55,15 @@
return false;
}
- synchronized void delete(int uid) {
+ synchronized void delete(int uid, long nowElapsed) {
final Pair<Integer, Long> pendingPid = mPendingUids.get(uid);
if (pendingPid != null) {
+ if (nowElapsed < pendingPid.second) {
+ Slog.i(TAG,
+ "updateOomAdj start time is before than pendingPid added,"
+ + " don't delete it");
+ return;
+ }
final long delay = SystemClock.elapsedRealtime() - pendingPid.second;
if (delay >= 1000 /*ms*/) {
Slog.i(TAG,
@@ -87,4 +93,4 @@
synchronized boolean isPendingTopUid(int uid) {
return mPendingUids.get(uid) != null;
}
-}
\ No newline at end of file
+}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 270a61b..1a482e4 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -252,8 +252,8 @@
return;
}
}
- setCommunicationRouteForClient(
- cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, device, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
}
@@ -283,8 +283,8 @@
return false;
}
}
- setCommunicationRouteForClient(
- cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, deviceAttr, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
return true;
@@ -348,26 +348,35 @@
}
/**
- * Returns the device currently requested for communication use case.
- * If the current audio mode owner is in the communication route client list,
- * use this preference.
- * Otherwise use first client's preference (first client corresponds to latest request).
- * null is returned if no client is in the list.
- * @return AudioDeviceAttributes the requested device for communication.
+ * Returns the communication client with the highest priority:
+ * - 1) the client which is currently also controlling the audio mode
+ * - 2) the first client in the stack if there is no audio mode owner
+ * - 3) no client otherwise
+ * @return CommunicationRouteClient the client driving the communication use case routing.
*/
-
@GuardedBy("mDeviceStateLock")
- private AudioDeviceAttributes requestedCommunicationDevice() {
- AudioDeviceAttributes device = null;
- for (CommunicationRouteClient cl : mCommunicationRouteClients) {
- if (cl.getPid() == mModeOwnerPid) {
- device = cl.getDevice();
+ private CommunicationRouteClient topCommunicationRouteClient() {
+ for (CommunicationRouteClient crc : mCommunicationRouteClients) {
+ if (crc.getPid() == mModeOwnerPid) {
+ return crc;
}
}
if (!mCommunicationRouteClients.isEmpty() && mModeOwnerPid == 0) {
- device = mCommunicationRouteClients.get(0).getDevice();
+ return mCommunicationRouteClients.get(0);
}
+ return null;
+ }
+ /**
+ * Returns the device currently requested for communication use case.
+ * Use the device requested by the communication route client selected by
+ * {@link #topCommunicationRouteClient()} if any or none otherwise.
+ * @return AudioDeviceAttributes the requested device for communication.
+ */
+ @GuardedBy("mDeviceStateLock")
+ private AudioDeviceAttributes requestedCommunicationDevice() {
+ CommunicationRouteClient crc = topCommunicationRouteClient();
+ AudioDeviceAttributes device = crc != null ? crc.getDevice() : null;
if (AudioService.DEBUG_COMM_RTE) {
Log.v(TAG, "requestedCommunicationDevice, device: "
+ device + " mode owner pid: " + mModeOwnerPid);
@@ -710,7 +719,7 @@
}
synchronized (mDeviceStateLock) {
mBluetoothScoOn = on;
- sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE, SENDMSG_QUEUE, eventSource);
+ postUpdateCommunicationRouteClient(eventSource);
}
}
@@ -770,7 +779,9 @@
synchronized (mDeviceStateLock) {
AudioDeviceAttributes device =
new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
- setCommunicationRouteForClient(cb, pid, device, scoAudioMode, eventSource);
+
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, device, scoAudioMode, eventSource));
}
}
}
@@ -788,8 +799,8 @@
if (client == null || !client.requestsBluetoothSco()) {
return;
}
- setCommunicationRouteForClient(
- cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ postSetCommunicationRouteForClient(new CommunicationClientInfo(
+ cb, pid, null, BtHelper.SCO_MODE_UNDEFINED, eventSource));
}
}
}
@@ -990,6 +1001,61 @@
MSG_I_SAVE_CLEAR_PREF_DEVICES_FOR_CAPTURE_PRESET, SENDMSG_QUEUE, capturePreset);
}
+ /*package*/ void postUpdateCommunicationRouteClient(String eventSource) {
+ sendLMsgNoDelay(MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT, SENDMSG_QUEUE, eventSource);
+ }
+
+ /*package*/ void postSetCommunicationRouteForClient(CommunicationClientInfo info) {
+ sendLMsgNoDelay(MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT, SENDMSG_QUEUE, info);
+ }
+
+ /*package*/ void postScoAudioStateChanged(int state) {
+ sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state);
+ }
+
+ /*package*/ static final class CommunicationClientInfo {
+ final @NonNull IBinder mCb;
+ final int mPid;
+ final @NonNull AudioDeviceAttributes mDevice;
+ final int mScoAudioMode;
+ final @NonNull String mEventSource;
+
+ CommunicationClientInfo(@NonNull IBinder cb, int pid, @NonNull AudioDeviceAttributes device,
+ int scoAudioMode, @NonNull String eventSource) {
+ mCb = cb;
+ mPid = pid;
+ mDevice = device;
+ mScoAudioMode = scoAudioMode;
+ mEventSource = eventSource;
+ }
+
+ // redefine equality op so we can match messages intended for this client
+ @Override
+ public boolean equals(Object o) {
+ if (o == null) {
+ return false;
+ }
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof CommunicationClientInfo)) {
+ return false;
+ }
+
+ return mCb.equals(((CommunicationClientInfo) o).mCb)
+ && mPid == ((CommunicationClientInfo) o).mPid;
+ }
+
+ @Override
+ public String toString() {
+ return "CommunicationClientInfo mCb=" + mCb.toString()
+ +"mPid=" + mPid
+ +"mDevice=" + mDevice.toString()
+ +"mScoAudioMode=" + mScoAudioMode
+ +"mEventSource=" + mEventSource;
+ }
+ }
+
//---------------------------------------------------------------------
// Method forwarding between the helper classes (BtHelper, AudioDeviceInventory)
// only call from a "handle"* method or "on"* method
@@ -1265,18 +1331,30 @@
synchronized (mDeviceStateLock) {
mModeOwnerPid = msg.arg1;
if (msg.arg2 != AudioSystem.MODE_RINGTONE) {
- onUpdateCommunicationRoute("setNewModeOwner");
+ onUpdateCommunicationRouteClient("setNewModeOwner");
}
}
}
break;
- case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
+
+ case MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
- onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
+ CommunicationClientInfo info = (CommunicationClientInfo) msg.obj;
+ setCommunicationRouteForClient(info.mCb, info.mPid, info.mDevice,
+ info.mScoAudioMode, info.mEventSource);
}
}
break;
+
+ case MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onUpdateCommunicationRouteClient((String) msg.obj);
+ }
+ }
+ break;
+
case MSG_L_UPDATE_COMMUNICATION_ROUTE:
synchronized (mSetModeLock) {
synchronized (mDeviceStateLock) {
@@ -1284,6 +1362,23 @@
}
}
break;
+
+ case MSG_L_COMMUNICATION_ROUTE_CLIENT_DIED:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ onCommunicationRouteClientDied((CommunicationRouteClient) msg.obj);
+ }
+ }
+ break;
+
+ case MSG_I_SCO_AUDIO_STATE_CHANGED:
+ synchronized (mSetModeLock) {
+ synchronized (mDeviceStateLock) {
+ mBtHelper.onScoAudioStateChanged(msg.arg1);
+ }
+ }
+ break;
+
case MSG_TOGGLE_HDMI:
synchronized (mDeviceStateLock) {
mDeviceInventory.onToggleHdmi();
@@ -1437,6 +1532,9 @@
private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE = 39;
private static final int MSG_IL_SET_PREF_DEVICES_FOR_STRATEGY = 40;
private static final int MSG_I_REMOVE_PREF_DEVICES_FOR_STRATEGY = 41;
+ private static final int MSG_L_SET_COMMUNICATION_ROUTE_FOR_CLIENT = 42;
+ private static final int MSG_L_UPDATE_COMMUNICATION_ROUTE_CLIENT = 43;
+ private static final int MSG_I_SCO_AUDIO_STATE_CHANGED = 44;
private static final int MSG_L_BT_ACTIVE_DEVICE_CHANGE_EXT = 45;
//
@@ -1679,9 +1777,8 @@
return;
}
Log.w(TAG, "Communication client died");
- setCommunicationRouteForClient(
- client.getBinder(), client.getPid(), null, BtHelper.SCO_MODE_UNDEFINED,
- "onCommunicationRouteClientDied");
+ removeCommunicationRouteClient(client.getBinder(), true);
+ onUpdateCommunicationRouteClient("onCommunicationRouteClientDied");
}
/**
@@ -1735,11 +1832,31 @@
AudioSystem.setParameters("BT_SCO=on");
}
if (preferredCommunicationDevice == null) {
- postRemovePreferredDevicesForStrategy(mCommunicationStrategyId);
+ removePreferredDevicesForStrategySync(mCommunicationStrategyId);
} else {
- postSetPreferredDevicesForStrategy(
+ setPreferredDevicesForStrategySync(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
}
+ onUpdatePhoneStrategyDevice(preferredCommunicationDevice);
+ }
+
+ /**
+ * Select new communication device from communication route client at the top of the stack
+ * and restore communication route including restarting SCO audio if needed.
+ */
+ // @GuardedBy("mSetModeLock")
+ @GuardedBy("mDeviceStateLock")
+ private void onUpdateCommunicationRouteClient(String eventSource) {
+ onUpdateCommunicationRoute(eventSource);
+ CommunicationRouteClient crc = topCommunicationRouteClient();
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "onUpdateCommunicationRouteClient, crc: "
+ + crc + " eventSource: " + eventSource);
+ }
+ if (crc != null) {
+ setCommunicationRouteForClient(crc.getBinder(), crc.getPid(), crc.getDevice(),
+ BtHelper.SCO_MODE_UNDEFINED, eventSource);
+ }
}
private void onUpdatePhoneStrategyDevice(AudioDeviceAttributes device) {
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 5aae319..d775581 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -1368,14 +1368,18 @@
mRm = (RoleManager) mContext.getSystemService(Context.ROLE_SERVICE);
if (mRm != null) {
mRm.addOnRoleHoldersChangedListenerAsUser(mExecutor, this, UserHandle.ALL);
- updateAssistantUId(true);
+ synchronized (mSettingsLock) {
+ updateAssistantUIdLocked(/* forceUpdate= */ true);
+ }
}
}
@Override
public void onRoleHoldersChanged(@NonNull String roleName, @NonNull UserHandle user) {
if (RoleManager.ROLE_ASSISTANT.equals(roleName)) {
- updateAssistantUId(false);
+ synchronized (mSettingsLock) {
+ updateAssistantUIdLocked(/* forceUpdate= */ false);
+ }
}
}
@@ -1474,7 +1478,7 @@
sendEncodedSurroundMode(mContentResolver, "onAudioServerDied");
sendEnabledSurroundFormats(mContentResolver, true);
AudioSystem.setRttEnabled(mRttEnabled);
- updateAssistantServicesUidsLocked();
+ resetAssistantServicesUidsLocked();
}
synchronized (mAccessibilityServiceUidsLock) {
@@ -1603,6 +1607,12 @@
}
@GuardedBy("mSettingsLock")
+ private void resetAssistantServicesUidsLocked() {
+ mAssistantUids.clear();
+ updateAssistantUIdLocked(/* forceUpdate= */ true);
+ }
+
+ @GuardedBy("mSettingsLock")
private void updateAssistantServicesUidsLocked() {
int[] assistantUids = mAssistantUids.stream().mapToInt(Integer::intValue).toArray();
AudioSystem.setAssistantServicesUids(assistantUids);
@@ -2386,7 +2396,7 @@
}
@GuardedBy("mSettingsLock")
- private void updateAssistantUId(boolean forceUpdate) {
+ private void updateAssistantUIdLocked(boolean forceUpdate) {
int assistantUid = INVALID_UID;
// Consider assistants in the following order of priority:
// 1) apk in assistant role
@@ -2484,7 +2494,7 @@
readDockAudioSettings(cr);
sendEncodedSurroundMode(cr, "readPersistedSettings");
sendEnabledSurroundFormats(cr, true);
- updateAssistantUId(true);
+ updateAssistantUIdLocked(/* forceUpdate= */ true);
resetActiveAssistantUidsLocked();
AudioSystem.setRttEnabled(mRttEnabled);
}
@@ -8317,7 +8327,7 @@
updateMasterBalance(mContentResolver);
updateEncodedSurroundOutput();
sendEnabledSurroundFormats(mContentResolver, mSurroundModeChanged);
- updateAssistantUId(false);
+ updateAssistantUIdLocked(/* forceUpdate= */ false);
}
}
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 49a935e..d10ed55 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -104,7 +104,7 @@
// SCO audio mode is virtual voice call (BluetoothHeadset.startScoUsingVirtualVoiceCall())
/*package*/ static final int SCO_MODE_VIRTUAL_CALL = 0;
// SCO audio mode is Voice Recognition (BluetoothHeadset.startVoiceRecognition())
- private static final int SCO_MODE_VR = 2;
+ private static final int SCO_MODE_VR = 2;
// max valid SCO audio mode values
private static final int SCO_MODE_MAX = 2;
@@ -305,69 +305,77 @@
BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
setBtScoActiveDevice(btDevice);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
- boolean broadcast = false;
- int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
- Log.i(TAG, "receiveBtEvent ACTION_AUDIO_STATE_CHANGED: " + btState);
- switch (btState) {
- case BluetoothHeadset.STATE_AUDIO_CONNECTED:
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
- && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- } else if (mDeviceBroker.isBluetoothScoRequested()) {
- // broadcast intent if the connection was initated by AudioService
- broadcast = true;
- }
- mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
- break;
- case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
- mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
- scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
- // There are two cases where we want to immediately reconnect audio:
- // 1) If a new start request was received while disconnecting: this was
- // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
- // 2) If audio was connected then disconnected via Bluetooth APIs and
- // we still have pending activation requests by apps: this is indicated by
- // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
- if (mScoAudioState == SCO_STATE_ACTIVATE_REQ
- || (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL
- && mDeviceBroker.isBluetoothScoRequested())) {
- if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
- && connectBluetoothScoAudioHelper(mBluetoothHeadset,
- mBluetoothHeadsetDevice, mScoAudioMode)) {
- mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
- scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
- broadcast = true;
- break;
- }
- }
- if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
- broadcast = true;
- }
- mScoAudioState = SCO_STATE_INACTIVE;
- break;
- case BluetoothHeadset.STATE_AUDIO_CONNECTING:
- if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
- && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
- mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
- }
- break;
- default:
- break;
- }
- if (broadcast) {
- broadcastScoConnectionState(scoAudioState);
- //FIXME: this is to maintain compatibility with deprecated intent
- // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
- Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
- newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
- sendStickyBroadcastToAll(newIntent);
- }
+ Log.i(TAG,"receiveBtEvent ACTION_AUDIO_STATE_CHANGED: "+btState);
+ mDeviceBroker.postScoAudioStateChanged(btState);
}
}
/**
+ * Exclusively called from AudioDeviceBroker when handling MSG_I_SCO_AUDIO_STATE_CHANGED
+ * as part of the serialization of the communication route selection
+ */
+ // @GuardedBy("AudioDeviceBroker.mSetModeLock")
+ @GuardedBy("AudioDeviceBroker.mDeviceStateLock")
+ void onScoAudioStateChanged(int state) {
+ boolean broadcast = false;
+ int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
+ switch (state) {
+ case BluetoothHeadset.STATE_AUDIO_CONNECTED:
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+ && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ } else if (mDeviceBroker.isBluetoothScoRequested()) {
+ // broadcast intent if the connection was initated by AudioService
+ broadcast = true;
+ }
+ mDeviceBroker.setBluetoothScoOn(true, "BtHelper.receiveBtEvent");
+ break;
+ case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
+ mDeviceBroker.setBluetoothScoOn(false, "BtHelper.receiveBtEvent");
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
+ // There are two cases where we want to immediately reconnect audio:
+ // 1) If a new start request was received while disconnecting: this was
+ // notified by requestScoState() setting state to SCO_STATE_ACTIVATE_REQ.
+ // 2) If audio was connected then disconnected via Bluetooth APIs and
+ // we still have pending activation requests by apps: this is indicated by
+ // state SCO_STATE_ACTIVE_EXTERNAL and BT SCO is requested.
+ if (mScoAudioState == SCO_STATE_ACTIVATE_REQ) {
+ if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null
+ && connectBluetoothScoAudioHelper(mBluetoothHeadset,
+ mBluetoothHeadsetDevice, mScoAudioMode)) {
+ mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
+ scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTING;
+ broadcast = true;
+ break;
+ }
+ }
+ if (mScoAudioState != SCO_STATE_ACTIVE_EXTERNAL) {
+ broadcast = true;
+ }
+ mScoAudioState = SCO_STATE_INACTIVE;
+ break;
+ case BluetoothHeadset.STATE_AUDIO_CONNECTING:
+ if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL
+ && mScoAudioState != SCO_STATE_DEACTIVATE_REQ) {
+ mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
+ }
+ break;
+ default:
+ break;
+ }
+ if(broadcast) {
+ broadcastScoConnectionState(scoAudioState);
+ //FIXME: this is to maintain compatibility with deprecated intent
+ // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
+ Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
+ newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
+ sendStickyBroadcastToAll(newIntent);
+ }
+
+ }
+ /**
*
* @return false if SCO isn't connected
*/
@@ -756,6 +764,15 @@
case SCO_STATE_ACTIVE_INTERNAL:
Log.w(TAG, "requestScoState: already in ACTIVE mode, simply return");
break;
+ case SCO_STATE_ACTIVE_EXTERNAL:
+ /* Confirm SCO Audio connection to requesting app as it is already connected
+ * externally (i.e. through SCO APIs by Telecom service).
+ * Once SCO Audio is disconnected by the external owner, we will reconnect it
+ * automatically on behalf of the requesting app and the state will move to
+ * SCO_STATE_ACTIVE_INTERNAL.
+ */
+ broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
+ break;
default:
Log.w(TAG, "requestScoState: failed to connect in state "
+ mScoAudioState + ", scoAudioMode=" + scoAudioMode);
diff --git a/services/core/java/com/android/server/notification/GroupHelper.java b/services/core/java/com/android/server/notification/GroupHelper.java
index 4f26809..273afcc 100644
--- a/services/core/java/com/android/server/notification/GroupHelper.java
+++ b/services/core/java/com/android/server/notification/GroupHelper.java
@@ -42,7 +42,7 @@
private final int mAutoGroupAtCount;
// count the number of ongoing notifications per group
- // userId -> (package name -> (group Id -> (set of notification keys)))
+ // userId|packageName -> (set of ongoing notifications that aren't in an app group)
final ArrayMap<String, ArraySet<String>>
mOngoingGroupCount = new ArrayMap<>();
@@ -55,52 +55,43 @@
mCallback = callback;
}
- private String generatePackageGroupKey(int userId, String pkg, String group) {
- return userId + "|" + pkg + "|" + group;
+ private String generatePackageKey(int userId, String pkg) {
+ return userId + "|" + pkg;
}
@VisibleForTesting
- protected int getOngoingGroupCount(int userId, String pkg, String group) {
- String key = generatePackageGroupKey(userId, pkg, group);
+ protected int getOngoingGroupCount(int userId, String pkg) {
+ String key = generatePackageKey(userId, pkg);
return mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0)).size();
}
- private void addToOngoingGroupCount(StatusBarNotification sbn, boolean add) {
- if (sbn.getNotification().isGroupSummary()) return;
- if (!sbn.isOngoing() && add) return;
- String group = sbn.getGroup();
- if (group == null) return;
- int userId = sbn.getUser().getIdentifier();
- String key = generatePackageGroupKey(userId, sbn.getPackageName(), group);
+ private void updateOngoingGroupCount(StatusBarNotification sbn, boolean add) {
+ if (sbn.getNotification().isGroupSummary()) {
+ return;
+ }
+ String key = generatePackageKey(sbn.getUserId(), sbn.getPackageName());
ArraySet<String> notifications = mOngoingGroupCount.getOrDefault(key, new ArraySet<>(0));
if (add) {
notifications.add(sbn.getKey());
mOngoingGroupCount.put(key, notifications);
} else {
notifications.remove(sbn.getKey());
- // we dont need to put it back if it is default
+ // we don't need to put it back if it is default
}
- String combinedKey = generatePackageGroupKey(userId, sbn.getPackageName(), group);
+
boolean needsOngoingFlag = notifications.size() > 0;
- mCallback.updateAutogroupSummary(userId, sbn.getPackageName(), needsOngoingFlag);
+ mCallback.updateAutogroupSummary(sbn.getUserId(), sbn.getPackageName(), needsOngoingFlag);
}
- public void onNotificationUpdated(StatusBarNotification childSbn,
- boolean autogroupSummaryExists) {
- if (childSbn.getGroup() != AUTOGROUP_KEY
- || childSbn.getNotification().isGroupSummary()) return;
- if (childSbn.isOngoing()) {
- addToOngoingGroupCount(childSbn, true);
- } else {
- addToOngoingGroupCount(childSbn, false);
- }
+ public void onNotificationUpdated(StatusBarNotification childSbn) {
+ updateOngoingGroupCount(childSbn, childSbn.isOngoing() && !childSbn.isAppGroup());
}
public void onNotificationPosted(StatusBarNotification sbn, boolean autogroupSummaryExists) {
- if (DEBUG) Log.i(TAG, "POSTED " + sbn.getKey());
try {
+ updateOngoingGroupCount(sbn, sbn.isOngoing() && !sbn.isAppGroup());
+
List<String> notificationsToGroup = new ArrayList<>();
- if (autogroupSummaryExists) addToOngoingGroupCount(sbn, true);
if (!sbn.isAppGroup()) {
// Not grouped by the app, add to the list of notifications for the app;
// send grouping update if app exceeds the autogrouping limit.
@@ -134,6 +125,7 @@
// Grouped, but not by us. Send updates to un-autogroup, if we grouped it.
maybeUngroup(sbn, false, sbn.getUserId());
}
+
} catch (Exception e) {
Slog.e(TAG, "Failure processing new notification", e);
}
@@ -141,7 +133,7 @@
public void onNotificationRemoved(StatusBarNotification sbn) {
try {
- addToOngoingGroupCount(sbn, false);
+ updateOngoingGroupCount(sbn, false);
maybeUngroup(sbn, true, sbn.getUserId());
} catch (Exception e) {
Slog.e(TAG, "Error processing canceled notification", e);
@@ -189,7 +181,8 @@
private void adjustAutogroupingSummary(int userId, String packageName, String triggeringKey,
boolean summaryNeeded) {
if (summaryNeeded) {
- mCallback.addAutoGroupSummary(userId, packageName, triggeringKey);
+ mCallback.addAutoGroupSummary(userId, packageName, triggeringKey,
+ getOngoingGroupCount(userId, packageName) > 0);
} else {
mCallback.removeAutoGroupSummary(userId, packageName);
}
@@ -209,7 +202,8 @@
protected interface Callback {
void addAutoGroup(String key);
void removeAutoGroup(String key);
- void addAutoGroupSummary(int userId, String pkg, String triggeringKey);
+ void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
+ boolean needsOngoingFlag);
void removeAutoGroupSummary(int user, String pkg);
void updateAutogroupSummary(int userId, String pkg, boolean needsOngoingFlag);
}
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 5d1c3e1..326a5f2 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2557,8 +2557,10 @@
}
@Override
- public void addAutoGroupSummary(int userId, String pkg, String triggeringKey) {
- NotificationRecord r = createAutoGroupSummary(userId, pkg, triggeringKey);
+ public void addAutoGroupSummary(int userId, String pkg, String triggeringKey,
+ boolean needsOngoingFlag) {
+ NotificationRecord r = createAutoGroupSummary(
+ userId, pkg, triggeringKey, needsOngoingFlag);
if (r != null) {
final boolean isAppForeground =
mActivityManager.getPackageImportance(pkg) == IMPORTANCE_FOREGROUND;
@@ -5739,6 +5741,7 @@
void removeAutogroupKeyLocked(String key) {
NotificationRecord r = mNotificationsByKey.get(key);
if (r == null) {
+ Slog.w(TAG, "Failed to remove autogroup " + key);
return;
}
if (r.getSbn().getOverrideGroupKey() != null) {
@@ -5778,7 +5781,8 @@
}
// Creates a 'fake' summary for a package that has exceeded the solo-notification limit.
- NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey) {
+ NotificationRecord createAutoGroupSummary(int userId, String pkg, String triggeringKey,
+ boolean needsOngoingFlag) {
NotificationRecord summaryRecord = null;
boolean isPermissionFixed = mPermissionHelper.isMigrationEnabled()
? mPermissionHelper.isPermissionFixed(pkg, userId) : false;
@@ -5818,6 +5822,7 @@
.setGroup(GroupHelper.AUTOGROUP_KEY)
.setFlag(FLAG_AUTOGROUP_SUMMARY, true)
.setFlag(Notification.FLAG_GROUP_SUMMARY, true)
+ .setFlag(FLAG_ONGOING_EVENT, needsOngoingFlag)
.setColor(adjustedSbn.getNotification().color)
.setLocalOnly(true)
.build();
@@ -7356,17 +7361,16 @@
mListeners.notifyPostedLocked(r, old);
if ((oldSbn == null || !Objects.equals(oldSbn.getGroup(), n.getGroup()))
&& !isCritical(r)) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
+ mHandler.post(() -> {
+ synchronized (mNotificationLock) {
mGroupHelper.onNotificationPosted(
n, hasAutoGroupSummaryLocked(n));
}
});
} else if (oldSbn != null) {
final NotificationRecord finalRecord = r;
- mHandler.post(() -> mGroupHelper.onNotificationUpdated(
- finalRecord.getSbn(), hasAutoGroupSummaryLocked(n)));
+ mHandler.post(() ->
+ mGroupHelper.onNotificationUpdated(finalRecord.getSbn()));
}
} else {
Slog.e(TAG, "Not posting notification without small icon: " + notification);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 1b0b492..b20781f 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -3962,8 +3962,8 @@
try {
mClient.resized(mClientWindowFrames, reportDraw, mLastReportedConfiguration,
- forceRelayout, alwaysConsumeSystemBars, displayId, Integer.MAX_VALUE,
- resizeMode);
+ forceRelayout, alwaysConsumeSystemBars, displayId, mSyncSeqId, resizeMode);
+
if (drawPending && reportOrientation && mOrientationChanging) {
mOrientationChangeRedrawRequestTime = SystemClock.elapsedRealtime();
ProtoLog.v(WM_DEBUG_ORIENTATION,
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 55e504a..486b6a1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -7233,7 +7233,7 @@
return;
}
Preconditions.checkCallAuthorization(
- hasCallingOrSelfPermission(permission.SEND_LOST_MODE_LOCATION_UPDATES));
+ hasCallingOrSelfPermission(permission.TRIGGER_LOST_MODE));
synchronized (getLockObject()) {
final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
diff --git a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
index e096687..4a51e41 100644
--- a/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java
@@ -217,6 +217,8 @@
@Mock
private AppOpsManager mAppOpsManager;
@Mock
+ private BatteryManager mBatteryManager;
+ @Mock
private DeviceIdleInternal mDeviceIdleInternal;
@Mock
private PermissionManagerServiceInternal mPermissionManagerInternal;
@@ -453,6 +455,7 @@
eq(Manifest.permission.USE_EXACT_ALARM), anyInt(), anyInt(), anyString()));
when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager);
+ when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager);
registerAppIds(new String[]{TEST_CALLING_PACKAGE},
new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)});
@@ -477,6 +480,8 @@
// Other boot phases don't matter
mService.onBootPhase(SystemService.PHASE_SYSTEM_SERVICES_READY);
+
+ verify(mBatteryManager).isCharging();
setTareEnabled(false);
mAppStandbyWindow = mService.mConstants.APP_STANDBY_WINDOW;
mAllowWhileIdleWindow = mService.mConstants.ALLOW_WHILE_IDLE_WINDOW;
@@ -1101,6 +1106,7 @@
new Intent(parole ? BatteryManager.ACTION_CHARGING
: BatteryManager.ACTION_DISCHARGING));
assertAndHandleMessageSync(CHARGING_STATUS_CHANGED);
+ assertEquals(parole, mService.mAppStandbyParole);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index 484dc84..197c21f 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -8455,7 +8455,7 @@
@Test
public void testSendLostModeLocationUpdate_notOrganizationOwnedDevice() {
- mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+ mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
assertThrows(IllegalStateException.class, () -> dpm.sendLostModeLocationUpdate(
getServices().executor, /* empty callback */ result -> {}));
}
@@ -8463,7 +8463,7 @@
@Test
public void testSendLostModeLocationUpdate_asDeviceOwner() throws Exception {
final String TEST_PROVIDER = "network";
- mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+ mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
setDeviceOwner();
when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
when(getServices().locationManager.isProviderEnabled(TEST_PROVIDER)).thenReturn(true);
@@ -8480,7 +8480,7 @@
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
mContext.binder.callingUid = MANAGED_PROFILE_ADMIN_UID;
- mContext.callerPermissions.add(permission.SEND_LOST_MODE_LOCATION_UPDATES);
+ mContext.callerPermissions.add(permission.TRIGGER_LOST_MODE);
addManagedProfile(admin1, MANAGED_PROFILE_ADMIN_UID, admin1);
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
when(getServices().locationManager.getAllProviders()).thenReturn(List.of(TEST_PROVIDER));
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
index 5458a5b..ff6c976 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/GroupHelperTest.java
@@ -20,6 +20,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
@@ -53,7 +54,7 @@
public class GroupHelperTest extends UiServiceTestCase {
private @Mock GroupHelper.Callback mCallback;
- private final static int AUTOGROUP_AT_COUNT = 4;
+ private final static int AUTOGROUP_AT_COUNT = 7;
private GroupHelper mGroupHelper;
@Before
@@ -88,7 +89,7 @@
false);
}
verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
verify(mCallback, never()).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -105,7 +106,7 @@
mGroupHelper.onNotificationPosted(
getSbn(pkg2, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM), false);
verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
verify(mCallback, never()).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -120,7 +121,8 @@
}
mGroupHelper.onNotificationPosted(
getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.ALL), false);
- verify(mCallback, never()).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, never()).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), anyBoolean());
verify(mCallback, never()).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -136,13 +138,12 @@
mGroupHelper.onNotificationPosted(
getSbn(pkg, AUTOGROUP_AT_COUNT, "four", UserHandle.SYSTEM, "a"), false);
verify(mCallback, never()).addAutoGroupSummary(
- eq(UserHandle.USER_SYSTEM), eq(pkg), anyString());
+ eq(UserHandle.USER_SYSTEM), eq(pkg), anyString(), anyBoolean());
verify(mCallback, never()).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
}
-
@Test
public void testPostingOverLimit() throws Exception {
final String pkg = "package";
@@ -150,7 +151,23 @@
mGroupHelper.onNotificationPosted(
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM), false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
+ verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroup(anyString());
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
+
+ @Test
+ public void testPostingOverLimit_addsOngoingFlag() throws Exception {
+ final String pkg = "package";
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
+ StatusBarNotification sbn = getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM);
+ if (i == 0) {
+ sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
+ }
+ mGroupHelper.onNotificationPosted(sbn, false);
+ }
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(true));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -178,7 +195,7 @@
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT + 1);
+ userId, pkg), AUTOGROUP_AT_COUNT + 1);
}
@Test
@@ -199,15 +216,14 @@
}
notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
-
- mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+ mGroupHelper.onNotificationUpdated(notifications.get(0));
verify(mCallback, times(AUTOGROUP_AT_COUNT + 2))
.updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT);
+ userId, pkg), AUTOGROUP_AT_COUNT);
}
@Test
@@ -229,18 +245,18 @@
notifications.get(0).getNotification().flags &= ~Notification.FLAG_ONGOING_EVENT;
- mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+ mGroupHelper.onNotificationUpdated(notifications.get(0));
notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+ mGroupHelper.onNotificationUpdated(notifications.get(0));
verify(mCallback, times(AUTOGROUP_AT_COUNT + 3))
.updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT + 1);
+ userId, pkg), AUTOGROUP_AT_COUNT + 1);
}
@Test
@@ -267,7 +283,7 @@
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), AUTOGROUP_AT_COUNT);
+ userId, pkg), AUTOGROUP_AT_COUNT);
}
@@ -288,14 +304,14 @@
}
notifications.get(0).getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
- mGroupHelper.onNotificationUpdated(notifications.get(0), true);
+ mGroupHelper.onNotificationUpdated(notifications.get(0));
verify(mCallback, times(1))
.updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), 1);
+ userId, pkg), 1);
}
@Test
@@ -305,7 +321,7 @@
for (int i = 0; i < AUTOGROUP_AT_COUNT + 1; i++) {
notifications.add(getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM));
}
- StatusBarNotification sbn = notifications.get(0);
+ StatusBarNotification sbn = notifications.get(AUTOGROUP_AT_COUNT);
sbn.getNotification().flags |= Notification.FLAG_ONGOING_EVENT;
sbn.setOverrideGroupKey(AUTOGROUP_KEY);
@@ -319,7 +335,7 @@
int userId = UserHandle.SYSTEM.getIdentifier();
assertEquals(mGroupHelper.getOngoingGroupCount(
- userId, pkg, AUTOGROUP_KEY), 1);
+ userId, pkg), 1);
}
@Test
@@ -342,7 +358,7 @@
.updateAutogroupSummary(anyInt(), anyString(), eq(true));
int userId = UserHandle.SYSTEM.getIdentifier();
- assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg, AUTOGROUP_KEY), 0);
+ assertEquals(mGroupHelper.getOngoingGroupCount(userId, pkg), 0);
}
@@ -355,7 +371,7 @@
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -382,28 +398,22 @@
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(1)).addAutoGroupSummary(
+ anyInt(), eq(pkg), anyString(), anyBoolean());
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
Mockito.reset(mCallback);
- int i = 0;
- for (i = 0; i < AUTOGROUP_AT_COUNT - 2; i++) {
+ for (int i = 0; i < AUTOGROUP_AT_COUNT; i++) {
final StatusBarNotification sbn =
getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
mGroupHelper.onNotificationPosted(sbn, false);
+ verify(mCallback, times(1)).removeAutoGroup(sbn.getKey());
+ if (i < AUTOGROUP_AT_COUNT -1) {
+ verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
+ }
}
- verify(mCallback, times(AUTOGROUP_AT_COUNT - 2)).removeAutoGroup(anyString());
- verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
- Mockito.reset(mCallback);
-
- for (; i < AUTOGROUP_AT_COUNT; i++) {
- final StatusBarNotification sbn =
- getSbn(pkg, i, String.valueOf(i), UserHandle.SYSTEM, "app group");
- mGroupHelper.onNotificationPosted(sbn, false);
- }
- verify(mCallback, times(2)).removeAutoGroup(anyString());
verify(mCallback, times(1)).removeAutoGroupSummary(anyInt(), anyString());
}
@@ -417,7 +427,7 @@
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, false);
}
- verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString());
+ verify(mCallback, times(1)).addAutoGroupSummary(anyInt(), eq(pkg), anyString(), eq(false));
verify(mCallback, times(AUTOGROUP_AT_COUNT)).addAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
@@ -441,7 +451,7 @@
final StatusBarNotification sbn = getSbn(pkg, 5, String.valueOf(5), UserHandle.SYSTEM);
posted.add(sbn);
mGroupHelper.onNotificationPosted(sbn, true);
- verify(mCallback, times(posted.size())).addAutoGroup(anyString());
+ verify(mCallback, times(1)).addAutoGroup(sbn.getKey());
verify(mCallback, never()).removeAutoGroup(anyString());
verify(mCallback, never()).removeAutoGroupSummary(anyInt(), anyString());
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
index 2ba587d..0f6d5a5 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationPermissionMigrationTest.java
@@ -722,7 +722,7 @@
when(mPermissionHelper.isPermissionFixed(PKG, temp.getUserId())).thenReturn(true);
NotificationRecord r = mService.createAutoGroupSummary(
- temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey());
+ temp.getUserId(), temp.getSbn().getPackageName(), temp.getKey(), false);
assertThat(r.isImportanceFixed()).isTrue();
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
index 66da2a6..716612c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityTaskSupervisorTests.java
@@ -284,7 +284,7 @@
.setCreateActivity(true).build().getTopMostActivity();
activity2.getTask().setResumedActivity(activity2, "test");
- mAtm.mAmInternal.deletePendingTopUid(activity1.getUid());
+ mAtm.mAmInternal.deletePendingTopUid(activity1.getUid(), Long.MAX_VALUE);
clearInvocations(mAtm);
activity1.moveFocusableActivityToTop("test");
assertTrue(mAtm.mAmInternal.isPendingTopUid(activity1.getUid()));
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index 0fa79df..04c52f7 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -84,7 +84,7 @@
private final Object mLock = new Object();
private boolean mIsOpen;
- private final UsbMidiPacketConverter mUsbMidiPacketConverter = new UsbMidiPacketConverter();
+ private UsbMidiPacketConverter mUsbMidiPacketConverter;
private final MidiDeviceServer.Callback mCallback = new MidiDeviceServer.Callback() {
@@ -264,6 +264,11 @@
Log.d(TAG, "openLocked()");
UsbManager manager = mContext.getSystemService(UsbManager.class);
+ // Converting from raw MIDI to USB MIDI is not thread-safe.
+ // UsbMidiPacketConverter creates a converter from raw MIDI
+ // to USB MIDI for each USB output.
+ mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs);
+
mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
@@ -415,7 +420,7 @@
} else {
convertedArray =
mUsbMidiPacketConverter.rawMidiToUsbMidi(
- event.data, event.count);
+ event.data, event.count, portFinal);
}
if (DEBUG) {
@@ -518,6 +523,8 @@
mInputUsbEndpoints = null;
mOutputUsbEndpoints = null;
+ mUsbMidiPacketConverter = null;
+
mIsOpen = false;
}
diff --git a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
index 7c93c76..56bb236 100644
--- a/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
+++ b/services/usb/java/com/android/server/usb/UsbMidiPacketConverter.java
@@ -74,8 +74,15 @@
private static final byte SYSEX_START_EXCLUSIVE = (byte) 0xF0;
private static final byte SYSEX_END_EXCLUSIVE = (byte) 0xF7;
- private UsbMidiEncoder mUsbMidiEncoder = new UsbMidiEncoder();
private UsbMidiDecoder mUsbMidiDecoder = new UsbMidiDecoder();
+ private UsbMidiEncoder[] mUsbMidiEncoders;
+
+ public UsbMidiPacketConverter(int numEncoders) {
+ mUsbMidiEncoders = new UsbMidiEncoder[numEncoders];
+ for (int i = 0; i < numEncoders; i++) {
+ mUsbMidiEncoders[i] = new UsbMidiEncoder();
+ }
+ }
/**
* Converts a USB MIDI array into a raw MIDI array.
@@ -93,10 +100,11 @@
*
* @param midiBytes the raw MIDI bytes to convert
* @param size the size of usbMidiBytes
+ * @param encoderId which encoder to use
* @return byte array of USB MIDI packets
*/
- public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size) {
- return mUsbMidiEncoder.encode(midiBytes, size);
+ public byte[] rawMidiToUsbMidi(byte[] midiBytes, int size, int encoderId) {
+ return mUsbMidiEncoders[encoderId].encode(midiBytes, size);
}
private class UsbMidiDecoder {
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index d527a23..c86f38d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -679,6 +679,7 @@
private void restartProcessLocked() {
Slog.v(TAG, "Restarting hotword detection process");
ServiceConnection oldConnection = mRemoteHotwordDetectionService;
+ HotwordDetectionServiceIdentity previousIdentity = mIdentity;
// TODO(volnov): this can be done after connect() has been successful.
if (mValidatingDspTrigger) {
@@ -722,6 +723,9 @@
}
oldConnection.ignoreConnectionStatusEvents();
oldConnection.unbind();
+ if (previousIdentity != null) {
+ removeServiceUidForAudioPolicy(previousIdentity.getIsolatedUid());
+ }
}
static final class SoundTriggerCallback extends IRecognitionStatusCallback.Stub {
diff --git a/telephony/java/android/telephony/ModemActivityInfo.java b/telephony/java/android/telephony/ModemActivityInfo.java
index 730a9d1..2d0135a 100644
--- a/telephony/java/android/telephony/ModemActivityInfo.java
+++ b/telephony/java/android/telephony/ModemActivityInfo.java
@@ -383,8 +383,6 @@
* activity.
*/
public @NonNull ModemActivityInfo getDelta(@NonNull ModemActivityInfo other) {
- int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
-
ActivityStatsTechSpecificInfo[] mDeltaSpecificInfo;
mDeltaSpecificInfo = new ActivityStatsTechSpecificInfo[other.getSpecificInfoLength()];
@@ -399,6 +397,7 @@
if (other.mActivityStatsTechSpecificInfo[i].getFrequencyRange()
== mActivityStatsTechSpecificInfo[j].getFrequencyRange()) {
int freq = mActivityStatsTechSpecificInfo[j].getFrequencyRange();
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
txTimeMs[lvl] =
(int) (other.getTransmitDurationMillisAtPowerLevel(
@@ -416,6 +415,7 @@
- getReceiveTimeMillis(rat, freq)));
}
} else {
+ int[] txTimeMs = new int[ModemActivityInfo.TX_POWER_LEVELS];
for (int lvl = 0; lvl < ModemActivityInfo.TX_POWER_LEVELS; lvl++) {
txTimeMs[lvl] =
(int) (other.getTransmitDurationMillisAtPowerLevel(lvl, rat)