Merge "Request embedded focus if a SurfacePackage was added to SV" into udc-dev
diff --git a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
index 64b2423..fd8ddbc 100644
--- a/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/job/JobSchedulerInternal.java
@@ -69,8 +69,8 @@
* @return {@code true} if the given notification channel is associated with any user-initiated
* jobs.
*/
- boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
- int userId, String packageName);
+ boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+ @NonNull String notificationChannel, int userId, @NonNull String packageName);
/**
* Report a snapshot of sync-related jobs back to the sync manager
diff --git a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index 0650ce3..f779b4d 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -355,7 +355,7 @@
@GuardedBy("this")
private boolean mHasGps;
@GuardedBy("this")
- private boolean mHasNetworkLocation;
+ private boolean mHasFusedLocation;
@GuardedBy("this")
private Location mLastGenericLocation;
@GuardedBy("this")
@@ -3782,12 +3782,14 @@
scheduleAlarmLocked(mConstants.LOCATING_TIMEOUT);
LocationManager locationManager = mInjector.getLocationManager();
if (locationManager != null
- && locationManager.getProvider(LocationManager.NETWORK_PROVIDER) != null) {
- locationManager.requestLocationUpdates(mLocationRequest,
- mGenericLocationListener, mHandler.getLooper());
+ && locationManager.getProvider(LocationManager.FUSED_PROVIDER) != null) {
+ locationManager.requestLocationUpdates(LocationManager.FUSED_PROVIDER,
+ mLocationRequest,
+ AppSchedulingModuleThread.getExecutor(),
+ mGenericLocationListener);
mLocating = true;
} else {
- mHasNetworkLocation = false;
+ mHasFusedLocation = false;
}
if (locationManager != null
&& locationManager.getProvider(LocationManager.GPS_PROVIDER) != null) {
@@ -5301,9 +5303,10 @@
pw.print(" "); pw.print(mStationaryListeners.size());
pw.println(" stationary listeners registered");
}
- pw.print(" mLocating="); pw.print(mLocating); pw.print(" mHasGps=");
- pw.print(mHasGps); pw.print(" mHasNetwork=");
- pw.print(mHasNetworkLocation); pw.print(" mLocated="); pw.println(mLocated);
+ pw.print(" mLocating="); pw.print(mLocating);
+ pw.print(" mHasGps="); pw.print(mHasGps);
+ pw.print(" mHasFused="); pw.print(mHasFusedLocation);
+ pw.print(" mLocated="); pw.println(mLocated);
if (mLastGenericLocation != null) {
pw.print(" mLastGenericLocation="); pw.println(mLastGenericLocation);
}
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 8bd3d127..b9b825c 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -1925,16 +1925,14 @@
return null;
}
- @GuardedBy("mLock")
boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId, int userId,
- String packageName) {
+ @NonNull String packageName) {
return mNotificationCoordinator.isNotificationAssociatedWithAnyUserInitiatedJobs(
notificationId, userId, packageName);
}
- @GuardedBy("mLock")
- boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
- int userId, String packageName) {
+ boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+ @NonNull String notificationChannel, int userId, @NonNull String packageName) {
return mNotificationCoordinator.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
notificationChannel, userId, packageName);
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
index f6e00ec..d94674b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobNotificationCoordinator.java
@@ -27,9 +27,12 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.IntArray;
import android.util.Slog;
+import android.util.SparseArrayMap;
import android.util.SparseSetArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.server.LocalServices;
import com.android.server.job.controllers.JobStatus;
import com.android.server.notification.NotificationManagerInternal;
@@ -38,6 +41,14 @@
private static final String TAG = "JobNotificationCoordinator";
/**
+ * Local lock for independent objects like mUijNotifications and mUijNotificationChannels which
+ * don't depend on other JS objects such as JobServiceContext which require the global JS lock.
+ *
+ * Note: do <b>NOT</b> acquire the global lock while this one is held.
+ */
+ private final Object mUijLock = new Object();
+
+ /**
* Mapping of UserPackage -> {notificationId -> List<JobServiceContext>} to track which jobs
* are associated with each app's notifications.
*/
@@ -49,6 +60,27 @@
private final ArrayMap<JobServiceContext, NotificationDetails> mNotificationDetails =
new ArrayMap<>();
+ /**
+ * Mapping of userId -> {packageName, notificationIds} tracking which notifications
+ * associated with each app belong to user-initiated jobs.
+ *
+ * Note: this map can be accessed without holding the main JS lock, so that other services like
+ * NotificationManagerService can call into JS and verify associations.
+ */
+ @GuardedBy("mUijLock")
+ private final SparseArrayMap<String, IntArray> mUijNotifications = new SparseArrayMap<>();
+
+ /**
+ * Mapping of userId -> {packageName, notificationChannels} tracking which notification channels
+ * associated with each app are hosting a user-initiated job notification.
+ *
+ * Note: this map can be accessed without holding the main JS lock, so that other services like
+ * NotificationManagerService can call into JS and verify associations.
+ */
+ @GuardedBy("mUijLock")
+ private final SparseArrayMap<String, ArraySet<String>> mUijNotificationChannels =
+ new SparseArrayMap<>();
+
private static final class NotificationDetails {
@NonNull
public final UserPackage userPackage;
@@ -81,15 +113,24 @@
int callingPid, int callingUid, int notificationId, @NonNull Notification notification,
@JobService.JobEndNotificationPolicy int jobEndNotificationPolicy) {
validateNotification(packageName, callingUid, notification, jobEndNotificationPolicy);
+ final JobStatus jobStatus = hostingContext.getRunningJobLocked();
final NotificationDetails oldDetails = mNotificationDetails.get(hostingContext);
if (oldDetails != null && oldDetails.notificationId != notificationId) {
// App is switching notification IDs. Remove association with the old one.
- removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED);
+ removeNotificationAssociation(hostingContext, JobParameters.STOP_REASON_UNDEFINED,
+ jobStatus);
}
final int userId = UserHandle.getUserId(callingUid);
- final JobStatus jobStatus = hostingContext.getRunningJobLocked();
if (jobStatus != null && jobStatus.startedAsUserInitiatedJob) {
notification.flags |= Notification.FLAG_USER_INITIATED_JOB;
+ synchronized (mUijLock) {
+ maybeCreateUijNotificationSetsLocked(userId, packageName);
+ final IntArray notificationIds = mUijNotifications.get(userId, packageName);
+ if (notificationIds.indexOf(notificationId) == -1) {
+ notificationIds.add(notificationId);
+ }
+ mUijNotificationChannels.get(userId, packageName).add(notification.getChannelId());
+ }
}
final UserPackage userPackage = UserPackage.of(userId, packageName);
final NotificationDetails details = new NotificationDetails(
@@ -110,7 +151,7 @@
}
void removeNotificationAssociation(@NonNull JobServiceContext hostingContext,
- @JobParameters.StopReason int stopReason) {
+ @JobParameters.StopReason int stopReason, JobStatus completedJob) {
final NotificationDetails details = mNotificationDetails.remove(hostingContext);
if (details == null) {
return;
@@ -121,10 +162,11 @@
Slog.wtf(TAG, "Association data structures not in sync");
return;
}
- final String packageName = details.userPackage.packageName;
final int userId = UserHandle.getUserId(details.appUid);
+ final String packageName = details.userPackage.packageName;
+ final int notificationId = details.notificationId;
boolean stripUijFlag = true;
- ArraySet<JobServiceContext> associatedContexts = associations.get(details.notificationId);
+ ArraySet<JobServiceContext> associatedContexts = associations.get(notificationId);
if (associatedContexts == null || associatedContexts.isEmpty()) {
// No more jobs using this notification. Apply the final job stop policy.
// If the user attempted to stop the job/app, then always remove the notification
@@ -133,23 +175,50 @@
|| stopReason == JobParameters.STOP_REASON_USER) {
mNotificationManagerInternal.cancelNotification(
packageName, packageName, details.appUid, details.appPid, /* tag */ null,
- details.notificationId, userId);
+ notificationId, userId);
stripUijFlag = false;
}
} else {
// Strip the UIJ flag only if there are no other UIJs associated with the notification
- stripUijFlag = !isNotificationAssociatedWithAnyUserInitiatedJobs(
- details.notificationId, userId, packageName);
+ stripUijFlag = !isNotificationUsedForAnyUij(userId, packageName, notificationId);
}
if (stripUijFlag) {
- // Strip the user-initiated job flag from the notification.
mNotificationManagerInternal.removeUserInitiatedJobFlagFromNotification(
- packageName, details.notificationId, userId);
+ packageName, notificationId, userId);
+ }
+
+ // Clean up UIJ related objects if the just completed job was a UIJ
+ if (completedJob != null && completedJob.startedAsUserInitiatedJob) {
+ maybeDeleteNotificationIdAssociation(userId, packageName, notificationId);
+ maybeDeleteNotificationChannelAssociation(
+ userId, packageName, details.notificationChannel);
}
}
boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId,
- int userId, String packageName) {
+ int userId, @NonNull String packageName) {
+ synchronized (mUijLock) {
+ final IntArray notifications = mUijNotifications.get(userId, packageName);
+ if (notifications != null) {
+ return notifications.indexOf(notificationId) != -1;
+ }
+ return false;
+ }
+ }
+
+ boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+ @NonNull String notificationChannel, int userId, @NonNull String packageName) {
+ synchronized (mUijLock) {
+ final ArraySet<String> channels = mUijNotificationChannels.get(userId, packageName);
+ if (channels != null) {
+ return channels.contains(notificationChannel);
+ }
+ return false;
+ }
+ }
+
+ private boolean isNotificationUsedForAnyUij(int userId, String packageName,
+ int notificationId) {
final UserPackage pkgDetails = UserPackage.of(userId, packageName);
final SparseSetArray<JobServiceContext> associations = mCurrentAssociations.get(pkgDetails);
if (associations == null) {
@@ -170,8 +239,26 @@
return false;
}
- boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(String notificationChannel,
- int userId, String packageName) {
+ private void maybeDeleteNotificationIdAssociation(int userId, String packageName,
+ int notificationId) {
+ if (isNotificationUsedForAnyUij(userId, packageName, notificationId)) {
+ return;
+ }
+
+ // Safe to delete - no UIJs for this package are using this notification id
+ synchronized (mUijLock) {
+ final IntArray notifications = mUijNotifications.get(userId, packageName);
+ if (notifications != null) {
+ notifications.remove(notifications.indexOf(notificationId));
+ if (notifications.size() == 0) {
+ mUijNotifications.delete(userId, packageName);
+ }
+ }
+ }
+ }
+
+ private void maybeDeleteNotificationChannelAssociation(int userId, String packageName,
+ String notificationChannel) {
for (int i = mNotificationDetails.size() - 1; i >= 0; i--) {
final JobServiceContext jsc = mNotificationDetails.keyAt(i);
final NotificationDetails details = mNotificationDetails.get(jsc);
@@ -183,11 +270,31 @@
&& details.notificationChannel.equals(notificationChannel)) {
final JobStatus jobStatus = jsc.getRunningJobLocked();
if (jobStatus != null && jobStatus.startedAsUserInitiatedJob) {
- return true;
+ return;
}
}
}
- return false;
+
+ // Safe to delete - no UIJs for this package are using this notification channel
+ synchronized (mUijLock) {
+ ArraySet<String> channels = mUijNotificationChannels.get(userId, packageName);
+ if (channels != null) {
+ channels.remove(notificationChannel);
+ if (channels.isEmpty()) {
+ mUijNotificationChannels.delete(userId, packageName);
+ }
+ }
+ }
+ }
+
+ @GuardedBy("mUijLock")
+ private void maybeCreateUijNotificationSetsLocked(int userId, String packageName) {
+ if (mUijNotifications.get(userId, packageName) == null) {
+ mUijNotifications.add(userId, packageName, new IntArray());
+ }
+ if (mUijNotificationChannels.get(userId, packageName) == null) {
+ mUijNotificationChannels.add(userId, packageName, new ArraySet<>());
+ }
}
private void validateNotification(@NonNull String packageName, int callingUid,
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
index 3aec8ba..6eeff82 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -3713,26 +3713,22 @@
@Override
public boolean isNotificationAssociatedWithAnyUserInitiatedJobs(int notificationId,
- int userId, String packageName) {
+ int userId, @NonNull String packageName) {
if (packageName == null) {
return false;
}
- synchronized (mLock) {
- return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs(
- notificationId, userId, packageName);
- }
+ return mConcurrencyManager.isNotificationAssociatedWithAnyUserInitiatedJobs(
+ notificationId, userId, packageName);
}
@Override
public boolean isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
- String notificationChannel, int userId, String packageName) {
+ @NonNull String notificationChannel, int userId, @NonNull String packageName) {
if (packageName == null || notificationChannel == null) {
return false;
}
- synchronized (mLock) {
- return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
- notificationChannel, userId, packageName);
- }
+ return mConcurrencyManager.isNotificationChannelAssociatedWithAnyUserInitiatedJobs(
+ notificationChannel, userId, packageName);
}
@Override
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 44700c8..fb36cde 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -1464,7 +1464,8 @@
JobSchedulerEconomicPolicy.ACTION_JOB_TIMEOUT,
String.valueOf(mRunningJob.getJobId()));
}
- mNotificationCoordinator.removeNotificationAssociation(this, reschedulingStopReason);
+ mNotificationCoordinator.removeNotificationAssociation(this,
+ reschedulingStopReason, completedJob);
if (mWakeLock != null) {
mWakeLock.release();
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
index eb43c38..ef634b5 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/restrictions/ThermalStatusRestriction.java
@@ -92,13 +92,17 @@
final int priority = job.getEffectivePriority();
if (mThermalStatus >= HIGHER_PRIORITY_THRESHOLD) {
// For moderate throttling:
- // Only let expedited & user-initiated jobs run if:
+ // Let all user-initiated jobs run.
+ // Only let expedited jobs run if:
// 1. They haven't previously run
// 2. They're already running and aren't yet in overtime
// Only let high priority jobs run if:
// They are already running and aren't yet in overtime
// Don't let any other job run.
- if (job.shouldTreatAsExpeditedJob() || job.shouldTreatAsUserInitiatedJob()) {
+ if (job.shouldTreatAsUserInitiatedJob()) {
+ return false;
+ }
+ if (job.shouldTreatAsExpeditedJob()) {
return job.getNumPreviousAttempts() > 0
|| (mService.isCurrentlyRunningLocked(job)
&& mService.isJobInOvertimeLocked(job));
diff --git a/core/java/android/hardware/display/DisplayManager.java b/core/java/android/hardware/display/DisplayManager.java
index 2aead3c..72a3f6c 100644
--- a/core/java/android/hardware/display/DisplayManager.java
+++ b/core/java/android/hardware/display/DisplayManager.java
@@ -51,9 +51,11 @@
import android.view.Surface;
import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
@@ -81,8 +83,10 @@
private final DisplayManagerGlobal mGlobal;
private final Object mLock = new Object();
- private final SparseArray<Display> mDisplays = new SparseArray<Display>();
+ @GuardedBy("mLock")
+ private final WeakDisplayCache mDisplayCache = new WeakDisplayCache();
+ @GuardedBy("mLock")
private final ArrayList<Display> mTempDisplays = new ArrayList<Display>();
/**
@@ -684,6 +688,7 @@
}
}
+ @GuardedBy("mLock")
private void addAllDisplaysLocked(ArrayList<Display> displays, int[] displayIds) {
for (int i = 0; i < displayIds.length; i++) {
Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/);
@@ -693,6 +698,7 @@
}
}
+ @GuardedBy("mLock")
private void addDisplaysLocked(
ArrayList<Display> displays, int[] displayIds, int matchType, int flagMask) {
for (int displayId : displayIds) {
@@ -709,8 +715,9 @@
}
}
+ @GuardedBy("mLock")
private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) {
- Display display = mDisplays.get(displayId);
+ Display display = mDisplayCache.get(displayId);
if (display == null) {
// TODO: We cannot currently provide any override configurations for metrics on displays
// other than the display the context is associated with.
@@ -719,7 +726,7 @@
display = mGlobal.getCompatibleDisplay(displayId, resources);
if (display != null) {
- mDisplays.put(displayId, display);
+ mDisplayCache.put(display);
}
} else if (!assumeValid && !display.isValid()) {
display = null;
@@ -1767,4 +1774,57 @@
*/
String KEY_BRIGHTNESS_THROTTLING_DATA = "brightness_throttling_data";
}
+
+ /**
+ * Helper class to maintain cache of weak references to Display instances.
+ *
+ * Note this class is not thread-safe, so external synchronization is needed if accessed
+ * concurrently.
+ */
+ private static final class WeakDisplayCache {
+ private final SparseArray<WeakReference<Display>> mDisplayCache = new SparseArray<>();
+
+ /**
+ * Return cached {@link Display} instance for the provided display id.
+ *
+ * @param displayId - display id of the requested {@link Display} instance.
+ * @return cached {@link Display} instance or null
+ */
+ Display get(int displayId) {
+ WeakReference<Display> wrDisplay = mDisplayCache.get(displayId);
+ if (wrDisplay == null) {
+ return null;
+ }
+ return wrDisplay.get();
+ }
+
+ /**
+ * Insert new {@link Display} instance in the cache. This replaced the previously cached
+ * {@link Display} instance, if there's already one with the same display id.
+ *
+ * @param display - Display instance to cache.
+ */
+ void put(Display display) {
+ removeStaleEntries();
+ mDisplayCache.put(display.getDisplayId(), new WeakReference<>(display));
+ }
+
+ /**
+ * Evict gc-ed entries from the cache.
+ */
+ private void removeStaleEntries() {
+ ArrayList<Integer> staleEntriesIndices = new ArrayList();
+ for (int i = 0; i < mDisplayCache.size(); i++) {
+ if (mDisplayCache.valueAt(i).get() == null) {
+ staleEntriesIndices.add(i);
+ }
+ }
+
+ for (int i = 0; i < staleEntriesIndices.size(); i++) {
+ // removeAt call to SparseArray doesn't compact the underlying array
+ // so the indices stay valid even after removal.
+ mDisplayCache.removeAt(staleEntriesIndices.get(i));
+ }
+ }
+ }
}
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index ecce2ff..218ecc8 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -238,6 +238,15 @@
*/
public static final String KEY_ONE_SHOT = "KEY_ONE_SHOT";
+ /**
+ * Intent key: Whether to use default strings when showing the dialog that prompts
+ * user for device credentials.
+ * False indicates using the custom strings provided by {@code DynamicSystem}.
+ * @hide
+ */
+ public static final String KEY_KEYGUARD_USE_DEFAULT_STRINGS =
+ "KEY_KEYGUARD_USE_DEFAULT_STRINGS";
+
private static class IncomingHandler extends Handler {
private final WeakReference<DynamicSystemClient> mWeakClient;
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 7cb959d..aa5a0d0 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -2585,7 +2585,7 @@
* <p>
* To start an activity with this intent, apps should set the wellbeing package explicitly in
* the intent together with this action. The wellbeing package is defined in
- * {@code com.android.internal.R.string.config_defaultWellbeingPackage}.
+ * {@code com.android.internal.R.string.config_systemWellbeing}.
* <p>
* Output: Nothing
*
diff --git a/core/java/android/service/dreams/DreamManagerInternal.java b/core/java/android/service/dreams/DreamManagerInternal.java
index dd5373f..82571db 100644
--- a/core/java/android/service/dreams/DreamManagerInternal.java
+++ b/core/java/android/service/dreams/DreamManagerInternal.java
@@ -80,10 +80,10 @@
*/
public interface DreamManagerStateListener {
/**
- * Called when keep dreaming when undocked has changed.
+ * Called when keep dreaming when plug has changed.
*
* @param keepDreaming True if the current dream should continue when undocking.
*/
- void onKeepDreamingWhenUndockedChanged(boolean keepDreaming);
+ void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming);
}
}
diff --git a/core/java/android/service/wallpaper/WallpaperService.java b/core/java/android/service/wallpaper/WallpaperService.java
index 77bbeb5..74ab709 100644
--- a/core/java/android/service/wallpaper/WallpaperService.java
+++ b/core/java/android/service/wallpaper/WallpaperService.java
@@ -112,6 +112,7 @@
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
/**
@@ -431,6 +432,7 @@
Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED,
reportDraw ? 1 : 0,
mergedConfiguration);
+ mIWallpaperEngine.mPendingResizeCount.incrementAndGet();
mCaller.sendMessage(msg);
}
@@ -510,6 +512,7 @@
public Engine(Supplier<Long> clockFunction, Handler handler) {
mClockFunction = clockFunction;
mHandler = handler;
+ mMergedConfiguration.setOverrideConfiguration(getResources().getConfiguration());
}
/**
@@ -1051,6 +1054,10 @@
out.print(prefix); out.print("mZoom="); out.println(mZoom);
out.print(prefix); out.print("mPreviewSurfacePosition=");
out.println(mPreviewSurfacePosition);
+ final int pendingCount = mIWallpaperEngine.mPendingResizeCount.get();
+ if (pendingCount != 0) {
+ out.print(prefix); out.print("mPendingResizeCount="); out.println(pendingCount);
+ }
synchronized (mLock) {
out.print(prefix); out.print("mPendingXOffset="); out.print(mPendingXOffset);
out.print(" mPendingXOffset="); out.println(mPendingXOffset);
@@ -1113,10 +1120,6 @@
}
}
- private void updateConfiguration(MergedConfiguration mergedConfiguration) {
- mMergedConfiguration.setTo(mergedConfiguration);
- }
-
void updateSurface(boolean forceRelayout, boolean forceReport, boolean redrawNeeded) {
if (mDestroyed) {
Log.w(TAG, "Ignoring updateSurface due to destroyed");
@@ -1165,7 +1168,7 @@
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
final Configuration config = mMergedConfiguration.getMergedConfiguration();
- final Rect maxBounds = config.windowConfiguration.getMaxBounds();
+ final Rect maxBounds = new Rect(config.windowConfiguration.getMaxBounds());
if (myWidth == ViewGroup.LayoutParams.MATCH_PARENT
&& myHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
mLayout.width = myWidth;
@@ -1221,6 +1224,17 @@
final int relayoutResult = mSession.relayout(mWindow, mLayout, mWidth, mHeight,
View.VISIBLE, 0, 0, 0, mWinFrames, mMergedConfiguration,
mSurfaceControl, mInsetsState, mTempControls, mSyncSeqIdBundle);
+ final Rect outMaxBounds = mMergedConfiguration.getMergedConfiguration()
+ .windowConfiguration.getMaxBounds();
+ if (!outMaxBounds.equals(maxBounds)) {
+ Log.i(TAG, "Retry updateSurface because bounds changed from relayout: "
+ + maxBounds + " -> " + outMaxBounds);
+ mSurfaceHolder.mSurfaceLock.unlock();
+ mDrawingAllowed = false;
+ mCaller.sendMessage(mCaller.obtainMessageI(MSG_WINDOW_RESIZED,
+ redrawNeeded ? 1 : 0));
+ return;
+ }
final int transformHint = SurfaceControl.rotationToBufferTransform(
(mDisplay.getInstallOrientation() + mDisplay.getRotation()) % 4);
@@ -2324,6 +2338,8 @@
final IBinder mWindowToken;
final int mWindowType;
final boolean mIsPreview;
+ final AtomicInteger mPendingResizeCount = new AtomicInteger();
+ boolean mReportDraw;
boolean mShownReported;
int mReqWidth;
int mReqHeight;
@@ -2579,11 +2595,7 @@
mEngine.doCommand(cmd);
} break;
case MSG_WINDOW_RESIZED: {
- final boolean reportDraw = message.arg1 != 0;
- mEngine.updateConfiguration(((MergedConfiguration) message.obj));
- mEngine.updateSurface(true, false, reportDraw);
- mEngine.doOffsetsChanged(true);
- mEngine.scaleAndCropScreenshot();
+ handleResized((MergedConfiguration) message.obj, message.arg1 != 0);
} break;
case MSG_WINDOW_MOVED: {
// Do nothing. What does it mean for a Wallpaper to move?
@@ -2631,6 +2643,40 @@
Log.w(TAG, "Unknown message type " + message.what);
}
}
+
+ /**
+ * In general this performs relayout for IWindow#resized. If there are several pending
+ * (in the message queue) MSG_WINDOW_RESIZED from server side, only the last one will be
+ * handled (ignore intermediate states). Note that this procedure cannot be skipped if the
+ * configuration is not changed because this is also used to dispatch insets changes.
+ */
+ private void handleResized(MergedConfiguration config, boolean reportDraw) {
+ // The config can be null when retrying for a changed config from relayout, otherwise
+ // it is from IWindow#resized which always sends non-null config.
+ final int pendingCount = config != null ? mPendingResizeCount.decrementAndGet() : -1;
+ if (reportDraw) {
+ mReportDraw = true;
+ }
+ if (pendingCount > 0) {
+ if (DEBUG) {
+ Log.d(TAG, "Skip outdated resize, bounds="
+ + config.getMergedConfiguration().windowConfiguration.getMaxBounds()
+ + " pendingCount=" + pendingCount);
+ }
+ return;
+ }
+ if (config != null) {
+ if (DEBUG) {
+ Log.d(TAG, "Update config from resized, bounds="
+ + config.getMergedConfiguration().windowConfiguration.getMaxBounds());
+ }
+ mEngine.mMergedConfiguration.setTo(config);
+ }
+ mEngine.updateSurface(true /* forceRelayout */, false /* forceReport */, mReportDraw);
+ mReportDraw = false;
+ mEngine.doOffsetsChanged(true);
+ mEngine.scaleAndCropScreenshot();
+ }
}
/**
diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java
index 5dd2d82..edc5993 100644
--- a/core/java/android/view/Choreographer.java
+++ b/core/java/android/view/Choreographer.java
@@ -195,7 +195,7 @@
private boolean mDebugPrintNextFrameTimeDelta;
private int mFPSDivisor = 1;
- private DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
+ private final DisplayEventReceiver.VsyncEventData mLastVsyncEventData =
new DisplayEventReceiver.VsyncEventData();
private final FrameData mFrameData = new FrameData();
@@ -860,7 +860,7 @@
mFrameScheduled = false;
mLastFrameTimeNanos = frameTimeNanos;
mLastFrameIntervalNanos = frameIntervalNanos;
- mLastVsyncEventData = vsyncEventData;
+ mLastVsyncEventData.copyFrom(vsyncEventData);
}
if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -1262,7 +1262,7 @@
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
- private VsyncEventData mLastVsyncEventData = new VsyncEventData();
+ private final VsyncEventData mLastVsyncEventData = new VsyncEventData();
FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle);
@@ -1302,7 +1302,7 @@
mTimestampNanos = timestampNanos;
mFrame = frame;
- mLastVsyncEventData = vsyncEventData;
+ mLastVsyncEventData.copyFrom(vsyncEventData);
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
diff --git a/core/java/android/view/DisplayEventReceiver.java b/core/java/android/view/DisplayEventReceiver.java
index 0307489..54db34e 100644
--- a/core/java/android/view/DisplayEventReceiver.java
+++ b/core/java/android/view/DisplayEventReceiver.java
@@ -81,7 +81,10 @@
// GC'd while the native peer of the receiver is using them.
private MessageQueue mMessageQueue;
+ private final VsyncEventData mVsyncEventData = new VsyncEventData();
+
private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
+ WeakReference<VsyncEventData> vsyncEventData,
MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle);
private static native long nativeGetDisplayEventReceiverFinalizer();
@FastNative
@@ -124,7 +127,9 @@
}
mMessageQueue = looper.getQueue();
- mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
+ mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this),
+ new WeakReference<VsyncEventData>(mVsyncEventData),
+ mMessageQueue,
vsyncSource, eventRegistration, layerHandle);
mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this,
mReceiverPtr);
@@ -147,9 +152,6 @@
* @hide
*/
public static final class VsyncEventData {
- static final FrameTimeline[] INVALID_FRAME_TIMELINES =
- {new FrameTimeline(FrameInfo.INVALID_VSYNC_ID, Long.MAX_VALUE, Long.MAX_VALUE)};
-
// The amount of frame timeline choices.
// Must be in sync with VsyncEventData::kFrameTimelinesLength in
// frameworks/native/libs/gui/include/gui/VsyncEventData.h. If they do not match, a runtime
@@ -157,22 +159,32 @@
static final int FRAME_TIMELINES_LENGTH = 7;
public static class FrameTimeline {
+ FrameTimeline() {}
+
+ // Called from native code.
+ @SuppressWarnings("unused")
FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) {
this.vsyncId = vsyncId;
this.expectedPresentationTime = expectedPresentationTime;
this.deadline = deadline;
}
+ void copyFrom(FrameTimeline other) {
+ vsyncId = other.vsyncId;
+ expectedPresentationTime = other.expectedPresentationTime;
+ deadline = other.deadline;
+ }
+
// The frame timeline vsync id, used to correlate a frame
// produced by HWUI with the timeline data stored in Surface Flinger.
- public final long vsyncId;
+ public long vsyncId = FrameInfo.INVALID_VSYNC_ID;
// The frame timestamp for when the frame is expected to be presented.
- public final long expectedPresentationTime;
+ public long expectedPresentationTime = Long.MAX_VALUE;
// The frame deadline timestamp in {@link System#nanoTime()} timebase that it is
// allotted for the frame to be completed.
- public final long deadline;
+ public long deadline = Long.MAX_VALUE;
}
/**
@@ -180,11 +192,18 @@
* {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily
* delayed by the app.
*/
- public final long frameInterval;
+ public long frameInterval = -1;
public final FrameTimeline[] frameTimelines;
- public final int preferredFrameTimelineIndex;
+ public int preferredFrameTimelineIndex = 0;
+
+ VsyncEventData() {
+ frameTimelines = new FrameTimeline[FRAME_TIMELINES_LENGTH];
+ for (int i = 0; i < frameTimelines.length; i++) {
+ frameTimelines[i] = new FrameTimeline();
+ }
+ }
// Called from native code.
@SuppressWarnings("unused")
@@ -195,10 +214,12 @@
this.frameInterval = frameInterval;
}
- VsyncEventData() {
- this.frameInterval = -1;
- this.frameTimelines = INVALID_FRAME_TIMELINES;
- this.preferredFrameTimelineIndex = 0;
+ void copyFrom(VsyncEventData other) {
+ preferredFrameTimelineIndex = other.preferredFrameTimelineIndex;
+ frameInterval = other.frameInterval;
+ for (int i = 0; i < frameTimelines.length; i++) {
+ frameTimelines[i].copyFrom(other.frameTimelines[i]);
+ }
}
public FrameTimeline preferredFrameTimeline() {
@@ -304,9 +325,8 @@
// Called from native code.
@SuppressWarnings("unused")
- private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame,
- VsyncEventData vsyncEventData) {
- onVsync(timestampNanos, physicalDisplayId, frame, vsyncEventData);
+ private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
+ onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData);
}
// Called from native code.
diff --git a/core/java/android/view/SurfaceControlViewHost.java b/core/java/android/view/SurfaceControlViewHost.java
index 16c1335..bd6224b 100644
--- a/core/java/android/view/SurfaceControlViewHost.java
+++ b/core/java/android/view/SurfaceControlViewHost.java
@@ -120,6 +120,8 @@
private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl();
+ private ViewRootImpl.ConfigChangedCallback mConfigChangedCallback;
+
/**
* Package encapsulating a Surface hierarchy which contains interactive view
* elements. It's expected to get this object from
@@ -307,7 +309,7 @@
mWm = wwm;
mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout());
mCloseGuard.openWithCallSite("release", callsite);
- addConfigCallback(c, d);
+ setConfigCallback(c, d);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
@@ -357,21 +359,23 @@
mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout());
mCloseGuard.openWithCallSite("release", callsite);
- addConfigCallback(context, display);
+ setConfigCallback(context, display);
WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot);
mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection();
}
- private void addConfigCallback(Context c, Display d) {
+ private void setConfigCallback(Context c, Display d) {
final IBinder token = c.getWindowContextToken();
- mViewRoot.addConfigCallback((conf) -> {
+ mConfigChangedCallback = conf -> {
if (token instanceof WindowTokenClient) {
final WindowTokenClient w = (WindowTokenClient) token;
w.onConfigurationChanged(conf, d.getDisplayId(), true);
}
- });
+ };
+
+ ViewRootImpl.addConfigCallback(mConfigChangedCallback);
}
/**
@@ -386,8 +390,7 @@
mCloseGuard.warnIfOpen();
}
// We aren't on the UI thread here so we need to pass false to doDie
- mViewRoot.die(false /* immediate */);
- WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
+ doRelease(false /* immediate */);
}
/**
@@ -496,7 +499,16 @@
*/
public void release() {
// ViewRoot will release mSurfaceControl for us.
- mViewRoot.die(true /* immediate */);
+ doRelease(true /* immediate */);
+ }
+
+ private void doRelease(boolean immediate) {
+ if (mConfigChangedCallback != null) {
+ ViewRootImpl.removeConfigCallback(mConfigChangedCallback);
+ mConfigChangedCallback = null;
+ }
+
+ mViewRoot.die(immediate);
WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot);
mReleased = true;
mCloseGuard.close();
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 055b5cb..86e7fb0 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -11263,13 +11263,19 @@
}
if (syncBuffer) {
- mBlastBufferQueue.syncNextTransaction(new Consumer<Transaction>() {
- @Override
- public void accept(Transaction transaction) {
- surfaceSyncGroup.addTransaction(transaction);
- surfaceSyncGroup.markSyncReady();
- }
+ boolean result = mBlastBufferQueue.syncNextTransaction(transaction -> {
+ surfaceSyncGroup.addTransaction(transaction);
+ surfaceSyncGroup.markSyncReady();
});
+ if (!result) {
+ // syncNextTransaction can only return false if something is already trying
+ // to sync the same frame in the same BBQ. That shouldn't be possible, but
+ // if it did happen, invoke markSyncReady so the active SSG doesn't get
+ // stuck.
+ Log.e(mTag, "Unable to syncNextTransaction. Possibly something else is"
+ + " trying to sync?");
+ surfaceSyncGroup.markSyncReady();
+ }
}
return didProduceBuffer -> {
@@ -11283,7 +11289,7 @@
// the next draw attempt. The next transaction and transaction complete callback
// were only set for the current draw attempt.
if (!didProduceBuffer) {
- mBlastBufferQueue.syncNextTransaction(null);
+ mBlastBufferQueue.clearSyncTransaction();
// Gather the transactions that were sent to mergeWithNextTransaction
// since the frame didn't draw on this vsync. It's possible the frame will
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 7d1dc76..84ef226 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -28,6 +28,7 @@
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Xml;
+import android.view.InflateException;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -137,16 +138,9 @@
try {
parser = context.getResources().getAnimation(id);
return createAnimationFromXml(context, parser);
- } catch (XmlPullParserException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
- } catch (IOException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
+ } catch (XmlPullParserException | IOException ex) {
+ throw new NotFoundException(
+ "Can't load animation resource ID #0x" + Integer.toHexString(id), ex);
} finally {
if (parser != null) parser.close();
}
@@ -159,8 +153,9 @@
}
@UnsupportedAppUsage
- private static Animation createAnimationFromXml(Context c, XmlPullParser parser,
- AnimationSet parent, AttributeSet attrs) throws XmlPullParserException, IOException {
+ private static Animation createAnimationFromXml(
+ Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs)
+ throws XmlPullParserException, IOException, InflateException {
Animation anim = null;
@@ -168,8 +163,8 @@
int type;
int depth = parser.getDepth();
- while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
- && type != XmlPullParser.END_DOCUMENT) {
+ while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+ && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
@@ -193,7 +188,7 @@
} else if (name.equals("extend")) {
anim = new ExtendAnimation(c, attrs);
} else {
- throw new RuntimeException("Unknown animation name: " + parser.getName());
+ throw new InflateException("Unknown animation name: " + parser.getName());
}
if (parent != null) {
@@ -220,29 +215,24 @@
try {
parser = context.getResources().getAnimation(id);
return createLayoutAnimationFromXml(context, parser);
- } catch (XmlPullParserException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
- } catch (IOException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
+ } catch (XmlPullParserException | IOException | InflateException ex) {
+ throw new NotFoundException(
+ "Can't load animation resource ID #0x" + Integer.toHexString(id), ex);
} finally {
if (parser != null) parser.close();
}
}
- private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
- XmlPullParser parser) throws XmlPullParserException, IOException {
+ private static LayoutAnimationController createLayoutAnimationFromXml(
+ Context c, XmlPullParser parser)
+ throws XmlPullParserException, IOException, InflateException {
return createLayoutAnimationFromXml(c, parser, Xml.asAttributeSet(parser));
}
- private static LayoutAnimationController createLayoutAnimationFromXml(Context c,
- XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException {
+ private static LayoutAnimationController createLayoutAnimationFromXml(
+ Context c, XmlPullParser parser, AttributeSet attrs)
+ throws XmlPullParserException, IOException, InflateException {
LayoutAnimationController controller = null;
@@ -263,7 +253,7 @@
} else if ("gridLayoutAnimation".equals(name)) {
controller = new GridLayoutAnimationController(c, attrs);
} else {
- throw new RuntimeException("Unknown layout animation name: " + name);
+ throw new InflateException("Unknown layout animation name: " + name);
}
}
@@ -342,16 +332,9 @@
try {
parser = context.getResources().getAnimation(id);
return createInterpolatorFromXml(context.getResources(), context.getTheme(), parser);
- } catch (XmlPullParserException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
- } catch (IOException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
+ } catch (XmlPullParserException | IOException | InflateException ex) {
+ throw new NotFoundException(
+ "Can't load animation resource ID #0x" + Integer.toHexString(id), ex);
} finally {
if (parser != null) parser.close();
}
@@ -367,30 +350,26 @@
* @throws NotFoundException
* @hide
*/
- public static Interpolator loadInterpolator(Resources res, Theme theme, int id) throws NotFoundException {
+ public static Interpolator loadInterpolator(Resources res, Theme theme, int id)
+ throws NotFoundException {
XmlResourceParser parser = null;
try {
parser = res.getAnimation(id);
return createInterpolatorFromXml(res, theme, parser);
- } catch (XmlPullParserException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
- } catch (IOException ex) {
- NotFoundException rnf = new NotFoundException("Can't load animation resource ID #0x" +
- Integer.toHexString(id));
- rnf.initCause(ex);
- throw rnf;
+ } catch (XmlPullParserException | IOException | InflateException ex) {
+ throw new NotFoundException(
+ "Can't load animation resource ID #0x" + Integer.toHexString(id), ex);
} finally {
- if (parser != null)
+ if (parser != null) {
parser.close();
+ }
}
}
- private static Interpolator createInterpolatorFromXml(Resources res, Theme theme, XmlPullParser parser)
- throws XmlPullParserException, IOException {
+ private static Interpolator createInterpolatorFromXml(
+ Resources res, Theme theme, XmlPullParser parser)
+ throws XmlPullParserException, IOException, InflateException {
BaseInterpolator interpolator = null;
@@ -430,7 +409,7 @@
} else if (name.equals("pathInterpolator")) {
interpolator = new PathInterpolator(res, theme, attrs);
} else {
- throw new RuntimeException("Unknown interpolator name: " + parser.getName());
+ throw new InflateException("Unknown interpolator name: " + parser.getName());
}
}
return interpolator;
diff --git a/core/java/android/window/SurfaceSyncGroup.java b/core/java/android/window/SurfaceSyncGroup.java
index 7f99fb7..b3d5124 100644
--- a/core/java/android/window/SurfaceSyncGroup.java
+++ b/core/java/android/window/SurfaceSyncGroup.java
@@ -120,6 +120,7 @@
private static HandlerThread sHandlerThread;
private Handler mHandler;
+ @GuardedBy("mLock")
private boolean mTimeoutAdded;
private static boolean isLocalBinder(IBinder binder) {
@@ -234,6 +235,9 @@
* SurfaceSyncGroup have completed their sync.
*/
public void markSyncReady() {
+ if (DEBUG) {
+ Log.d(TAG, "markSyncReady " + mName);
+ }
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "markSyncReady " + mName);
}
@@ -456,7 +460,15 @@
*/
public void addTransaction(@NonNull Transaction transaction) {
synchronized (mLock) {
- mTransaction.merge(transaction);
+ // If the caller tries to add a transaction to a completed SSG, just apply the
+ // transaction immediately since there's nothing to wait on.
+ if (mFinished) {
+ Log.w(TAG, "Adding transaction to a completed SurfaceSyncGroup(" + mName + "). "
+ + " Applying immediately");
+ transaction.apply();
+ } else {
+ mTransaction.merge(transaction);
+ }
}
}
@@ -509,7 +521,7 @@
private boolean addLocalSync(ISurfaceSyncGroup childSyncToken, boolean parentSyncGroupMerge) {
if (DEBUG) {
- Log.d(TAG, "Adding local sync " + mName);
+ Log.d(TAG, "Adding local sync to " + mName);
}
SurfaceSyncGroup childSurfaceSyncGroup = getSurfaceSyncGroup(childSyncToken);
@@ -540,7 +552,7 @@
private void setTransactionCallbackFromParent(ISurfaceSyncGroup parentSyncGroup,
ITransactionReadyCallback transactionReadyCallback) {
if (DEBUG) {
- Log.d(TAG, "setTransactionCallbackFromParent " + mName);
+ Log.d(TAG, "setTransactionCallbackFromParent for child " + mName);
}
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
@@ -677,7 +689,7 @@
*/
public ITransactionReadyCallback createTransactionReadyCallback(boolean parentSyncGroupMerge) {
if (DEBUG) {
- Log.d(TAG, "createTransactionReadyCallback " + mName);
+ Log.d(TAG, "createTransactionReadyCallback as part of " + mName);
}
ITransactionReadyCallback transactionReadyCallback =
new ITransactionReadyCallback.Stub() {
@@ -780,7 +792,7 @@
Runnable runnable = () -> {
Log.e(TAG, "Failed to receive transaction ready in " + TRANSACTION_READY_TIMEOUT
- + "ms. Marking SurfaceSyncGroup as ready " + mName);
+ + "ms. Marking SurfaceSyncGroup(" + mName + ") as ready");
// Clear out any pending syncs in case the other syncs can't complete or timeout due to
// a crash.
synchronized (mLock) {
diff --git a/core/java/com/android/internal/app/OWNERS b/core/java/com/android/internal/app/OWNERS
index a1d571f..52f18fb 100644
--- a/core/java/com/android/internal/app/OWNERS
+++ b/core/java/com/android/internal/app/OWNERS
@@ -1,15 +1,16 @@
per-file *AppOp* = file:/core/java/android/permission/OWNERS
per-file UnlaunchableAppActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
per-file IntentForwarderActivity.java = file:/core/java/android/app/admin/WorkProfile_OWNERS
-per-file *Resolver* = file:/packages/SystemUI/OWNERS
-per-file *Chooser* = file:/packages/SystemUI/OWNERS
-per-file SimpleIconFactory.java = file:/packages/SystemUI/OWNERS
-per-file AbstractMultiProfilePagerAdapter.java = file:/packages/SystemUI/OWNERS
-per-file *EmptyStateProvider.java = file:/packages/SystemUI/OWNERS
per-file NetInitiatedActivity.java = file:/location/java/android/location/OWNERS
per-file *BatteryStats* = file:/BATTERY_STATS_OWNERS
per-file *SoundTrigger* = file:/media/java/android/media/soundtrigger/OWNERS
+# Chooser and Resolver.
+per-file *Chooser* = file:chooser/OWNERS
+per-file *Resolver* = file:chooser/OWNERS
+per-file SimpleIconFactory.java = file:chooser/OWNERS
+per-file AbstractMultiProfilePagerAdapter.java = file:chooser/OWNERS
+per-file *EmptyStateProvider.java = file:chooser/OWNERS
# Voice Interaction
per-file *Assist* = file:/core/java/android/service/voice/OWNERS
diff --git a/core/java/com/android/internal/app/chooser/OWNERS b/core/java/com/android/internal/app/chooser/OWNERS
index a6f1632..0844cfa 100644
--- a/core/java/com/android/internal/app/chooser/OWNERS
+++ b/core/java/com/android/internal/app/chooser/OWNERS
@@ -1 +1,3 @@
-file:/packages/SystemUI/OWNERS
\ No newline at end of file
+# Bug component: 324112
+
+include platform/packages/modules/IntentResolver:/OWNERS
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index cee8c1a..3633d91 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -1018,7 +1018,7 @@
* Applies debugger system properties to the zygote arguments.
*
* For eng builds all apps are debuggable. On userdebug and user builds
- * if persist.debuggable.dalvik.vm.jdwp.enabled is 1 all apps are
+ * if persist.debug.dalvik.vm.jdwp.enabled is 1 all apps are
* debuggable. Otherwise, the debugger state is specified via the
* "--enable-jdwp" flag in the spawn request.
*
diff --git a/core/java/com/android/internal/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index 1172f7b..aa2318d 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -50,6 +50,7 @@
import android.media.ImageReader;
import android.os.SystemProperties;
import android.util.Slog;
+import android.view.InflateException;
import android.view.SurfaceControl;
import android.view.WindowManager.LayoutParams;
import android.view.WindowManager.TransitionOldType;
@@ -1264,7 +1265,7 @@
public static Animation loadAnimationSafely(Context context, int resId, String tag) {
try {
return AnimationUtils.loadAnimation(context, resId);
- } catch (Resources.NotFoundException e) {
+ } catch (Resources.NotFoundException | InflateException e) {
Slog.w(tag, "Unable to load animation resource", e);
return null;
}
diff --git a/core/jni/android_graphics_BLASTBufferQueue.cpp b/core/jni/android_graphics_BLASTBufferQueue.cpp
index 0381510..55aa711 100644
--- a/core/jni/android_graphics_BLASTBufferQueue.cpp
+++ b/core/jni/android_graphics_BLASTBufferQueue.cpp
@@ -125,26 +125,24 @@
jobject mObject;
};
-static void nativeSyncNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jobject callback,
+static bool nativeSyncNextTransaction(JNIEnv* env, jclass clazz, jlong ptr, jobject callback,
jboolean acquireSingleBuffer) {
+ LOG_ALWAYS_FATAL_IF(!callback, "callback passed in to syncNextTransaction must not be NULL");
+
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
JavaVM* vm = nullptr;
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
- if (!callback) {
- queue->syncNextTransaction(nullptr, acquireSingleBuffer);
- } else {
- auto globalCallbackRef =
- std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
- queue->syncNextTransaction(
- [globalCallbackRef](SurfaceComposerClient::Transaction* t) {
- JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
- env->NewObject(gTransactionClassInfo.clazz,
- gTransactionClassInfo.ctor,
- reinterpret_cast<jlong>(t)));
- },
- acquireSingleBuffer);
- }
+
+ auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
+ return queue->syncNextTransaction(
+ [globalCallbackRef](SurfaceComposerClient::Transaction* t) {
+ JNIEnv* env = getenv(globalCallbackRef->vm());
+ env->CallVoidMethod(globalCallbackRef->object(), gTransactionConsumer.accept,
+ env->NewObject(gTransactionClassInfo.clazz,
+ gTransactionClassInfo.ctor,
+ reinterpret_cast<jlong>(t)));
+ },
+ acquireSingleBuffer);
}
static void nativeStopContinuousSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
@@ -152,6 +150,11 @@
queue->stopContinuousSyncTransaction();
}
+static void nativeClearSyncTransaction(JNIEnv* env, jclass clazz, jlong ptr) {
+ sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
+ queue->clearSyncTransaction();
+}
+
static void nativeUpdate(JNIEnv* env, jclass clazz, jlong ptr, jlong surfaceControl, jlong width,
jlong height, jint format) {
sp<BLASTBufferQueue> queue = reinterpret_cast<BLASTBufferQueue*>(ptr);
@@ -207,8 +210,9 @@
{"nativeCreate", "(Ljava/lang/String;Z)J", (void*)nativeCreate},
{"nativeGetSurface", "(JZ)Landroid/view/Surface;", (void*)nativeGetSurface},
{"nativeDestroy", "(J)V", (void*)nativeDestroy},
- {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)V", (void*)nativeSyncNextTransaction},
+ {"nativeSyncNextTransaction", "(JLjava/util/function/Consumer;Z)Z", (void*)nativeSyncNextTransaction},
{"nativeStopContinuousSyncTransaction", "(J)V", (void*)nativeStopContinuousSyncTransaction},
+ {"nativeClearSyncTransaction", "(J)V", (void*)nativeClearSyncTransaction},
{"nativeUpdate", "(JJJJI)V", (void*)nativeUpdate},
{"nativeMergeWithNextTransaction", "(JJJ)V", (void*)nativeMergeWithNextTransaction},
{"nativeGetLastAcquiredFrameNum", "(J)J", (void*)nativeGetLastAcquiredFrameNum},
diff --git a/core/jni/android_view_DisplayEventReceiver.cpp b/core/jni/android_view_DisplayEventReceiver.cpp
index dd72689..410b441 100644
--- a/core/jni/android_view_DisplayEventReceiver.cpp
+++ b/core/jni/android_view_DisplayEventReceiver.cpp
@@ -48,12 +48,22 @@
struct {
jclass clazz;
+
jmethodID init;
+
+ jfieldID vsyncId;
+ jfieldID expectedPresentationTime;
+ jfieldID deadline;
} frameTimelineClassInfo;
struct {
jclass clazz;
+
jmethodID init;
+
+ jfieldID frameInterval;
+ jfieldID preferredFrameTimelineIndex;
+ jfieldID frameTimelines;
} vsyncEventDataClassInfo;
} gDisplayEventReceiverClassInfo;
@@ -61,7 +71,7 @@
class NativeDisplayEventReceiver : public DisplayEventDispatcher {
public:
- NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+ NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, jobject vsyncEventDataWeak,
const sp<MessageQueue>& messageQueue, jint vsyncSource,
jint eventRegistration, jlong layerHandle);
@@ -72,6 +82,7 @@
private:
jobject mReceiverWeakGlobal;
+ jobject mVsyncEventDataWeakGlobal;
sp<MessageQueue> mMessageQueue;
void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count,
@@ -85,6 +96,7 @@
};
NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak,
+ jobject vsyncEventDataWeak,
const sp<MessageQueue>& messageQueue,
jint vsyncSource, jint eventRegistration,
jlong layerHandle)
@@ -96,6 +108,7 @@
reinterpret_cast<IBinder*>(layerHandle))
: nullptr),
mReceiverWeakGlobal(env->NewGlobalRef(receiverWeak)),
+ mVsyncEventDataWeakGlobal(env->NewGlobalRef(vsyncEventDataWeak)),
mMessageQueue(messageQueue) {
ALOGV("receiver %p ~ Initializing display event receiver.", this);
}
@@ -154,12 +167,43 @@
JNIEnv* env = AndroidRuntime::getJNIEnv();
ScopedLocalRef<jobject> receiverObj(env, GetReferent(env, mReceiverWeakGlobal));
- if (receiverObj.get()) {
+ ScopedLocalRef<jobject> vsyncEventDataObj(env, GetReferent(env, mVsyncEventDataWeakGlobal));
+ if (receiverObj.get() && vsyncEventDataObj.get()) {
ALOGV("receiver %p ~ Invoking vsync handler.", this);
- jobject javaVsyncEventData = createJavaVsyncEventData(env, vsyncEventData);
+ env->SetIntField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo
+ .preferredFrameTimelineIndex,
+ vsyncEventData.preferredFrameTimelineIndex);
+ env->SetLongField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval,
+ vsyncEventData.frameInterval);
+
+ ScopedLocalRef<jobjectArray>
+ frameTimelinesObj(env,
+ reinterpret_cast<jobjectArray>(
+ env->GetObjectField(vsyncEventDataObj.get(),
+ gDisplayEventReceiverClassInfo
+ .vsyncEventDataClassInfo
+ .frameTimelines)));
+ for (int i = 0; i < VsyncEventData::kFrameTimelinesLength; i++) {
+ VsyncEventData::FrameTimeline& frameTimeline = vsyncEventData.frameTimelines[i];
+ ScopedLocalRef<jobject>
+ frameTimelineObj(env, env->GetObjectArrayElement(frameTimelinesObj.get(), i));
+ env->SetLongField(frameTimelineObj.get(),
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId,
+ frameTimeline.vsyncId);
+ env->SetLongField(frameTimelineObj.get(),
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo
+ .expectedPresentationTime,
+ frameTimeline.expectedPresentationTime);
+ env->SetLongField(frameTimelineObj.get(),
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline,
+ frameTimeline.deadlineTimestamp);
+ }
+
env->CallVoidMethod(receiverObj.get(), gDisplayEventReceiverClassInfo.dispatchVsync,
- timestamp, displayId.value, count, javaVsyncEventData);
+ timestamp, displayId.value, count);
ALOGV("receiver %p ~ Returned from vsync handler.", this);
}
@@ -227,8 +271,9 @@
mMessageQueue->raiseAndClearException(env, "dispatchModeChanged");
}
-static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject messageQueueObj,
- jint vsyncSource, jint eventRegistration, jlong layerHandle) {
+static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject vsyncEventDataWeak,
+ jobject messageQueueObj, jint vsyncSource, jint eventRegistration,
+ jlong layerHandle) {
sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
if (messageQueue == NULL) {
jniThrowRuntimeException(env, "MessageQueue is not initialized.");
@@ -236,8 +281,8 @@
}
sp<NativeDisplayEventReceiver> receiver =
- new NativeDisplayEventReceiver(env, receiverWeak, messageQueue, vsyncSource,
- eventRegistration, layerHandle);
+ new NativeDisplayEventReceiver(env, receiverWeak, vsyncEventDataWeak, messageQueue,
+ vsyncSource, eventRegistration, layerHandle);
status_t status = receiver->initialize();
if (status) {
String8 message;
@@ -284,7 +329,9 @@
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- {"nativeInit", "(Ljava/lang/ref/WeakReference;Landroid/os/MessageQueue;IIJ)J",
+ {"nativeInit",
+ "(Ljava/lang/ref/WeakReference;Ljava/lang/ref/WeakReference;Landroid/os/"
+ "MessageQueue;IIJ)J",
(void*)nativeInit},
{"nativeGetDisplayEventReceiverFinalizer", "()J",
(void*)nativeGetDisplayEventReceiverFinalizer},
@@ -301,8 +348,7 @@
gDisplayEventReceiverClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
gDisplayEventReceiverClassInfo.dispatchVsync =
- GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync",
- "(JJILandroid/view/DisplayEventReceiver$VsyncEventData;)V");
+ GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.clazz, "dispatchVsync", "(JJI)V");
gDisplayEventReceiverClassInfo.dispatchHotplug = GetMethodIDOrDie(env,
gDisplayEventReceiverClassInfo.clazz, "dispatchHotplug", "(JJZ)V");
gDisplayEventReceiverClassInfo.dispatchModeChanged =
@@ -328,6 +374,15 @@
gDisplayEventReceiverClassInfo.frameTimelineClassInfo.init =
GetMethodIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
"<init>", "(JJJ)V");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.vsyncId =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "vsyncId", "J");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.expectedPresentationTime =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "expectedPresentationTime", "J");
+ gDisplayEventReceiverClassInfo.frameTimelineClassInfo.deadline =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.frameTimelineClassInfo.clazz,
+ "deadline", "J");
jclass vsyncEventDataClazz =
FindClassOrDie(env, "android/view/DisplayEventReceiver$VsyncEventData");
@@ -339,6 +394,17 @@
"([Landroid/view/"
"DisplayEventReceiver$VsyncEventData$FrameTimeline;IJ)V");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.preferredFrameTimelineIndex =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "preferredFrameTimelineIndex", "I");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameInterval =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "frameInterval", "J");
+ gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.frameTimelines =
+ GetFieldIDOrDie(env, gDisplayEventReceiverClassInfo.vsyncEventDataClassInfo.clazz,
+ "frameTimelines",
+ "[Landroid/view/DisplayEventReceiver$VsyncEventData$FrameTimeline;");
+
return res;
}
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 9f10ae60..5544701 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -534,8 +534,10 @@
<!-- If this is true, long press on power button will be available from the non-interactive state -->
<bool name="config_supportLongPressPowerWhenNonInteractive">false</bool>
- <!-- If this is true, then keep dreaming when undocking. -->
- <bool name="config_keepDreamingWhenUndocking">false</bool>
+ <!-- If this is true, then keep dreaming when unplugging.
+ This config was formerly known as config_keepDreamingWhenUndocking.
+ It has been updated to affect other plug types. -->
+ <bool name="config_keepDreamingWhenUnplugging">false</bool>
<!-- The timeout (in ms) to wait before attempting to reconnect to the dream overlay service if
it becomes disconnected -->
@@ -4304,8 +4306,12 @@
This package must be trusted, as it has the permissions to control other applications
on the device.
Example: "com.android.wellbeing"
+
+ Note: This config is deprecated, please use config_systemWellbeing instead.
-->
- <string name="config_defaultWellbeingPackage" translatable="false"></string>
+ <string name="config_defaultWellbeingPackage" translatable="false">
+ @string/config_systemWellbeing
+ </string>
<!-- The component name for the default system attention service.
This service must be trusted, as it can be activated without explicit consent of the user.
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index a823d1f..cea6032 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1944,7 +1944,7 @@
<java-symbol type="bool" name="config_allowTheaterModeWakeFromLidSwitch" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromDock" />
<java-symbol type="bool" name="config_allowTheaterModeWakeFromWindowLayout" />
- <java-symbol type="bool" name="config_keepDreamingWhenUndocking" />
+ <java-symbol type="bool" name="config_keepDreamingWhenUnplugging" />
<java-symbol type="integer" name="config_keyguardDrawnTimeout" />
<java-symbol type="bool" name="config_goToSleepOnButtonPressTheaterMode" />
<java-symbol type="bool" name="config_supportLongPressPowerWhenNonInteractive" />
diff --git a/core/res/res/xml/irq_device_map.xml b/core/res/res/xml/irq_device_map.xml
index 4fae8fb..8b3667e 100644
--- a/core/res/res/xml/irq_device_map.xml
+++ b/core/res/res/xml/irq_device_map.xml
@@ -18,14 +18,16 @@
-->
<irq-device-map>
<!-- This file maps devices (chips) that can send interrupts to the main processor (and bring it
- out of sleep) to logical subsystems in userspace code. Since each Android device has its own
- uniquely designed chipset, this mapping is expected to be empty by default and should be
- overridden by device-specific configs.
+ out of sleep) to logical subsystems in userspace code. Since each Android device can have
+ a differently designed chipset, this mapping is expected to be empty by default and should
+ be overridden by device-specific configs.
This mapping helps the system to meaningfully attribute CPU wakeups to logical work that
- happened on the device. The devices are referred to by their names as defined in the kernel.
- Currently, defined subsystems are:
- - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
- - Wifi: Use this to denote network traffic that uses the wifi transport.
+ happened on the device and the app activity that caused it. The devices are referred to by
+ their names as defined in the kernel. Currently, defined subsystems are:
+ - Alarm: Use this to denote wakeup alarms requested by apps via the AlarmManager API.
+ - Wifi: Use this to denote network traffic that uses the wifi transport.
+ - Sound_trigger: Use this to denote sound phrase detection, like the ones supported by
+ SoundTriggerManager.
The overlay should use tags <device> and <subsystem> to describe this mapping in the
following way:
diff --git a/data/keyboards/GoogleTV-Remote.idc b/data/keyboards/GoogleTV-Remote.idc
new file mode 100644
index 0000000..14fb4e2
--- /dev/null
+++ b/data/keyboards/GoogleTV-Remote.idc
@@ -0,0 +1,25 @@
+# Copyright 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+#
+# Input Device Configuration file for Google Reference Remote Control Unit (RCU).
+#
+#
+
+# Basic Parameters
+# Depending on the FLASH configurations, RCUs may have PID 0006 instead
+# of 0001.
+keyboard.layout = Vendor_0957_Product_0001
+keyboard.doNotWakeByDefault = 1
+audio.mic = 1
diff --git a/graphics/java/android/graphics/BLASTBufferQueue.java b/graphics/java/android/graphics/BLASTBufferQueue.java
index 9940ca3..c52f700 100644
--- a/graphics/java/android/graphics/BLASTBufferQueue.java
+++ b/graphics/java/android/graphics/BLASTBufferQueue.java
@@ -16,6 +16,7 @@
package android.graphics;
+import android.annotation.NonNull;
import android.view.Surface;
import android.view.SurfaceControl;
@@ -31,9 +32,10 @@
private static native long nativeCreate(String name, boolean updateDestinationFrame);
private static native void nativeDestroy(long ptr);
private static native Surface nativeGetSurface(long ptr, boolean includeSurfaceControlHandle);
- private static native void nativeSyncNextTransaction(long ptr,
+ private static native boolean nativeSyncNextTransaction(long ptr,
Consumer<SurfaceControl.Transaction> callback, boolean acquireSingleBuffer);
private static native void nativeStopContinuousSyncTransaction(long ptr);
+ private static native void nativeClearSyncTransaction(long ptr);
private static native void nativeUpdate(long ptr, long surfaceControl, long width, long height,
int format);
private static native void nativeMergeWithNextTransaction(long ptr, long transactionPtr,
@@ -92,9 +94,9 @@
* acquired. If false, continue to acquire all buffers into the
* transaction until stopContinuousSyncTransaction is called.
*/
- public void syncNextTransaction(boolean acquireSingleBuffer,
- Consumer<SurfaceControl.Transaction> callback) {
- nativeSyncNextTransaction(mNativeObject, callback, acquireSingleBuffer);
+ public boolean syncNextTransaction(boolean acquireSingleBuffer,
+ @NonNull Consumer<SurfaceControl.Transaction> callback) {
+ return nativeSyncNextTransaction(mNativeObject, callback, acquireSingleBuffer);
}
/**
@@ -104,8 +106,8 @@
* @param callback The callback invoked when the buffer has been added to the transaction. The
* callback will contain the transaction with the buffer.
*/
- public void syncNextTransaction(Consumer<SurfaceControl.Transaction> callback) {
- syncNextTransaction(true /* acquireSingleBuffer */, callback);
+ public boolean syncNextTransaction(@NonNull Consumer<SurfaceControl.Transaction> callback) {
+ return syncNextTransaction(true /* acquireSingleBuffer */, callback);
}
/**
@@ -118,6 +120,14 @@
}
/**
+ * Tell BBQ to clear the sync transaction that was previously set. The callback will not be
+ * invoked when the next frame is acquired.
+ */
+ public void clearSyncTransaction() {
+ nativeClearSyncTransaction(mNativeObject);
+ }
+
+ /**
* Updates {@link SurfaceControl}, size, and format for a particular BLASTBufferQueue
* @param sc The new SurfaceControl that this BLASTBufferQueue will update
* @param width The new width for the buffer.
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
index 575b0ce..cc46a4b 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/area/WindowAreaComponentImpl.java
@@ -129,12 +129,9 @@
* {@link WindowAreaComponent#STATUS_AVAILABLE} or
* {@link WindowAreaComponent#STATUS_UNAVAILABLE} if the feature is supported or not in that
* state respectively. When the rear display feature is triggered, the status is updated to be
- * {@link WindowAreaComponent#STATUS_UNAVAILABLE}.
+ * {@link WindowAreaComponent#STATUS_ACTIVE}.
* TODO(b/240727590): Prefix with AREA_
*
- * TODO(b/239833099): Add a STATUS_ACTIVE option to let apps know if a feature is currently
- * enabled.
- *
* @param consumer {@link Consumer} interested in receiving updates to the status of
* rear display mode.
*/
@@ -407,18 +404,21 @@
}
}
-
@GuardedBy("mLock")
private int getCurrentRearDisplayModeStatus() {
if (mRearDisplayState == INVALID_DEVICE_STATE) {
return WindowAreaComponent.STATUS_UNSUPPORTED;
}
- if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
- || !ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)
- || isRearDisplayActive()) {
+ if (!ArrayUtils.contains(mCurrentSupportedDeviceStates, mRearDisplayState)) {
return WindowAreaComponent.STATUS_UNAVAILABLE;
}
+
+ if (mRearDisplaySessionStatus == WindowAreaComponent.SESSION_STATE_ACTIVE
+ || isRearDisplayActive()) {
+ return WindowAreaComponent.STATUS_ACTIVE;
+ }
+
return WindowAreaComponent.STATUS_AVAILABLE;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
index 521a65c..bfbddbb 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/activityembedding/ActivityEmbeddingController.java
@@ -22,6 +22,7 @@
import static java.util.Objects.requireNonNull;
import android.content.Context;
+import android.graphics.Rect;
import android.os.IBinder;
import android.util.ArrayMap;
import android.view.SurfaceControl;
@@ -35,6 +36,9 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.Transitions;
+import com.android.wm.shell.util.TransitionUtil;
+
+import java.util.List;
/**
* Responsible for handling ActivityEmbedding related transitions.
@@ -86,12 +90,13 @@
@NonNull SurfaceControl.Transaction finishTransaction,
@NonNull Transitions.TransitionFinishCallback finishCallback) {
boolean containsEmbeddingSplit = false;
- for (TransitionInfo.Change change : info.getChanges()) {
+ boolean containsNonEmbeddedChange = false;
+ final List<TransitionInfo.Change> changes = info.getChanges();
+ for (int i = changes.size() - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = changes.get(i);
if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
- // Only animate the transition if all changes are in a Task with ActivityEmbedding.
- return false;
- }
- if (!containsEmbeddingSplit && !change.hasFlags(FLAG_FILLS_TASK)) {
+ containsNonEmbeddedChange = true;
+ } else if (!change.hasFlags(FLAG_FILLS_TASK)) {
// Whether the Task contains any ActivityEmbedding split before or after the
// transition.
containsEmbeddingSplit = true;
@@ -103,6 +108,9 @@
// such as the device is in a folded state.
return false;
}
+ if (containsNonEmbeddedChange && !handleNonEmbeddedChanges(changes)) {
+ return false;
+ }
// Start ActivityEmbedding animation.
mTransitionCallbacks.put(transition, finishCallback);
@@ -110,6 +118,37 @@
return true;
}
+ private boolean handleNonEmbeddedChanges(List<TransitionInfo.Change> changes) {
+ final Rect nonClosingEmbeddedArea = new Rect();
+ for (int i = changes.size() - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = changes.get(i);
+ if (!TransitionUtil.isClosingType(change.getMode())) {
+ if (change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ nonClosingEmbeddedArea.union(change.getEndAbsBounds());
+ continue;
+ }
+ // Not able to handle non-embedded container if it is not closing.
+ return false;
+ }
+ }
+ for (int i = changes.size() - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = changes.get(i);
+ if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)
+ && !nonClosingEmbeddedArea.contains(change.getEndAbsBounds())) {
+ // Unknown to animate containers outside the area of embedded activities.
+ return false;
+ }
+ }
+ // Drop the non-embedded closing change because it is occluded by embedded activities.
+ for (int i = changes.size() - 1; i >= 0; i--) {
+ final TransitionInfo.Change change = changes.get(i);
+ if (!change.hasFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY)) {
+ changes.remove(i);
+ }
+ }
+ return true;
+ }
+
@Nullable
@Override
public WindowContainerTransaction handleRequest(@NonNull IBinder transition,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
index 8c98c77..1d7e649 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipAnimationController.java
@@ -300,7 +300,7 @@
* @return true if handled by the handler, false otherwise.
*/
public boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
- Rect destinationBounds) {
+ Rect destinationBounds, float alpha) {
return false;
}
}
@@ -401,9 +401,10 @@
}
boolean handlePipTransaction(SurfaceControl leash, SurfaceControl.Transaction tx,
- Rect destinationBounds) {
+ Rect destinationBounds, float alpha) {
if (mPipTransactionHandler != null) {
- return mPipTransactionHandler.handlePipTransaction(leash, tx, destinationBounds);
+ return mPipTransactionHandler.handlePipTransaction(
+ leash, tx, destinationBounds, alpha);
}
return false;
}
@@ -548,7 +549,9 @@
getSurfaceTransactionHelper().alpha(tx, leash, alpha)
.round(tx, leash, shouldApplyCornerRadius())
.shadow(tx, leash, shouldApplyShadowRadius());
- tx.apply();
+ if (!handlePipTransaction(leash, tx, destinationBounds, alpha)) {
+ tx.apply();
+ }
}
@Override
@@ -663,7 +666,7 @@
.shadow(tx, leash, shouldApplyShadowRadius());
}
}
- if (!handlePipTransaction(leash, tx, bounds)) {
+ if (!handlePipTransaction(leash, tx, bounds, /* alpha= */ 1f)) {
tx.apply();
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 0006244..0775f52 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -45,6 +45,13 @@
String MENU_WINDOW_TITLE = "PipMenuView";
/**
+ * Used with
+ * {@link PipMenuController#movePipMenu(SurfaceControl, SurfaceControl.Transaction, Rect,
+ * float)} to indicate that we don't want to affect the alpha value of the menu surfaces.
+ */
+ float ALPHA_NO_CHANGE = -1f;
+
+ /**
* Called when
* {@link PipTaskOrganizer#onTaskAppeared(RunningTaskInfo, SurfaceControl)}
* is called.
@@ -85,8 +92,8 @@
* need to synchronize the movements on the same frame as PiP.
*/
default void movePipMenu(@Nullable SurfaceControl pipLeash,
- @Nullable SurfaceControl.Transaction t,
- Rect destinationBounds) {}
+ @Nullable SurfaceControl.Transaction t, Rect destinationBounds, float alpha) {
+ }
/**
* Update the PiP menu with the given bounds for re-layout purposes.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 5670fe6..d04ce15 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -249,7 +249,7 @@
}, null);
}
- private boolean shouldSyncPipTransactionWithMenu() {
+ protected boolean shouldSyncPipTransactionWithMenu() {
return mPipMenuController.isMenuVisible();
}
@@ -277,9 +277,9 @@
new PipAnimationController.PipTransactionHandler() {
@Override
public boolean handlePipTransaction(SurfaceControl leash,
- SurfaceControl.Transaction tx, Rect destinationBounds) {
+ SurfaceControl.Transaction tx, Rect destinationBounds, float alpha) {
if (shouldSyncPipTransactionWithMenu()) {
- mPipMenuController.movePipMenu(leash, tx, destinationBounds);
+ mPipMenuController.movePipMenu(leash, tx, destinationBounds, alpha);
return true;
}
return false;
@@ -381,6 +381,10 @@
return mPipTransitionController;
}
+ PipAnimationController.PipTransactionHandler getPipTransactionHandler() {
+ return mPipTransactionHandler;
+ }
+
public Rect getCurrentOrAnimatingBounds() {
PipAnimationController.PipTransitionAnimator animator =
mPipAnimationController.getCurrentAnimator();
@@ -712,15 +716,16 @@
return;
}
+ final int animationType = shouldAlwaysFadeIn()
+ ? ANIM_TYPE_ALPHA
+ : mPipAnimationController.takeOneShotEnterAnimationType();
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
+ mPipTransitionController.setEnterAnimationType(animationType);
// For Shell transition, we will animate the window in PipTransition#startAnimation
// instead of #onTaskAppeared.
return;
}
- final int animationType = shouldAlwaysFadeIn()
- ? ANIM_TYPE_ALPHA
- : mPipAnimationController.takeOneShotEnterAnimationType();
if (mWaitForFixedRotation) {
onTaskAppearedWithFixedRotation(animationType);
return;
@@ -1385,7 +1390,7 @@
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
if (shouldSyncPipTransactionWithMenu()) {
- mPipMenuController.movePipMenu(mLeash, tx, toBounds);
+ mPipMenuController.movePipMenu(mLeash, tx, toBounds, PipMenuController.ALPHA_NO_CHANGE);
} else {
tx.apply();
}
@@ -1551,7 +1556,8 @@
if (!isInPip()) {
return;
}
- mPipMenuController.movePipMenu(null, null, destinationBounds);
+ mPipMenuController.movePipMenu(null, null, destinationBounds,
+ PipMenuController.ALPHA_NO_CHANGE);
mPipMenuController.updateMenuBounds(destinationBounds);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
index b743140..aa3afc5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransition.java
@@ -274,9 +274,6 @@
if (!requestHasPipEnter(request)) {
throw new IllegalStateException("Called PiP augmentRequest when request has no PiP");
}
- mEnterAnimationType = mPipOrganizer.shouldAlwaysFadeIn()
- ? ANIM_TYPE_ALPHA
- : mPipAnimationController.takeOneShotEnterAnimationType();
if (mEnterAnimationType == ANIM_TYPE_ALPHA) {
mRequestedEnterTransition = transition;
mRequestedEnterTask = request.getTriggerTask().token;
@@ -665,6 +662,11 @@
return false;
}
+ @Override
+ public void setEnterAnimationType(@PipAnimationController.AnimationType int type) {
+ mEnterAnimationType = type;
+ }
+
private void startEnterAnimation(@NonNull TransitionInfo info,
@NonNull SurfaceControl.Transaction startTransaction,
@NonNull SurfaceControl.Transaction finishTransaction,
@@ -786,8 +788,6 @@
final int enterAnimationType = mEnterAnimationType;
if (enterAnimationType == ANIM_TYPE_ALPHA) {
- // Restore to default type.
- mEnterAnimationType = ANIM_TYPE_BOUNDS;
startTransaction.setAlpha(leash, 0f);
}
startTransaction.apply();
@@ -823,6 +823,7 @@
throw new RuntimeException("Unrecognized animation type: " + enterAnimationType);
}
animator.setTransitionDirection(TRANSITION_DIRECTION_TO_PIP)
+ .setPipTransactionHandler(mPipOrganizer.getPipTransactionHandler())
.setPipAnimationCallback(mPipAnimationCallback)
.setDuration(mEnterExitAnimationDuration);
if (rotationDelta != Surface.ROTATION_0 && mInFixedRotation) {
@@ -949,7 +950,8 @@
}
private void finishResizeForMenu(Rect destinationBounds) {
- mPipMenuController.movePipMenu(null, null, destinationBounds);
+ mPipMenuController.movePipMenu(null, null, destinationBounds,
+ PipMenuController.ALPHA_NO_CHANGE);
mPipMenuController.updateMenuBounds(destinationBounds);
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
index 7979ce7..ff7ab8b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTransitionController.java
@@ -225,6 +225,10 @@
throw new IllegalStateException("Request isn't entering PiP");
}
+ /** Sets the type of animation when a PiP task appears. */
+ public void setEnterAnimationType(@PipAnimationController.AnimationType int type) {
+ }
+
/** Play a transition animation for entering PiP on a specific PiP change. */
public void startEnterAnimation(@NonNull final TransitionInfo.Change pipChange,
@NonNull final SurfaceControl.Transaction startTransaction,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 94e593b..e7a1395 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -298,7 +298,8 @@
}
// Sync the menu bounds before showing it in case it is out of sync.
- movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds);
+ movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds,
+ PipMenuController.ALPHA_NO_CHANGE);
updateMenuBounds(stackBounds);
mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay,
@@ -311,7 +312,7 @@
@Override
public void movePipMenu(@Nullable SurfaceControl pipLeash,
@Nullable SurfaceControl.Transaction t,
- Rect destinationBounds) {
+ Rect destinationBounds, float alpha) {
if (destinationBounds.isEmpty()) {
return;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index b18e21c..b2a189b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -30,6 +30,7 @@
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewRootImpl;
+import android.view.WindowManager;
import android.view.WindowManagerGlobal;
import android.window.SurfaceSyncGroup;
@@ -202,8 +203,10 @@
}
private void addPipMenuViewToSystemWindows(View v, String title) {
- mSystemWindows.addView(v, getPipMenuLayoutParams(mContext, title, 0 /* width */,
- 0 /* height */), 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
+ final WindowManager.LayoutParams layoutParams =
+ getPipMenuLayoutParams(mContext, title, 0 /* width */, 0 /* height */);
+ layoutParams.alpha = 0f;
+ mSystemWindows.addView(v, layoutParams, 0 /* displayId */, SHELL_ROOT_LAYER_PIP);
}
void onPipTransitionFinished(boolean enterTransition) {
@@ -309,9 +312,9 @@
@Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction pipTx,
- Rect pipBounds) {
+ Rect pipBounds, float alpha) {
ProtoLog.d(ShellProtoLogGroup.WM_SHELL_PICTURE_IN_PICTURE,
- "%s: movePipMenu: %s", TAG, pipBounds.toShortString());
+ "%s: movePipMenu: %s, alpha %s", TAG, pipBounds.toShortString(), alpha);
if (pipBounds.isEmpty()) {
if (pipTx == null) {
@@ -333,6 +336,11 @@
pipTx.setPosition(frontSurface, menuDestBounds.left, menuDestBounds.top);
pipTx.setPosition(backSurface, menuDestBounds.left, menuDestBounds.top);
+ if (alpha != ALPHA_NO_CHANGE) {
+ pipTx.setAlpha(frontSurface, alpha);
+ pipTx.setAlpha(backSurface, alpha);
+ }
+
// Synchronize drawing the content in the front and back surfaces together with the pip
// transaction and the position change for the front and back surfaces
final SurfaceSyncGroup syncGroup = new SurfaceSyncGroup("TvPip");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
index 0940490..4819f66 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipTaskOrganizer.java
@@ -98,4 +98,11 @@
protected boolean shouldAlwaysFadeIn() {
return true;
}
+
+ @Override
+ protected boolean shouldSyncPipTransactionWithMenu() {
+ // We always have a menu visible and want to sync the pip transaction with the menu, even
+ // when the menu alpha is 0 (e.g. when a fade-in animation starts).
+ return true;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
index 2c08cd4..51b8000 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenTransitions.java
@@ -38,7 +38,6 @@
import android.animation.ValueAnimator;
import android.annotation.NonNull;
import android.annotation.Nullable;
-import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -139,20 +138,13 @@
t.setAlpha(parentChange.getLeash(), 1.f);
// and then animate this layer outside the parent (since, for example, this is
// the home task animating from fullscreen to part-screen).
- t.reparent(leash, info.getRoot(rootIdx).getLeash());
- t.setLayer(leash, info.getChanges().size() - i);
+ t.reparent(parentChange.getLeash(), info.getRoot(rootIdx).getLeash());
+ t.setLayer(parentChange.getLeash(), info.getChanges().size() - i);
// build the finish reparent/reposition
mFinishTransaction.reparent(leash, parentChange.getLeash());
mFinishTransaction.setPosition(leash,
change.getEndRelOffset().x, change.getEndRelOffset().y);
}
- // TODO(shell-transitions): screenshot here
- final Rect startBounds = new Rect(change.getStartAbsBounds());
- final Rect endBounds = new Rect(change.getEndAbsBounds());
- final Point rootOffset = info.getRoot(rootIdx).getOffset();
- startBounds.offset(-rootOffset.x, -rootOffset.y);
- endBounds.offset(-rootOffset.x, -rootOffset.y);
- startExampleResizeAnimation(leash, startBounds, endBounds);
}
boolean isRootOrSplitSideRoot = change.getParent() == null
|| topRoot.equals(change.getParent());
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index 2ee3ca2..e4f2724 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -2414,9 +2414,14 @@
}
}
if (mMainStage.getChildCount() == 0 || mSideStage.getChildCount() == 0) {
- // TODO(shell-transitions): Implement a fallback behavior for now.
- throw new IllegalStateException("Somehow removed the last task in a stage"
- + " outside of a proper transition");
+ Log.e(TAG, "Somehow removed the last task in a stage outside of a proper "
+ + "transition.");
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final int dismissTop = mMainStage.getChildCount() == 0
+ ? STAGE_TYPE_MAIN : STAGE_TYPE_SIDE;
+ prepareExitSplitScreen(dismissTop, wct);
+ mSplitTransitions.startDismissTransition(wct, this, dismissTop,
+ EXIT_REASON_UNKNOWN);
// This can happen in some pathological cases. For example:
// 1. main has 2 tasks [Task A (Single-task), Task B], side has one task [Task C]
// 2. Task B closes itself and starts Task A in LAUNCH_ADJACENT at the same time
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 3dd10a0..6e9ecda 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -327,6 +327,7 @@
@ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
+ boolean isDisplayRotationAnimationStarted = false;
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
final TransitionInfo.Change change = info.getChanges().get(i);
if (change.hasAllFlags(FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY
@@ -350,6 +351,7 @@
if (!(isSeamlessDisplayChange || anim == ROTATION_ANIMATION_JUMPCUT)) {
startRotationAnimation(startTransaction, change, info, anim, animations,
onAnimFinish);
+ isDisplayRotationAnimationStarted = true;
continue;
}
} else {
@@ -405,6 +407,14 @@
}
}
+ // Hide the invisible surface directly without animating it if there is a display
+ // rotation animation playing.
+ if (isDisplayRotationAnimationStarted && TransitionUtil.isClosingType(
+ change.getMode())) {
+ startTransaction.hide(change.getLeash());
+ continue;
+ }
+
// The back gesture has animated this change before transition happen, so here we don't
// play the animation again.
if (change.hasFlags(FLAG_BACK_GESTURE_ANIMATED)) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
index cbbb291..b8f615a 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/activityembedding/ActivityEmbeddingControllerTests.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.activityembedding;
+import static android.view.WindowManager.TRANSIT_CLOSE;
import static android.view.WindowManager.TRANSIT_OPEN;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
@@ -82,10 +83,13 @@
@Test
public void testStartAnimation_containsNonActivityEmbeddingChange() {
+ final TransitionInfo.Change nonEmbeddedOpen = createChange(0 /* flags */);
+ final TransitionInfo.Change embeddedOpen = createEmbeddedChange(
+ EMBEDDED_LEFT_BOUNDS, EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS);
+ nonEmbeddedOpen.setMode(TRANSIT_OPEN);
final TransitionInfo info = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
- .addChange(createEmbeddedChange(
- EMBEDDED_LEFT_BOUNDS, EMBEDDED_LEFT_BOUNDS, TASK_BOUNDS))
- .addChange(createChange(0 /* flags */))
+ .addChange(embeddedOpen)
+ .addChange(nonEmbeddedOpen)
.build();
// No-op because it contains non-embedded change.
@@ -95,6 +99,22 @@
verifyNoMoreInteractions(mStartTransaction);
verifyNoMoreInteractions(mFinishTransaction);
verifyNoMoreInteractions(mFinishCallback);
+
+ final TransitionInfo.Change nonEmbeddedClose = createChange(0 /* flags */);
+ nonEmbeddedClose.setMode(TRANSIT_CLOSE);
+ nonEmbeddedClose.setEndAbsBounds(TASK_BOUNDS);
+ final TransitionInfo.Change embeddedOpen2 = createEmbeddedChange(
+ EMBEDDED_RIGHT_BOUNDS, EMBEDDED_RIGHT_BOUNDS, TASK_BOUNDS);
+ final TransitionInfo info2 = new TransitionInfoBuilder(TRANSIT_OPEN, 0)
+ .addChange(embeddedOpen)
+ .addChange(embeddedOpen2)
+ .addChange(nonEmbeddedClose)
+ .build();
+ // Ok to animate because nonEmbeddedClose is occluded by embeddedOpen and embeddedOpen2.
+ assertTrue(mController.startAnimation(mTransition, info2, mStartTransaction,
+ mFinishTransaction, mFinishCallback));
+ // The non-embedded change is dropped to avoid affecting embedded animation.
+ assertFalse(info2.getChanges().contains(nonEmbeddedClose));
}
@Test
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b1d2e33..4759689 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -3730,7 +3730,12 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setA2dpSuspended(boolean enable) {
- AudioSystem.setParameters("A2dpSuspended=" + enable);
+ final IAudioService service = getService();
+ try {
+ service.setA2dpSuspended(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
@@ -3743,7 +3748,12 @@
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(Manifest.permission.BLUETOOTH_STACK)
public void setLeAudioSuspended(boolean enable) {
- AudioSystem.setParameters("LeAudioSuspended=" + enable);
+ final IAudioService service = getService();
+ try {
+ service.setLeAudioSuspended(enable);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
}
/**
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index e73cf87..3123ee6 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -1237,6 +1237,9 @@
public static final Set<Integer> DEVICE_IN_ALL_SCO_SET;
/** @hide */
public static final Set<Integer> DEVICE_IN_ALL_USB_SET;
+ /** @hide */
+ public static final Set<Integer> DEVICE_IN_ALL_BLE_SET;
+
static {
DEVICE_IN_ALL_SET = new HashSet<>();
DEVICE_IN_ALL_SET.add(DEVICE_IN_COMMUNICATION);
@@ -1276,6 +1279,66 @@
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_ACCESSORY);
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_DEVICE);
DEVICE_IN_ALL_USB_SET.add(DEVICE_IN_USB_HEADSET);
+
+ DEVICE_IN_ALL_BLE_SET = new HashSet<>();
+ DEVICE_IN_ALL_BLE_SET.add(DEVICE_IN_BLE_HEADSET);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothDevice(int deviceType) {
+ return isBluetoothA2dpOutDevice(deviceType)
+ || isBluetoothScoDevice(deviceType)
+ || isBluetoothLeDevice(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothOutDevice(int deviceType) {
+ return isBluetoothA2dpOutDevice(deviceType)
+ || isBluetoothScoOutDevice(deviceType)
+ || isBluetoothLeOutDevice(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothInDevice(int deviceType) {
+ return isBluetoothScoInDevice(deviceType)
+ || isBluetoothLeInDevice(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothA2dpOutDevice(int deviceType) {
+ return DEVICE_OUT_ALL_A2DP_SET.contains(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothScoOutDevice(int deviceType) {
+ return DEVICE_OUT_ALL_SCO_SET.contains(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothScoInDevice(int deviceType) {
+ return DEVICE_IN_ALL_SCO_SET.contains(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothScoDevice(int deviceType) {
+ return isBluetoothScoOutDevice(deviceType)
+ || isBluetoothScoInDevice(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothLeOutDevice(int deviceType) {
+ return DEVICE_OUT_ALL_BLE_SET.contains(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothLeInDevice(int deviceType) {
+ return DEVICE_IN_ALL_BLE_SET.contains(deviceType);
+ }
+
+ /** @hide */
+ public static boolean isBluetoothLeDevice(int deviceType) {
+ return isBluetoothLeOutDevice(deviceType)
+ || isBluetoothLeInDevice(deviceType);
}
/** @hide */
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index fe5afc5..7ce189b 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -231,6 +231,12 @@
void setBluetoothScoOn(boolean on);
+ @EnforcePermission("BLUETOOTH_STACK")
+ void setA2dpSuspended(boolean on);
+
+ @EnforcePermission("BLUETOOTH_STACK")
+ void setLeAudioSuspended(boolean enable);
+
boolean isBluetoothScoOn();
void setBluetoothA2dpOn(boolean on);
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
index e8d3b1f..108f494 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt
@@ -198,7 +198,8 @@
it.type,
it.credentialRetrievalData,
it.credentialRetrievalData,
- it.isSystemProviderRequired
+ it.isSystemProviderRequired,
+ it.allowedProviders,
)
if (credentialOptionJetpack is GetPublicKeyCredentialOption) {
credentialOptionJetpack.preferImmediatelyAvailableCredentials
@@ -462,7 +463,8 @@
createCredentialRequest.type,
createCredentialRequest.credentialData,
createCredentialRequest.candidateQueryData,
- createCredentialRequest.isSystemProviderRequired
+ createCredentialRequest.isSystemProviderRequired,
+ createCredentialRequest.origin,
)
val appPreferredDefaultProviderId: String? =
if (!requestInfo.hasPermissionToOverrideDefault()) null
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
index 64e42cc..b522729 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/VerificationActivity.java
@@ -16,6 +16,8 @@
package com.android.dynsystem;
+import static android.os.image.DynamicSystemClient.KEY_KEYGUARD_USE_DEFAULT_STRINGS;
+
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
@@ -47,10 +49,7 @@
KeyguardManager km = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (km != null) {
- String title = getString(R.string.keyguard_title);
- String description = getString(R.string.keyguard_description);
- Intent intent = km.createConfirmDeviceCredentialIntent(title, description);
-
+ Intent intent = createConfirmDeviceCredentialIntent(km);
if (intent == null) {
Log.d(TAG, "This device is not protected by a password/pin");
startInstallationService();
@@ -63,6 +62,23 @@
}
}
+ private Intent createConfirmDeviceCredentialIntent(KeyguardManager km) {
+ final boolean useDefaultStrings =
+ getIntent().getBooleanExtra(KEY_KEYGUARD_USE_DEFAULT_STRINGS, false);
+ final String title;
+ final String description;
+ if (useDefaultStrings) {
+ // Use default strings provided by keyguard manager
+ title = null;
+ description = null;
+ } else {
+ // Use custom strings provided by DSU
+ title = getString(R.string.keyguard_title);
+ description = getString(R.string.keyguard_description);
+ }
+ return km.createConfirmDeviceCredentialIntent(title, description);
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
diff --git a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt
index 4a6e0b6..0a7ccc5 100644
--- a/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt
+++ b/packages/SystemUI/plugin/src/com/android/systemui/plugins/log/LogBuffer.kt
@@ -253,8 +253,8 @@
@Synchronized
fun unfreeze() {
if (frozen) {
- log(TAG, LogLevel.DEBUG, { str1 = name }, { "$str1 unfrozen" })
frozen = false
+ log(TAG, LogLevel.DEBUG, { str1 = name }, { "$str1 unfrozen" })
}
}
diff --git a/packages/SystemUI/res/layout/controls_more_item.xml b/packages/SystemUI/res/layout/controls_more_item.xml
index da9c43c..73d1c54 100644
--- a/packages/SystemUI/res/layout/controls_more_item.xml
+++ b/packages/SystemUI/res/layout/controls_more_item.xml
@@ -13,13 +13,17 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/controls_more_item_text"
style="@style/Control.MenuItem"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_gravity="start"
+ android:layout_height="@dimen/control_menu_item_height"
+ android:layout_gravity="center_vertical"
+ android:background="@drawable/controls_popup_item_background"
android:paddingStart="@dimen/control_menu_horizontal_padding"
android:paddingEnd="@dimen/control_menu_horizontal_padding"
- android:textDirection="locale"/>
-
+ android:textDirection="locale"
+ android:textSize="@dimen/control_item_text_size"
+ tools:fontFamily="@null"
+ tools:text="@tools:sample/lorem/random" />
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/controls_spinner_item.xml b/packages/SystemUI/res/layout/controls_spinner_item.xml
index 4048d03..8119651 100644
--- a/packages/SystemUI/res/layout/controls_spinner_item.xml
+++ b/packages/SystemUI/res/layout/controls_spinner_item.xml
@@ -16,18 +16,18 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
- android:layout_height="@dimen/control_popup_item_height"
+ android:layout_height="@dimen/control_apps_popup_item_height"
android:background="@drawable/controls_popup_item_background"
android:gravity="center_vertical|start"
android:orientation="horizontal"
- android:paddingStart="@dimen/control_popup_item_padding"
- android:paddingEnd="@dimen/control_popup_item_padding">
+ android:paddingStart="@dimen/control_menu_horizontal_padding"
+ android:paddingEnd="@dimen/control_menu_horizontal_padding">
<ImageView
android:id="@+id/app_icon"
android:layout_width="@dimen/controls_header_app_icon_size"
android:layout_height="@dimen/controls_header_app_icon_size"
- android:layout_marginEnd="@dimen/control_popup_item_padding"
+ android:layout_marginEnd="@dimen/control_menu_horizontal_padding"
android:contentDescription="@null"
tools:src="@drawable/ic_android" />
diff --git a/packages/SystemUI/res/layout/notification_snooze.xml b/packages/SystemUI/res/layout/notification_snooze.xml
index 11ec025..8b53680 100644
--- a/packages/SystemUI/res/layout/notification_snooze.xml
+++ b/packages/SystemUI/res/layout/notification_snooze.xml
@@ -21,6 +21,8 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
+ android:paddingTop="2dp"
+ android:paddingBottom="2dp"
android:background="?androidprv:attr/colorSurface"
android:theme="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 0eb0a07..9cb8aa0 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -1199,19 +1199,17 @@
<dimen name="controls_top_margin">48dp</dimen>
<dimen name="controls_content_margin_horizontal">0dp</dimen>
<dimen name="control_header_text_size">24sp</dimen>
- <dimen name="control_item_text_size">16sp</dimen>
+ <dimen name="control_item_text_size">14sp</dimen>
<dimen name="control_menu_item_text_size">16sp</dimen>
- <dimen name="control_menu_item_min_height">56dp</dimen>
+ <dimen name="control_menu_item_height">54dp</dimen>
<dimen name="control_menu_vertical_padding">12dp</dimen>
- <dimen name="control_menu_horizontal_padding">16dp</dimen>
- <dimen name="control_popup_item_corner_radius">4dp</dimen>
- <dimen name="control_popup_item_height">56dp</dimen>
- <dimen name="control_popup_item_padding">16dp</dimen>
- <dimen name="control_popup_items_divider_height">1dp</dimen>
+ <dimen name="control_menu_horizontal_padding">@dimen/notification_side_paddings</dimen>
+ <dimen name="control_apps_popup_item_height">56dp</dimen>
+ <dimen name="control_popup_item_corner_radius">@dimen/notification_corner_radius_small</dimen>
+ <dimen name="control_popup_items_divider_height">@dimen/controls_app_divider_height</dimen>
<dimen name="control_popup_max_width">380dp</dimen>
- <dimen name="control_popup_corner_radius">28dp</dimen>
+ <dimen name="control_popup_corner_radius">@dimen/notification_corner_radius</dimen>
<dimen name="control_popup_horizontal_margin">16dp</dimen>
- <dimen name="control_spinner_padding_vertical">24dp</dimen>
<dimen name="control_spinner_padding_horizontal">20dp</dimen>
<dimen name="control_text_size">14sp</dimen>
<dimen name="control_icon_size">24dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 74ae954..19deefb 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -3086,4 +3086,7 @@
<!-- Content description for when assistant attention is active [CHAR LIMIT=NONE] -->
<string name="assistant_attention_content_description">Assistant attention on</string>
+
+ <!--- Content of toast triggered when the notes app entry point is triggered without setting a default notes app. [CHAR LIMIT=NONE] -->
+ <string name="set_default_notes_app_toast_content">Set default notes app in Settings</string>
</resources>
diff --git a/packages/SystemUI/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index a359d3b..9d0cc11 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -895,7 +895,7 @@
<item name="android:textColor">@color/control_primary_text</item>
<item name="android:singleLine">true</item>
<item name="android:gravity">center_vertical</item>
- <item name="android:minHeight">@dimen/control_menu_item_min_height</item>
+ <item name="android:minHeight">@dimen/control_menu_item_height</item>
</style>
<style name="Control.Spinner">
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 2dd2d39..c48aaf4 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -156,6 +156,7 @@
import com.android.systemui.dump.DumpsysTableLogger;
import com.android.systemui.keyguard.domain.interactor.FaceAuthenticationListener;
import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
+import com.android.systemui.keyguard.shared.constants.TrustAgentUiEvent;
import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus;
import com.android.systemui.keyguard.shared.model.AuthenticationStatus;
import com.android.systemui.keyguard.shared.model.DetectionStatus;
@@ -537,6 +538,14 @@
mLogger.logTrustGrantedWithFlags(flags, newlyUnlocked, userId, message);
if (userId == getCurrentUser()) {
+ if (newlyUnlocked) {
+ // if this callback is ever removed, this should then be logged in
+ // TrustRepository
+ mUiEventLogger.log(
+ TrustAgentUiEvent.TRUST_AGENT_NEWLY_UNLOCKED,
+ getKeyguardSessionId()
+ );
+ }
final TrustGrantFlags trustGrantFlags = new TrustGrantFlags(flags);
for (int i = 0; i < mCallbacks.size(); i++) {
KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
@@ -4373,7 +4382,7 @@
* Cancels all operations in the scheduler if it is hung for 10 seconds.
*/
public void startBiometricWatchdog() {
- if (mFaceManager != null) {
+ if (mFaceManager != null && !isFaceAuthInteractorEnabled()) {
mFaceManager.scheduleWatchdog();
}
if (mFpm != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index e42f051..517f94f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -637,6 +637,7 @@
@Override
public void onDetachedFromWindow() {
+ mPanelInteractionDetector.disable();
OnBackInvokedDispatcher dispatcher = findOnBackInvokedDispatcher();
if (dispatcher != null) {
findOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mBackCallback);
@@ -674,7 +675,6 @@
@Override
public void dismissWithoutCallback(boolean animate) {
- mPanelInteractionDetector.disable();
if (animate) {
animateAway(false /* sendReason */, 0 /* reason */);
} else {
@@ -685,7 +685,6 @@
@Override
public void dismissFromSystemServer() {
- mPanelInteractionDetector.disable();
animateAway(false /* sendReason */, 0 /* reason */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
index 6c62a39..b72801d 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogPanelInteractionDetector.kt
@@ -30,6 +30,7 @@
@MainThread
fun disable() {
if (action != null) {
+ Log.i(TAG, "Disable dectector")
action = null
shadeExpansionStateManager.removeExpansionListener(this::onPanelExpansionChanged)
}
@@ -40,7 +41,7 @@
mainExecutor.execute {
action?.let {
if (event.tracking || (event.expanded && event.fraction > 0)) {
- Log.v(TAG, "Detected panel interaction, event: $event")
+ Log.i(TAG, "Detected panel interaction, event: $event")
it.onPanelInteraction.run()
disable()
}
diff --git a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
index 25b1e3a..83e61d6 100644
--- a/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/bluetooth/BroadcastDialog.java
@@ -155,8 +155,7 @@
}
@Override
- public void onStart() {
- super.onStart();
+ public void start() {
registerBroadcastCallBack(mExecutor, mBroadcastCallback);
}
@@ -200,8 +199,7 @@
}
@Override
- public void onStop() {
- super.onStop();
+ public void stop() {
unregisterBroadcastCallBack(mBroadcastCallback);
}
diff --git a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
index 9e15c7e..f17d0f3 100644
--- a/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/contrast/ContrastDialog.kt
@@ -86,13 +86,11 @@
highlightContrast(toContrastLevel(initialContrast))
}
- override fun onStart() {
- super.onStart()
+ override fun start() {
uiModeManager.addContrastChangeListener(mainExecutor, this)
}
- override fun onStop() {
- super.onStop()
+ override fun stop() {
uiModeManager.removeContrastChangeListener(this)
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
index d08bc48..f7c8e96 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsPopupMenu.kt
@@ -20,11 +20,18 @@
import android.content.res.Resources
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Drawable
-import android.view.Gravity
+import android.view.Gravity.END
+import android.view.Gravity.GravityFlags
+import android.view.Gravity.NO_GRAVITY
+import android.view.Gravity.START
import android.view.View
+import android.view.View.MeasureSpec
+import android.view.ViewGroup
import android.widget.ListPopupWindow
+import android.widget.ListView
import android.widget.PopupWindow
import com.android.systemui.R
+import kotlin.math.max
class ControlsPopupMenu(context: Context) : ListPopupWindow(context) {
@@ -40,13 +47,13 @@
private val dimDrawable: Drawable = ColorDrawable(resources.getColor(R.color.control_popup_dim))
private var dismissListener: PopupWindow.OnDismissListener? = null
+ @GravityFlags private var dropDownGravity: Int = NO_GRAVITY
init {
setBackgroundDrawable(dialogBackground)
inputMethodMode = INPUT_METHOD_NOT_NEEDED
isModal = true
- setDropDownGravity(Gravity.START)
// dismiss method isn't called when popup is hidden by outside touch. So we need to
// override a listener to remove a dimming foreground
@@ -59,30 +66,68 @@
override fun show() {
// need to call show() first in order to construct the listView
super.show()
-
- val paddedWidth = resources.displayMetrics.widthPixels - 2 * horizontalMargin
- width = maxWidth.coerceAtMost(paddedWidth)
+ updateWidth()
anchorView?.let {
- horizontalOffset = -width / 2 + it.width / 2
- verticalOffset = -it.height / 2
- if (it.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
- horizontalOffset = -horizontalOffset
- }
-
+ positionPopup(it)
it.rootView.foreground = dimDrawable
}
-
with(listView!!) {
clipToOutline = true
background = dialogBackground
dividerHeight = listDividerHeight
}
-
// actual show takes into account updated ListView specs
super.show()
}
+ override fun setDropDownGravity(@GravityFlags gravity: Int) {
+ super.setDropDownGravity(gravity)
+ dropDownGravity = gravity
+ }
+
override fun setOnDismissListener(listener: PopupWindow.OnDismissListener?) {
dismissListener = listener
}
+
+ private fun updateWidth() {
+ val paddedWidth = resources.displayMetrics.widthPixels - 2 * horizontalMargin
+ val maxWidth = maxWidth.coerceAtMost(paddedWidth)
+ when (width) {
+ ViewGroup.LayoutParams.MATCH_PARENT -> {
+ width = maxWidth
+ }
+ ViewGroup.LayoutParams.WRAP_CONTENT -> {
+ width = listView!!.measureDesiredWidth(maxWidth).coerceAtMost(maxWidth)
+ }
+ }
+ }
+
+ private fun positionPopup(anchorView: View) {
+ when (dropDownGravity) {
+ NO_GRAVITY -> {
+ horizontalOffset = (-width + anchorView.width) / 2
+ if (anchorView.layoutDirection == View.LAYOUT_DIRECTION_RTL) {
+ horizontalOffset = -horizontalOffset
+ }
+ }
+ END,
+ START -> {
+ horizontalOffset = 0
+ }
+ }
+ verticalOffset = -anchorView.height / 2
+ }
+
+ private fun ListView.measureDesiredWidth(maxWidth: Int): Int {
+ var maxItemWidth = 0
+ repeat(adapter.count) {
+ val view = adapter.getView(it, null, listView)
+ view.measure(
+ MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST),
+ MeasureSpec.UNSPECIFIED
+ )
+ maxItemWidth = max(maxItemWidth, view.measuredWidth)
+ }
+ return maxItemWidth
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index d4ce9b6..92607c6 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -34,6 +34,7 @@
import android.service.controls.ControlsProviderService
import android.util.Log
import android.view.ContextThemeWrapper
+import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -72,7 +73,6 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
-import com.android.systemui.globalactions.GlobalActionsPopupMenu
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
@@ -540,12 +540,12 @@
val anchor = parent.requireViewById<ImageView>(R.id.controls_more)
anchor.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
- popup = GlobalActionsPopupMenu(
- popupThemedContext,
- false /* isDropDownMode */
- ).apply {
- setAnchorView(anchor)
+ popup = ControlsPopupMenu(popupThemedContext).apply {
+ width = ViewGroup.LayoutParams.WRAP_CONTENT
+ anchorView = anchor
+ setDropDownGravity(Gravity.END)
setAdapter(adapter)
+
setOnItemClickListener(object : AdapterView.OnItemClickListener {
override fun onItemClick(
parent: AdapterView<*>,
@@ -618,7 +618,8 @@
anchor.setOnClickListener(object : View.OnClickListener {
override fun onClick(v: View) {
popup = ControlsPopupMenu(popupThemedContext).apply {
- setAnchorView(anchor)
+ anchorView = anchor
+ width = ViewGroup.LayoutParams.MATCH_PARENT
setAdapter(adapter)
setOnItemClickListener(object : AdapterView.OnItemClickListener {
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
index 9bf6b2a..20d690e 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUICoreStartableModule.kt
@@ -48,6 +48,7 @@
import com.android.systemui.shortcut.ShortcutKeyDispatcher
import com.android.systemui.statusbar.notification.InstantAppNotifier
import com.android.systemui.statusbar.phone.KeyguardLiftController
+import com.android.systemui.statusbar.phone.LetterboxModule
import com.android.systemui.stylus.StylusUsiPowerStartable
import com.android.systemui.temporarydisplay.chipbar.ChipbarCoordinator
import com.android.systemui.theme.ThemeOverlayController
@@ -66,7 +67,8 @@
*/
@Module(includes = [
MultiUserUtilsModule::class,
- StartControlsStartableModule::class
+ StartControlsStartableModule::class,
+ LetterboxModule::class,
])
abstract class SystemUICoreStartableModule {
/** Inject into AuthController. */
diff --git a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
index 276a290..7d1ffca 100644
--- a/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/dump/DumpManager.kt
@@ -39,6 +39,11 @@
private val dumpables: MutableMap<String, RegisteredDumpable<Dumpable>> = ArrayMap()
private val buffers: MutableMap<String, RegisteredDumpable<LogBuffer>> = ArrayMap()
+ /** See [registerCriticalDumpable]. */
+ fun registerCriticalDumpable(module: Dumpable) {
+ registerCriticalDumpable(module::class.java.simpleName, module)
+ }
+
/**
* Registers a dumpable to be called during the CRITICAL section of the bug report.
*
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index f01749a..dfe8697 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -67,10 +67,6 @@
// TODO(b/254512538): Tracking Bug
val INSTANT_VOICE_REPLY = unreleasedFlag(111, "instant_voice_reply")
- // TODO(b/254512425): Tracking Bug
- val NOTIFICATION_MEMORY_MONITOR_ENABLED =
- releasedFlag(112, "notification_memory_monitor_enabled")
-
/**
* This flag is server-controlled and should stay as [unreleasedFlag] since we never want to
* enable it on release builds.
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
index 07753ca..4be47ec 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsDialogLite.java
@@ -2477,8 +2477,7 @@
}
@Override
- protected void onStart() {
- super.onStart();
+ protected void start() {
mGlobalActionsLayout.updateList();
if (mBackgroundDrawable instanceof ScrimDrawable) {
@@ -2509,8 +2508,7 @@
}
@Override
- protected void onStop() {
- super.onStop();
+ protected void stop() {
mColorExtractor.removeOnColorsChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
index 801b165..c41b5e4 100644
--- a/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
+++ b/packages/SystemUI/src/com/android/systemui/graphics/ImageLoader.kt
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
package com.android.systemui.graphics
import android.annotation.AnyThread
@@ -20,6 +36,7 @@
import android.util.Size
import androidx.core.content.res.ResourcesCompat
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import java.io.IOException
import javax.inject.Inject
@@ -35,7 +52,7 @@
class ImageLoader
@Inject
constructor(
- private val defaultContext: Context,
+ @Application private val defaultContext: Context,
@Background private val backgroundDispatcher: CoroutineDispatcher
) {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
index c4fc883..5f6098b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepository.kt
@@ -34,6 +34,7 @@
import com.android.systemui.dump.DumpManager
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AcquiredAuthenticationStatus
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
@@ -133,6 +134,7 @@
private val alternateBouncerInteractor: AlternateBouncerInteractor,
@FaceDetectTableLog private val faceDetectLog: TableLogBuffer,
@FaceAuthTableLog private val faceAuthLog: TableLogBuffer,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
dumpManager: DumpManager,
) : DeviceEntryFaceAuthRepository, Dumpable {
private var authCancellationSignal: CancellationSignal? = null
@@ -211,6 +213,13 @@
observeFaceAuthGatingChecks()
observeFaceDetectGatingChecks()
observeFaceAuthResettingConditions()
+ listenForSchedulingWatchdog()
+ }
+
+ private fun listenForSchedulingWatchdog() {
+ keyguardTransitionInteractor.anyStateToGoneTransition
+ .onEach { faceManager?.scheduleWatchdog() }
+ .launchIn(applicationScope)
}
private fun observeFaceAuthResettingConditions() {
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/TrustAgentUiEvent.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/TrustAgentUiEvent.kt
new file mode 100644
index 0000000..ef6079f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/constants/TrustAgentUiEvent.kt
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.keyguard.shared.constants
+
+import com.android.internal.logging.UiEvent
+import com.android.internal.logging.UiEventLogger
+
+enum class TrustAgentUiEvent(private val metricId: Int) : UiEventLogger.UiEventEnum {
+ @UiEvent(doc = "TrustAgent newly unlocked the device") TRUST_AGENT_NEWLY_UNLOCKED(1361);
+ override fun getId() = metricId
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
index 4cc0410..fa42114 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaDataManager.kt
@@ -49,6 +49,7 @@
import android.support.v4.media.MediaMetadataCompat
import android.text.TextUtils
import android.util.Log
+import android.util.Pair as APair
import androidx.media.utils.MediaConstants
import com.android.internal.logging.InstanceId
import com.android.keyguard.KeyguardUpdateMonitor
@@ -217,6 +218,13 @@
private var smartspaceSession: SmartspaceSession? = null
private var allowMediaRecommendations = allowMediaRecommendations(context)
+ private val artworkWidth =
+ context.resources.getDimensionPixelSize(
+ com.android.internal.R.dimen.config_mediaMetadataBitmapMaxSize
+ )
+ private val artworkHeight =
+ context.resources.getDimensionPixelSize(R.dimen.qs_media_session_height_expanded)
+
/** Check whether this notification is an RCN */
private fun isRemoteCastNotification(sbn: StatusBarNotification): Boolean {
return sbn.notification.extras.containsKey(Notification.EXTRA_MEDIA_REMOTE_DEVICE)
@@ -1250,9 +1258,21 @@
return null
}
- val source = ImageDecoder.createSource(context.getContentResolver(), uri)
+ val source = ImageDecoder.createSource(context.contentResolver, uri)
return try {
- ImageDecoder.decodeBitmap(source) { decoder, _, _ ->
+ ImageDecoder.decodeBitmap(source) { decoder, info, _ ->
+ val width = info.size.width
+ val height = info.size.height
+ val scale =
+ MediaDataUtils.getScaleFactor(
+ APair(width, height),
+ APair(artworkWidth, artworkHeight)
+ )
+
+ // Downscale if needed
+ if (scale != 0f && scale < 1) {
+ decoder.setTargetSize((scale * width).toInt(), (scale * height).toInt())
+ }
decoder.allocator = ImageDecoder.ALLOCATOR_SOFTWARE
}
} catch (e: IOException) {
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
index a1d9214..ed4eef9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/MediaTimeoutListener.kt
@@ -17,6 +17,7 @@
package com.android.systemui.media.controls.pipeline
import android.media.session.MediaController
+import android.media.session.MediaSession
import android.media.session.PlaybackState
import android.os.SystemProperties
import com.android.internal.annotations.VisibleForTesting
@@ -148,7 +149,7 @@
reusedListener?.let {
val wasPlaying = it.isPlaying()
logger.logUpdateListener(key, wasPlaying)
- it.mediaData = data
+ it.setMediaData(data)
it.key = key
mediaListeners[key] = it
if (wasPlaying != it.isPlaying()) {
@@ -208,24 +209,7 @@
var resumption: Boolean? = null
var destroyed = false
var expiration = Long.MAX_VALUE
-
- var mediaData: MediaData = data
- set(value) {
- destroyed = false
- mediaController?.unregisterCallback(this)
- field = value
- val token = field.token
- mediaController =
- if (token != null) {
- mediaControllerFactory.create(token)
- } else {
- null
- }
- mediaController?.registerCallback(this)
- // Let's register the cancellations, but not dispatch events now.
- // Timeouts didn't happen yet and reentrant events are troublesome.
- processState(mediaController?.playbackState, dispatchEvents = false)
- }
+ var sessionToken: MediaSession.Token? = null
// Resume controls may have null token
private var mediaController: MediaController? = null
@@ -236,7 +220,7 @@
fun isPlaying() = lastState?.state?.isPlaying() ?: false
init {
- mediaData = data
+ setMediaData(data)
}
fun destroy() {
@@ -245,8 +229,28 @@
destroyed = true
}
+ fun setMediaData(data: MediaData) {
+ sessionToken = data.token
+ destroyed = false
+ mediaController?.unregisterCallback(this)
+ mediaController =
+ if (data.token != null) {
+ mediaControllerFactory.create(data.token)
+ } else {
+ null
+ }
+ mediaController?.registerCallback(this)
+ // Let's register the cancellations, but not dispatch events now.
+ // Timeouts didn't happen yet and reentrant events are troublesome.
+ processState(
+ mediaController?.playbackState,
+ dispatchEvents = false,
+ currentResumption = data.resumption,
+ )
+ }
+
override fun onPlaybackStateChanged(state: PlaybackState?) {
- processState(state, dispatchEvents = true)
+ processState(state, dispatchEvents = true, currentResumption = resumption)
}
override fun onSessionDestroyed() {
@@ -263,14 +267,18 @@
}
}
- private fun processState(state: PlaybackState?, dispatchEvents: Boolean) {
+ private fun processState(
+ state: PlaybackState?,
+ dispatchEvents: Boolean,
+ currentResumption: Boolean?,
+ ) {
logger.logPlaybackState(key, state)
val playingStateSame = (state?.state?.isPlaying() == isPlaying())
val actionsSame =
(lastState?.actions == state?.actions) &&
areCustomActionListsEqual(lastState?.customActions, state?.customActions)
- val resumptionChanged = resumption != mediaData.resumption
+ val resumptionChanged = resumption != currentResumption
lastState = state
@@ -282,7 +290,7 @@
if (playingStateSame && !resumptionChanged) {
return
}
- resumption = mediaData.resumption
+ resumption = currentResumption
val playing = isPlaying()
if (!playing) {
@@ -294,7 +302,7 @@
}
expireMediaTimeout(key, "PLAYBACK STATE CHANGED - $state, $resumption")
val timeout =
- if (mediaData.resumption) {
+ if (currentResumption == true) {
RESUME_MEDIA_TIMEOUT
} else {
PAUSED_MEDIA_TIMEOUT
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
index cb1f12cf..40027a1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/ui/MediaControlPanel.java
@@ -56,6 +56,7 @@
import android.os.Trace;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
@@ -122,6 +123,11 @@
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.time.SystemClock;
+import dagger.Lazy;
+
+import kotlin.Triple;
+import kotlin.Unit;
+
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
@@ -129,10 +135,6 @@
import javax.inject.Inject;
-import dagger.Lazy;
-import kotlin.Triple;
-import kotlin.Unit;
-
/**
* A view controller used for Media Playback.
*/
@@ -1000,18 +1002,9 @@
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
- if (width == 0 || height == 0 || targetWidth == 0 || targetHeight == 0) {
- return;
- }
-
- float scale;
- if ((width / (float) height) > (targetWidth / (float) targetHeight)) {
- // Drawable is wider than target view, scale to match height
- scale = targetHeight / (float) height;
- } else {
- // Drawable is taller than target view, scale to match width
- scale = targetWidth / (float) width;
- }
+ float scale = MediaDataUtils.getScaleFactor(new Pair(width, height),
+ new Pair(targetWidth, targetHeight));
+ if (scale == 0) return;
transitionDrawable.setLayerSize(layer, (int) (scale * width), (int) (scale * height));
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
index e95106e..0239d36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/util/MediaDataUtils.java
@@ -22,6 +22,7 @@
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.Pair;
import androidx.core.math.MathUtils;
import androidx.media.utils.MediaConstants;
@@ -87,4 +88,32 @@
}
return null;
}
+
+ /**
+ * Calculate a scale factor that will allow the input to fill the target size.
+ *
+ * @param input width, height of the input view
+ * @param target width, height of the target view
+ * @return the scale factor; 0 if any given dimension is 0
+ */
+ public static float getScaleFactor(Pair<Integer, Integer> input,
+ Pair<Integer, Integer> target) {
+ float width = (float) input.first;
+ float height = (float) input.second;
+
+ float targetWidth = (float) target.first;
+ float targetHeight = (float) target.second;
+
+ if (width == 0 || height == 0 || targetWidth == 0 || targetHeight == 0) {
+ return 0f;
+ }
+
+ if ((width / height) > (targetWidth / targetHeight)) {
+ // Input is wider than target view, scale to match height
+ return targetHeight / height;
+ } else {
+ // Input is taller than target view, scale to match width
+ return targetWidth / width;
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
index 9606bcf..08e47a0 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBaseDialog.java
@@ -273,8 +273,7 @@
}
@Override
- public void onStart() {
- super.onStart();
+ public void start() {
mMediaOutputController.start(this);
if (isBroadcastSupported() && !mIsLeBroadcastCallbackRegistered) {
mMediaOutputController.registerLeBroadcastServiceCallback(mExecutor,
@@ -284,8 +283,7 @@
}
@Override
- public void onStop() {
- super.onStop();
+ public void stop() {
if (isBroadcastSupported() && mIsLeBroadcastCallbackRegistered) {
mMediaOutputController.unregisterLeBroadcastServiceCallback(mBroadcastCallback);
mIsLeBroadcastCallbackRegistered = false;
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
index f0ff140..abf0932 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputBroadcastDialog.java
@@ -212,8 +212,8 @@
}
@Override
- public void onStart() {
- super.onStart();
+ public void start() {
+ super.start();
if (!mIsLeBroadcastAssistantCallbackRegistered) {
mIsLeBroadcastAssistantCallbackRegistered = true;
mMediaOutputController.registerLeBroadcastAssistantServiceCallback(mExecutor,
@@ -223,8 +223,8 @@
}
@Override
- public void onStop() {
- super.onStop();
+ public void stop() {
+ super.stop();
if (mIsLeBroadcastAssistantCallbackRegistered) {
mIsLeBroadcastAssistantCallbackRegistered = false;
mMediaOutputController.unregisterLeBroadcastAssistantServiceCallback(
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
index 334c70b..5f4e7cac 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskController.kt
@@ -34,7 +34,9 @@
import android.os.UserHandle
import android.os.UserManager
import android.util.Log
+import android.widget.Toast
import androidx.annotation.VisibleForTesting
+import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.devicepolicy.areKeyguardShortcutsDisabled
import com.android.systemui.notetask.NoteTaskRoleManagerExt.createNoteShortcutInfoAsUser
@@ -170,7 +172,13 @@
return
}
- val info = resolver.resolveInfo(entryPoint, isKeyguardLocked) ?: return
+ val info = resolver.resolveInfo(entryPoint, isKeyguardLocked)
+
+ if (info == null) {
+ logDebug { "Default notes app isn't set" }
+ showNoDefaultNotesAppToast()
+ return
+ }
infoReference.set(info)
@@ -207,6 +215,12 @@
logDebug { "onShowNoteTask - completed: $info" }
}
+ @VisibleForTesting
+ fun showNoDefaultNotesAppToast() {
+ Toast.makeText(context, R.string.set_default_notes_app_toast_content, Toast.LENGTH_SHORT)
+ .show()
+ }
+
/**
* Set `android:enabled` property in the `AndroidManifest` associated with the Shortcut
* component to [value].
@@ -214,7 +228,7 @@
* If the shortcut entry `android:enabled` is set to `true`, the shortcut will be visible in the
* Widget Picker to all users.
*/
- fun setNoteTaskShortcutEnabled(value: Boolean) {
+ fun setNoteTaskShortcutEnabled(value: Boolean, user: UserHandle) {
val componentName = ComponentName(context, CreateNoteTaskShortcutActivity::class.java)
val enabledState =
@@ -224,7 +238,16 @@
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
}
- context.packageManager.setComponentEnabledSetting(
+ // If the required user matches the tracking user, the injected context is already a context
+ // of the required user. Avoid calling #createContextAsUser because creating a context for
+ // a user takes time.
+ val userContext =
+ if (user == userTracker.userHandle) {
+ context
+ } else {
+ context.createContextAsUser(user, /* flags= */ 0)
+ }
+ userContext.packageManager.setComponentEnabledSetting(
componentName,
enabledState,
PackageManager.DONT_KILL_APP,
@@ -246,7 +269,7 @@
val packageName = roleManager.getDefaultRoleHolderAsUser(ROLE_NOTES, user)
val hasNotesRoleHolder = isEnabled && !packageName.isNullOrEmpty()
- setNoteTaskShortcutEnabled(hasNotesRoleHolder)
+ setNoteTaskShortcutEnabled(hasNotesRoleHolder, user)
if (hasNotesRoleHolder) {
shortcutManager.enableShortcuts(listOf(SHORTCUT_ID))
diff --git a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
index 23ee13b..7bb615b 100644
--- a/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
+++ b/packages/SystemUI/src/com/android/systemui/notetask/NoteTaskInitializer.kt
@@ -20,6 +20,7 @@
import android.view.KeyEvent
import androidx.annotation.VisibleForTesting
import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -36,6 +37,7 @@
private val optionalBubbles: Optional<Bubbles>,
@Background private val backgroundExecutor: Executor,
@NoteTaskEnabledKey private val isEnabled: Boolean,
+ private val userTracker: UserTracker,
) {
@VisibleForTesting
@@ -44,8 +46,9 @@
override fun handleSystemKey(key: KeyEvent) {
if (key.keyCode == KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL) {
controller.showNoteTask(NoteTaskEntryPoint.TAIL_BUTTON)
- } else if (key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed &&
- key.isCtrlPressed) {
+ } else if (
+ key.keyCode == KeyEvent.KEYCODE_N && key.isMetaPressed && key.isCtrlPressed
+ ) {
controller.showNoteTask(NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
}
}
@@ -55,7 +58,7 @@
// Guard against feature not being enabled or mandatory dependencies aren't available.
if (!isEnabled || optionalBubbles.isEmpty) return
- controller.setNoteTaskShortcutEnabled(true)
+ controller.setNoteTaskShortcutEnabled(true, userTracker.userHandle)
commandQueue.addCallback(callbacks)
roleManager.addOnRoleHoldersChangedListenerAsUser(
backgroundExecutor,
diff --git a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
index 03145a7..35a7cf1 100644
--- a/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/privacy/PrivacyDialog.kt
@@ -90,8 +90,7 @@
}
}
- override fun onStop() {
- super.onStop()
+ override fun stop() {
dismissed.set(true)
val iterator = dismissListeners.iterator()
while (iterator.hasNext()) {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
index 039dafb..380b85c 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialog.java
@@ -259,8 +259,7 @@
}
@Override
- public void onStart() {
- super.onStart();
+ public void start() {
if (DEBUG) {
Log.d(TAG, "onStart");
}
@@ -280,8 +279,7 @@
}
@Override
- public void onStop() {
- super.onStop();
+ public void stop() {
if (DEBUG) {
Log.d(TAG, "onStop");
}
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index cffe45f..0a188e0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -16,6 +16,7 @@
package com.android.systemui.recents;
+import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST;
import static android.content.pm.PackageManager.MATCH_SYSTEM_ONLY;
import static android.view.MotionEvent.ACTION_CANCEL;
@@ -48,6 +49,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
+import android.content.pm.ResolveInfo;
import android.graphics.Region;
import android.hardware.input.InputManager;
import android.hardware.input.InputManagerGlobal;
@@ -114,6 +116,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
@@ -393,20 +396,29 @@
private final BroadcastReceiver mLauncherStateChangedReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
- StringBuilder extraComponentList = new StringBuilder(" components: ");
- if (intent.hasExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST)) {
- String[] comps = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
- if (comps != null) {
- for (String c : comps) {
- extraComponentList.append(c).append(", ");
- }
+ // If adding, bind immediately
+ if (Objects.equals(intent.getAction(), ACTION_PACKAGE_ADDED)) {
+ updateEnabledAndBinding();
+ return;
+ }
+
+ // ACTION_PACKAGE_CHANGED
+ String[] compsList = intent.getStringArrayExtra(EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ if (compsList == null) {
+ return;
+ }
+
+ // Only rebind for TouchInteractionService component from launcher
+ ResolveInfo ri = context.getPackageManager()
+ .resolveService(new Intent(ACTION_QUICKSTEP), 0);
+ String interestingComponent = ri.serviceInfo.name;
+ for (String component : compsList) {
+ if (interestingComponent.equals(component)) {
+ Log.i(TAG_OPS, "Rebinding for component [" + component + "] change");
+ updateEnabledAndBinding();
+ return;
}
}
- Log.d(TAG_OPS, "launcherStateChanged intent: " + intent + extraComponentList);
- updateEnabledState();
-
- // Reconnect immediately, instead of waiting for resume to arrive.
- startConnectionToCurrentUser();
}
};
@@ -621,8 +633,7 @@
screenLifecycle.addObserver(mScreenLifecycleObserver);
wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver);
// Connect to the service
- updateEnabledState();
- startConnectionToCurrentUser();
+ updateEnabledAndBinding();
// Listen for assistant changes
assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener);
@@ -644,6 +655,10 @@
private void dispatchNavigationBarSurface() {
try {
if (mOverviewProxy != null) {
+ // Catch all for cases where the surface is no longer valid
+ if (mNavigationBarSurface != null && !mNavigationBarSurface.isValid()) {
+ mNavigationBarSurface = null;
+ }
mOverviewProxy.onNavigationBarSurface(mNavigationBarSurface);
}
} catch (RemoteException e) {
@@ -651,6 +666,11 @@
}
}
+ private void updateEnabledAndBinding() {
+ updateEnabledState();
+ startConnectionToCurrentUser();
+ }
+
private void updateSystemUiStateFlags() {
final NavigationBar navBarFragment =
mNavBarControllerLazy.get().getDefaultNavigationBar();
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index 222a0f4f..a4a7d4c 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -129,6 +129,7 @@
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardBottomAreaInteractor;
+import com.android.systemui.keyguard.domain.interactor.KeyguardFaceAuthInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor;
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor;
import com.android.systemui.keyguard.shared.constants.KeyguardBouncerConstants;
@@ -330,6 +331,7 @@
private final PulseExpansionHandler mPulseExpansionHandler;
private final KeyguardBypassController mKeyguardBypassController;
private final KeyguardUpdateMonitor mUpdateMonitor;
+ private final KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
private final ConversationNotificationManager mConversationNotificationManager;
private final AuthController mAuthController;
private final MediaHierarchyManager mMediaHierarchyManager;
@@ -747,7 +749,8 @@
DumpManager dumpManager,
KeyguardLongPressViewModel keyguardLongPressViewModel,
KeyguardInteractor keyguardInteractor,
- ActivityStarter activityStarter) {
+ ActivityStarter activityStarter,
+ KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) {
mInteractionJankMonitor = interactionJankMonitor;
keyguardStateController.addCallback(new KeyguardStateController.Callback() {
@Override
@@ -891,6 +894,7 @@
mScreenOffAnimationController = screenOffAnimationController;
mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
mLastDownEvents = new NPVCDownEventState.Buffer(MAX_DOWN_EVENT_BUFFER_SIZE);
+ mKeyguardFaceAuthInteractor = keyguardFaceAuthInteractor;
int currentMode = navigationModeController.addListener(
mode -> mIsGestureNavigation = QuickStepContract.isGesturalMode(mode));
@@ -2764,6 +2768,7 @@
mShadeLog.v("onMiddleClicked on Keyguard, mDozingOnDown: false");
// Try triggering face auth, this "might" run. Check
// KeyguardUpdateMonitor#shouldListenForFace to see when face auth won't run.
+ mKeyguardFaceAuthInteractor.onNotificationPanelClicked();
boolean didFaceAuthRun = mUpdateMonitor.requestFaceAuth(
FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
index b4653be..f0815e9 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeHeaderController.kt
@@ -19,11 +19,14 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.IdRes
+import android.app.PendingIntent
import android.app.StatusBarManager
+import android.content.Intent
import android.content.res.Configuration
import android.os.Bundle
import android.os.Trace
import android.os.Trace.TRACE_TAG_APP
+import android.provider.AlarmClock
import android.util.Pair
import android.view.DisplayCutout
import android.view.View
@@ -41,6 +44,7 @@
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
import com.android.systemui.shade.ShadeHeaderController.Companion.HEADER_TRANSITION_ID
@@ -58,6 +62,7 @@
import com.android.systemui.statusbar.phone.dagger.StatusBarViewModule.SHADE_HEADER
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.NextAlarmController
import com.android.systemui.statusbar.policy.VariableDateView
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.ViewController
@@ -91,6 +96,8 @@
private val combinedShadeHeadersConstraintManager: CombinedShadeHeadersConstraintManager,
private val demoModeController: DemoModeController,
private val qsBatteryModeController: QsBatteryModeController,
+ private val nextAlarmController: NextAlarmController,
+ private val activityStarter: ActivityStarter,
) : ViewController<View>(header), Dumpable {
companion object {
@@ -103,6 +110,8 @@
@VisibleForTesting
internal val LARGE_SCREEN_HEADER_CONSTRAINT = R.id.large_screen_header_constraint
+ @VisibleForTesting internal val DEFAULT_CLOCK_INTENT = Intent(AlarmClock.ACTION_SHOW_ALARMS)
+
private fun Int.stateToString() =
when (this) {
QQS_HEADER_CONSTRAINT -> "QQS Header"
@@ -125,6 +134,7 @@
private var roundedCorners = 0
private var cutout: DisplayCutout? = null
private var lastInsets: WindowInsets? = null
+ private var nextAlarmIntent: PendingIntent? = null
private var qsDisabled = false
private var visible = false
@@ -252,6 +262,11 @@
}
}
+ private val nextAlarmCallback =
+ NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
+ nextAlarmIntent = nextAlarm?.showIntent
+ }
+
override fun onInit() {
variableDateViewControllerFactory.create(date as VariableDateView).init()
batteryMeterViewController.init()
@@ -286,19 +301,23 @@
mShadeCarrierGroup.setPaddingRelative((v.width * v.scaleX).toInt(), 0, 0, 0)
}
+ clock.setOnClickListener { launchClockActivity() }
dumpManager.registerDumpable(this)
configurationController.addCallback(configurationControllerListener)
demoModeController.addCallback(demoModeReceiver)
statusBarIconController.addIconGroup(iconManager)
+ nextAlarmController.addCallback(nextAlarmCallback)
}
override fun onViewDetached() {
+ clock.setOnClickListener(null)
privacyIconsController.chipVisibilityListener = null
dumpManager.unregisterDumpable(this::class.java.simpleName)
configurationController.removeCallback(configurationControllerListener)
demoModeController.removeCallback(demoModeReceiver)
statusBarIconController.removeIconGroup(iconManager)
+ nextAlarmController.removeCallback(nextAlarmCallback)
}
fun disable(state1: Int, state2: Int, animate: Boolean) {
@@ -318,6 +337,15 @@
.start()
}
+ @VisibleForTesting
+ internal fun launchClockActivity() {
+ if (nextAlarmIntent != null) {
+ activityStarter.postStartActivityDismissingKeyguard(nextAlarmIntent)
+ } else {
+ activityStarter.postStartActivityDismissingKeyguard(DEFAULT_CLOCK_INTENT, 0 /*delay */)
+ }
+ }
+
private fun loadConstraints() {
// Use resources.getXml instead of passing the resource id due to bug b/205018300
header
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index ced725e..ea9817c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -702,6 +702,13 @@
mProcessArtworkTasks.remove(task);
}
+ // TODO(b/273443374): remove
+ public boolean isLockscreenWallpaperOnNotificationShade() {
+ return mBackdrop != null && mLockscreenWallpaper != null
+ && !mLockscreenWallpaper.isLockscreenLiveWallpaperEnabled()
+ && (mBackdropFront.isVisibleToUser() || mBackdropBack.isVisibleToUser());
+ }
+
/**
* {@link AsyncTask} to prepare album art for use as backdrop on lock screen.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
index 82bd45c..6322edf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinator.kt
@@ -24,6 +24,10 @@
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.expansionChanges
@@ -40,22 +44,26 @@
import com.android.systemui.statusbar.policy.headsUpEvents
import com.android.systemui.util.settings.SecureSettings
import com.android.systemui.util.settings.SettingsProxyExt.observerFlow
-import javax.inject.Inject
-import kotlin.time.Duration
-import kotlin.time.Duration.Companion.seconds
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.emitAll
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.transformLatest
import kotlinx.coroutines.launch
+import kotlinx.coroutines.yield
+import javax.inject.Inject
+import kotlin.time.Duration
+import kotlin.time.Duration.Companion.seconds
/**
* Filters low priority and privacy-sensitive notifications from the lockscreen, and hides section
@@ -69,6 +77,7 @@
private val headsUpManager: HeadsUpManager,
private val keyguardNotificationVisibilityProvider: KeyguardNotificationVisibilityProvider,
private val keyguardRepository: KeyguardRepository,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val notifPipelineFlags: NotifPipelineFlags,
@Application private val scope: CoroutineScope,
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider,
@@ -99,21 +108,46 @@
}
private suspend fun trackUnseenNotificationsWhileUnlocked() {
+ // Whether or not we're actively tracking unseen notifications to mark them as seen when
+ // appropriate.
+ val isTrackingUnseen: Flow<Boolean> =
+ keyguardRepository.isKeyguardShowing
+ // transformLatest so that we can cancel listening to keyguard transitions once
+ // isKeyguardShowing changes (after a successful transition to the keyguard).
+ .transformLatest { isShowing ->
+ if (isShowing) {
+ // If the keyguard is showing, we're not tracking unseen.
+ emit(false)
+ } else {
+ // If the keyguard stops showing, then start tracking unseen notifications.
+ emit(true)
+ // If the screen is turning off, stop tracking, but if that transition is
+ // cancelled, then start again.
+ emitAll(
+ keyguardTransitionRepository.transitions
+ .map { step -> !step.isScreenTurningOff }
+ )
+ }
+ }
+ // Prevent double emit of `false` caused by transition to AOD, followed by keyguard
+ // showing
+ .distinctUntilChanged()
+
// Use collectLatest so that trackUnseenNotifications() is cancelled when the keyguard is
// showing again
- var clearUnseenOnUnlock = false
- keyguardRepository.isKeyguardShowing.collectLatest { isKeyguardShowing ->
- if (isKeyguardShowing) {
+ var clearUnseenOnBeginTracking = false
+ isTrackingUnseen.collectLatest { trackingUnseen ->
+ if (!trackingUnseen) {
// Wait for the user to spend enough time on the lock screen before clearing unseen
// set when unlocked
awaitTimeSpentNotDozing(SEEN_TIMEOUT)
- clearUnseenOnUnlock = true
+ clearUnseenOnBeginTracking = true
} else {
- unseenNotifFilter.invalidateList("keyguard no longer showing")
- if (clearUnseenOnUnlock) {
- clearUnseenOnUnlock = false
+ if (clearUnseenOnBeginTracking) {
+ clearUnseenOnBeginTracking = false
unseenNotifications.clear()
}
+ unseenNotifFilter.invalidateList("keyguard no longer showing")
trackUnseenNotifications()
}
}
@@ -142,7 +176,10 @@
}
private suspend fun clearUnseenNotificationsWhenShadeIsExpanded() {
- statusBarStateController.expansionChanges.collect { isExpanded ->
+ statusBarStateController.expansionChanges.collectLatest { isExpanded ->
+ // Give keyguard events time to propagate, in case this expansion is part of the
+ // keyguard transition and not the user expanding the shade
+ yield()
if (isExpanded) {
unseenNotifications.clear()
}
@@ -276,3 +313,6 @@
private val SEEN_TIMEOUT = 5.seconds
}
}
+
+private val TransitionStep.isScreenTurningOff: Boolean get() =
+ transitionState == TransitionState.STARTED && to != KeyguardState.GONE
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
index 8eef3f3..0ed4175 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/init/NotificationsControllerImpl.kt
@@ -20,7 +20,6 @@
import com.android.systemui.ForegroundServiceNotificationListener
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
-import com.android.systemui.flags.Flags
import com.android.systemui.people.widget.PeopleSpaceWidgetManager
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption
import com.android.systemui.statusbar.NotificationListener
@@ -117,9 +116,7 @@
notificationLogger.setUpWithContainer(listContainer)
peopleSpaceWidgetManager.attach(notificationListener)
fgsNotifListener.init()
- if (featureFlags.isEnabled(Flags.NOTIFICATION_MEMORY_MONITOR_ENABLED)) {
- memoryMonitor.get().init()
- }
+ memoryMonitor.get().init()
}
// TODO: Convert all functions below this line into listeners instead of public methods
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 7596ce0..0ec20ca 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -2614,6 +2614,7 @@
}
mRemoteInputManager.closeRemoteInputs();
if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
+ mShadeLogger.d("ACTION_CLOSE_SYSTEM_DIALOGS intent: closing shade");
int flags = CommandQueue.FLAG_EXCLUDE_NONE;
if (reason != null) {
if (reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
@@ -2628,6 +2629,8 @@
}
}
mShadeController.animateCollapseShade(flags);
+ } else {
+ mShadeLogger.d("ACTION_CLOSE_SYSTEM_DIALOGS intent: non-matching user ID");
}
} else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
if (mNotificationShadeWindowController != null) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index 3268032..2814e8d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -49,6 +49,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.disableflags.DisableStateTracker;
@@ -119,6 +120,9 @@
private final Object mLock = new Object();
private final KeyguardLogger mLogger;
+ // TODO(b/273443374): remove
+ private NotificationMediaManager mNotificationMediaManager;
+
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@Override
@@ -283,7 +287,8 @@
SecureSettings secureSettings,
CommandQueue commandQueue,
@Main Executor mainExecutor,
- KeyguardLogger logger
+ KeyguardLogger logger,
+ NotificationMediaManager notificationMediaManager
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -335,6 +340,7 @@
/* mask2= */ DISABLE2_SYSTEM_ICONS,
this::updateViewState
);
+ mNotificationMediaManager = notificationMediaManager;
}
@Override
@@ -484,8 +490,11 @@
* (1.0f - mKeyguardHeadsUpShowingAmount);
}
- if (mSystemEventAnimator.isAnimationRunning()) {
+ if (mSystemEventAnimator.isAnimationRunning()
+ && !mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
newAlpha = Math.min(newAlpha, mSystemEventAnimatorAlpha);
+ } else {
+ mView.setTranslationX(0);
}
boolean hideForBypass =
@@ -625,11 +634,21 @@
private StatusBarSystemEventDefaultAnimator getSystemEventAnimator(boolean isAnimationRunning) {
return new StatusBarSystemEventDefaultAnimator(getResources(), (alpha) -> {
- mSystemEventAnimatorAlpha = alpha;
+ // TODO(b/273443374): remove if-else condition
+ if (!mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
+ mSystemEventAnimatorAlpha = alpha;
+ } else {
+ mSystemEventAnimatorAlpha = 1f;
+ }
updateViewState();
return Unit.INSTANCE;
}, (translationX) -> {
- mView.setTranslationX(translationX);
+ // TODO(b/273443374): remove if-else condition
+ if (!mNotificationMediaManager.isLockscreenWallpaperOnNotificationShade()) {
+ mView.setTranslationX(translationX);
+ } else {
+ mView.setTranslationX(0);
+ }
return Unit.INSTANCE;
}, isAnimationRunning);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
index 3989854..f742645 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxAppearanceCalculator.kt
@@ -26,10 +26,10 @@
import com.android.internal.statusbar.LetterboxDetails
import com.android.internal.util.ContrastColorUtil
import com.android.internal.view.AppearanceRegion
+import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.core.StatusBarInitializer.OnStatusBarViewInitializedListener
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import com.android.systemui.statusbar.phone.fragment.dagger.StatusBarFragmentComponent
import java.io.PrintWriter
import java.util.Arrays
@@ -50,25 +50,21 @@
* Responsible for calculating the [Appearance] and [AppearanceRegion] for the status bar when apps
* are letterboxed.
*/
-@CentralSurfacesScope
+@SysUISingleton
class LetterboxAppearanceCalculator
@Inject
constructor(
private val lightBarController: LightBarController,
- private val dumpManager: DumpManager,
+ dumpManager: DumpManager,
private val letterboxBackgroundProvider: LetterboxBackgroundProvider,
-) : OnStatusBarViewInitializedListener, CentralSurfacesComponent.Startable {
+) : OnStatusBarViewInitializedListener, Dumpable {
+
+ init {
+ dumpManager.registerCriticalDumpable(this)
+ }
private var statusBarBoundsProvider: StatusBarBoundsProvider? = null
- override fun start() {
- dumpManager.registerCriticalDumpable(javaClass.simpleName) { pw, _ -> dump(pw) }
- }
-
- override fun stop() {
- dumpManager.unregisterDumpable(javaClass.simpleName)
- }
-
private var lastAppearance: Int? = null
private var lastAppearanceRegions: Array<AppearanceRegion>? = null
private var lastLetterboxes: Array<LetterboxDetails>? = null
@@ -216,8 +212,8 @@
return this.intersect(other)
}
- private fun dump(printWriter: PrintWriter) {
- printWriter.println(
+ override fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println(
"""
lastAppearance: ${lastAppearance?.toAppearanceString()}
lastAppearanceRegion: ${Arrays.toString(lastAppearanceRegions)},
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
index 2763750..34c7059e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProvider.kt
@@ -22,28 +22,25 @@
import android.os.Handler
import android.os.RemoteException
import android.view.IWindowManager
+import com.android.systemui.CoreStartable
import com.android.systemui.Dumpable
+import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.dump.DumpManager
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent
-import com.android.systemui.statusbar.phone.dagger.CentralSurfacesComponent.CentralSurfacesScope
import java.io.PrintWriter
import java.util.concurrent.Executor
import javax.inject.Inject
/** Responsible for providing information about the background of letterboxed apps. */
-@CentralSurfacesScope
+@SysUISingleton
class LetterboxBackgroundProvider
@Inject
constructor(
private val windowManager: IWindowManager,
@Background private val backgroundExecutor: Executor,
- private val dumpManager: DumpManager,
private val wallpaperManager: WallpaperManager,
@Main private val mainHandler: Handler,
-) : CentralSurfacesComponent.Startable, Dumpable {
-
+) : CoreStartable, Dumpable {
@ColorInt
var letterboxBackgroundColor: Int = Color.BLACK
private set
@@ -57,7 +54,6 @@
}
override fun start() {
- dumpManager.registerDumpable(javaClass.simpleName, this)
fetchBackgroundColorInfo()
wallpaperManager.addOnColorsChangedListener(wallpaperColorsListener, mainHandler)
}
@@ -74,11 +70,6 @@
}
}
- override fun stop() {
- dumpManager.unregisterDumpable(javaClass.simpleName)
- wallpaperManager.removeOnColorsChangedListener(wallpaperColorsListener)
- }
-
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.println(
"""
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
new file mode 100644
index 0000000..2e3f0d0
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LetterboxModule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package com.android.systemui.statusbar.phone
+
+import com.android.systemui.CoreStartable
+import dagger.Binds
+import dagger.Module
+import dagger.multibindings.ClassKey
+import dagger.multibindings.IntoMap
+
+@Module
+abstract class LetterboxModule {
+ @Binds
+ @IntoMap
+ @ClassKey(LetterboxBackgroundProvider::class)
+ abstract fun bindFeature(impl: LetterboxBackgroundProvider): CoreStartable
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
index 0814ea5..c16877a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/LockscreenWallpaper.java
@@ -233,6 +233,11 @@
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
+ // TODO(b/273443374): remove
+ public boolean isLockscreenLiveWallpaperEnabled() {
+ return mWallpaperManager.isLockscreenLiveWallpaperEnabled();
+ }
+
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
pw.println(getClass().getSimpleName() + ":");
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
index 2027305..bb22365 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/SystemUIDialog.java
@@ -191,7 +191,7 @@
}
@Override
- protected void onStart() {
+ protected final void onStart() {
super.onStart();
if (mDismissReceiver != null) {
@@ -204,10 +204,18 @@
mDialogManager.setShowing(this, true);
mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, true)
.commitUpdate(mContext.getDisplayId());
+
+ start();
}
+ /**
+ * Called when {@link #onStart} is called. Subclasses wishing to override {@link #onStart()}
+ * should override this method instead.
+ */
+ protected void start() {}
+
@Override
- protected void onStop() {
+ protected final void onStop() {
super.onStop();
if (mDismissReceiver != null) {
@@ -218,8 +226,16 @@
mDialogManager.setShowing(this, false);
mSysUiState.setFlag(QuickStepContract.SYSUI_STATE_DIALOG_SHOWING, false)
.commitUpdate(mContext.getDisplayId());
+
+ stop();
}
+ /**
+ * Called when {@link #onStop} is called. Subclasses wishing to override {@link #onStop()}
+ * should override this method instead.
+ */
+ protected void stop() {}
+
public void setShowForAllUsers(boolean show) {
setShowForAllUsers(this, show);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
index b0532d7..f72e74b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/dagger/CentralSurfacesStartableModule.java
@@ -16,17 +16,15 @@
package com.android.systemui.statusbar.phone.dagger;
-import com.android.systemui.statusbar.phone.LetterboxAppearanceCalculator;
-import com.android.systemui.statusbar.phone.LetterboxBackgroundProvider;
import com.android.systemui.statusbar.phone.SystemBarAttributesListener;
-import java.util.Set;
-
import dagger.Binds;
import dagger.Module;
import dagger.multibindings.IntoSet;
import dagger.multibindings.Multibinds;
+import java.util.Set;
+
@Module
interface CentralSurfacesStartableModule {
@Multibinds
@@ -34,16 +32,6 @@
@Binds
@IntoSet
- CentralSurfacesComponent.Startable letterboxAppearanceCalculator(
- LetterboxAppearanceCalculator letterboxAppearanceCalculator);
-
- @Binds
- @IntoSet
CentralSurfacesComponent.Startable sysBarAttrsListener(
SystemBarAttributesListener systemBarAttributesListener);
-
- @Binds
- @IntoSet
- CentralSurfacesComponent.Startable letterboxBgProvider(
- LetterboxBackgroundProvider letterboxBackgroundProvider);
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
index e3ed2b4..db7fa14 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/CsdWarningDialog.java
@@ -186,8 +186,7 @@
}
@Override
- protected void onStart() {
- super.onStart();
+ protected void start() {
mShowTime = System.currentTimeMillis();
synchronized (mTimerLock) {
if (mNoUserActionRunnable != null) {
@@ -198,7 +197,7 @@
}
@Override
- protected void onStop() {
+ protected void stop() {
synchronized (mTimerLock) {
if (mCancelScheduledNoUserActionRunnable != null) {
mCancelScheduledNoUserActionRunnable.run();
diff --git a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
index 5b188b2..d42b964 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/SafetyWarningDialog.java
@@ -95,8 +95,7 @@
}
@Override
- protected void onStart() {
- super.onStart();
+ protected void start() {
mShowTime = System.currentTimeMillis();
}
diff --git a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
index 87a167b..96936e3 100644
--- a/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/volume/VolumePanelDialog.java
@@ -196,16 +196,14 @@
}
@Override
- protected void onStart() {
- super.onStart();
+ protected void start() {
Log.d(TAG, "onStart");
mLifecycleRegistry.setCurrentState(Lifecycle.State.STARTED);
mLifecycleRegistry.setCurrentState(Lifecycle.State.RESUMED);
}
@Override
- protected void onStop() {
- super.onStop();
+ protected void stop() {
Log.d(TAG, "onStop");
mLifecycleRegistry.setCurrentState(Lifecycle.State.DESTROYED);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index b4696e4..f914e75 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -170,24 +170,16 @@
}
@Test
- fun testFocusLossAfterRotating() {
+ fun testActionCancel_panelInteractionDetectorDisable() {
val container = initializeFingerprintContainer()
- waitForIdleSync()
-
- val requestID = authContainer?.requestId ?: 0L
-
- verify(callback).onDialogAnimatedIn(requestID)
- container.onOrientationChanged()
- container.onWindowFocusChanged(false)
- waitForIdleSync()
-
- verify(callback, never()).onDismissed(
- eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
- eq<ByteArray?>(null), /* credentialAttestation */
- eq(requestID)
+ container.mBiometricCallback.onAction(
+ AuthBiometricView.Callback.ACTION_USER_CANCELED
)
+ waitForIdleSync()
+ verify(panelInteractionDetector).disable()
}
+
@Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
index 86e2bd3..df6fa11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlsPopupMenuTest.kt
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner
import android.util.DisplayMetrics
import android.view.View
+import android.view.ViewGroup
import android.widget.PopupWindow.OnDismissListener
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.filters.SmallTest
@@ -29,13 +30,17 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.EmptyTestActivity
import com.android.systemui.util.mockito.whenever
+import com.android.systemui.widget.FakeListAdapter
+import com.android.systemui.widget.FakeListAdapter.FakeListAdapterItem
import com.google.common.truth.Truth.assertThat
+import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mock
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
@SmallTest
@RunWith(AndroidTestingRunner::class)
@@ -52,10 +57,16 @@
@Rule @JvmField val activityScenarioRule = ActivityScenarioRule(EmptyTestActivity::class.java)
- private val testDisplayMetrics: DisplayMetrics = DisplayMetrics()
+ private val testDisplayMetrics = DisplayMetrics()
+
+ @Before
+ fun setup() {
+ MockitoAnnotations.initMocks(this)
+ }
@Test
- fun testDismissListenerWorks() = testPopup { popupMenu ->
+ fun testDismissListenerWorks() = testPopup { activity, popupMenu ->
+ popupMenu.setAdapter(FakeListAdapter())
val listener = mock(OnDismissListener::class.java)
popupMenu.setOnDismissListener(listener)
popupMenu.show()
@@ -66,7 +77,9 @@
}
@Test
- fun testPopupDoesntExceedMaxWidth() = testPopup { popupMenu ->
+ fun testPopupDoesntExceedMaxWidth() = testPopup { activity, popupMenu ->
+ popupMenu.setAdapter(FakeListAdapter())
+ popupMenu.width = ViewGroup.LayoutParams.MATCH_PARENT
testDisplayMetrics.widthPixels = DISPLAY_WIDTH_WIDE
popupMenu.show()
@@ -75,7 +88,9 @@
}
@Test
- fun testPopupMarginsWidthLessMax() = testPopup { popupMenu ->
+ fun testPopupMarginsWidthLessMax() = testPopup { activity, popupMenu ->
+ popupMenu.setAdapter(FakeListAdapter())
+ popupMenu.width = ViewGroup.LayoutParams.MATCH_PARENT
testDisplayMetrics.widthPixels = DISPLAY_WIDTH_NARROW
popupMenu.show()
@@ -83,10 +98,32 @@
assertThat(popupMenu.width).isEqualTo(DISPLAY_WIDTH_NARROW - 2 * HORIZONTAL_MARGIN)
}
- private fun testPopup(test: (popup: ControlsPopupMenu) -> Unit) {
+ @Test
+ fun testWrapContentDoesntExceedMax() = testPopup { activity, popupMenu ->
+ popupMenu.setAdapter(
+ FakeListAdapter(
+ listOf(
+ FakeListAdapterItem({ _, _, _ ->
+ View(activity).apply { minimumWidth = MAX_WIDTH + 1 }
+ })
+ )
+ )
+ )
+ popupMenu.width = ViewGroup.LayoutParams.WRAP_CONTENT
+ testDisplayMetrics.widthPixels = DISPLAY_WIDTH_NARROW
+
+ popupMenu.show()
+
+ assertThat(popupMenu.width).isEqualTo(DISPLAY_WIDTH_NARROW - 2 * HORIZONTAL_MARGIN)
+ }
+
+ private fun testPopup(test: (activity: Activity, popup: ControlsPopupMenu) -> Unit) {
activityScenarioRule.scenario.onActivity { activity ->
val testActivity = setupActivity(activity)
- test(ControlsPopupMenu(testActivity).apply { anchorView = View(testActivity) })
+ test(
+ testActivity,
+ ControlsPopupMenu(testActivity).apply { anchorView = View(testActivity) }
+ )
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
index f21ea3d..fc75d47 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/DeviceEntryFaceAuthRepositoryTest.kt
@@ -44,11 +44,14 @@
import com.android.systemui.flags.Flags.FACE_AUTH_REFACTOR
import com.android.systemui.keyguard.domain.interactor.AlternateBouncerInteractor
import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
+import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.AuthenticationStatus
import com.android.systemui.keyguard.shared.model.DetectionStatus
import com.android.systemui.keyguard.shared.model.ErrorAuthenticationStatus
import com.android.systemui.keyguard.shared.model.HelpAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.SuccessAuthenticationStatus
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.keyguard.shared.model.WakeSleepReason
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.keyguard.shared.model.WakefulnessState
@@ -119,6 +122,7 @@
private lateinit var faceLockoutResetCallback: ArgumentCaptor<FaceManager.LockoutResetCallback>
private lateinit var testDispatcher: TestDispatcher
+ private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository
private lateinit var testScope: TestScope
private lateinit var fakeUserRepository: FakeUserRepository
private lateinit var authStatus: FlowValue<AuthenticationStatus?>
@@ -189,6 +193,9 @@
val systemClock = FakeSystemClock()
val faceAuthBuffer = TableLogBuffer(10, "face auth", systemClock)
val faceDetectBuffer = TableLogBuffer(10, "face detect", systemClock)
+ keyguardTransitionRepository = FakeKeyguardTransitionRepository()
+ val keyguardTransitionInteractor =
+ KeyguardTransitionInteractor(keyguardTransitionRepository)
return DeviceEntryFaceAuthRepositoryImpl(
mContext,
fmOverride,
@@ -207,6 +214,7 @@
alternateBouncerInteractor,
faceDetectBuffer,
faceAuthBuffer,
+ keyguardTransitionInteractor,
dumpManager,
)
}
@@ -772,6 +780,50 @@
}
}
+ @Test
+ fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromDozing() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.DOZING, to = KeyguardState.GONE)
+ )
+
+ runCurrent()
+ verify(faceManager).scheduleWatchdog()
+ }
+
+ @Test
+ fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromAod() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.AOD, to = KeyguardState.GONE)
+ )
+
+ runCurrent()
+ verify(faceManager).scheduleWatchdog()
+ }
+
+ @Test
+ fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromLockscreen() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.LOCKSCREEN, to = KeyguardState.GONE)
+ )
+
+ runCurrent()
+ verify(faceManager).scheduleWatchdog()
+ }
+
+ @Test
+ fun schedulesFaceManagerWatchdogWhenKeyguardIsGoneFromBouncer() =
+ testScope.runTest {
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(from = KeyguardState.PRIMARY_BOUNCER, to = KeyguardState.GONE)
+ )
+
+ runCurrent()
+ verify(faceManager).scheduleWatchdog()
+ }
+
private suspend fun TestScope.testGatingCheckForFaceAuth(gatingCheckModifier: () -> Unit) {
initCollectors()
allPreconditionsToRunFaceAuthAreTrue()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
new file mode 100644
index 0000000..86f3062
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/controls/util/MediaDataUtilsTest.kt
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.media.controls.util
+
+import android.testing.AndroidTestingRunner
+import android.util.Pair as APair
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class MediaDataUtilsTest : SysuiTestCase() {
+
+ @Test
+ fun testScaleFactor_zeroInput_returnsZero() {
+ val input = APair(0, 0)
+ val target = APair(100, 100)
+
+ val scale = MediaDataUtils.getScaleFactor(input, target)
+ assertThat(scale).isEqualTo(0f)
+ }
+
+ @Test
+ fun testScaleFactor_tooWide_scaleDown() {
+ val input = APair(400, 200)
+ val target = APair(100, 100)
+
+ val scale = MediaDataUtils.getScaleFactor(input, target)
+ assertThat(scale).isEqualTo(0.5f)
+ }
+
+ @Test
+ fun testScaleFactor_tooTall_scaleDown() {
+ val input = APair(200, 400)
+ val target = APair(100, 100)
+
+ val scale = MediaDataUtils.getScaleFactor(input, target)
+ assertThat(scale).isEqualTo(0.5f)
+ }
+
+ @Test
+ fun testScaleFactor_lessWide_scaleUp() {
+ val input = APair(50, 100)
+ val target = APair(100, 100)
+
+ val scale = MediaDataUtils.getScaleFactor(input, target)
+ assertThat(scale).isEqualTo(2f)
+ }
+
+ @Test
+ fun testScaleFactor_lessTall_scaleUp() {
+ val input = APair(100, 50)
+ val target = APair(100, 100)
+
+ val scale = MediaDataUtils.getScaleFactor(input, target)
+ assertThat(scale).isEqualTo(2f)
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
index 9a0bd9e..f206409 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/dialog/MediaOutputBaseDialogTest.java
@@ -255,10 +255,10 @@
mLocalBluetoothLeBroadcast);
mIsBroadcasting = true;
- mMediaOutputBaseDialogImpl.onStart();
+ mMediaOutputBaseDialogImpl.start();
verify(mLocalBluetoothLeBroadcast).registerServiceCallBack(any(), any());
- mMediaOutputBaseDialogImpl.onStop();
+ mMediaOutputBaseDialogImpl.stop();
verify(mLocalBluetoothLeBroadcast).unregisterServiceCallBack(any());
}
@@ -269,8 +269,8 @@
mLocalBluetoothLeBroadcast);
mIsBroadcasting = false;
- mMediaOutputBaseDialogImpl.onStart();
- mMediaOutputBaseDialogImpl.onStop();
+ mMediaOutputBaseDialogImpl.start();
+ mMediaOutputBaseDialogImpl.stop();
verify(mLocalBluetoothLeBroadcast, never()).registerServiceCallBack(any(), any());
verify(mLocalBluetoothLeBroadcast, never()).unregisterServiceCallBack(any());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
index ba29ca5..22a5b21 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskControllerTest.kt
@@ -63,8 +63,10 @@
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
+import org.mockito.Mockito.doNothing
import org.mockito.Mockito.isNull
import org.mockito.Mockito.never
+import org.mockito.Mockito.spy
import org.mockito.Mockito.verify
import org.mockito.Mockito.verifyZeroInteractions
import org.mockito.MockitoAnnotations
@@ -75,7 +77,9 @@
internal class NoteTaskControllerTest : SysuiTestCase() {
@Mock private lateinit var context: Context
+ @Mock private lateinit var workProfileContext: Context
@Mock private lateinit var packageManager: PackageManager
+ @Mock private lateinit var workProfilePackageManager: PackageManager
@Mock private lateinit var resolver: NoteTaskInfoResolver
@Mock private lateinit var bubbles: Bubbles
@Mock private lateinit var keyguardManager: KeyguardManager
@@ -107,6 +111,7 @@
.thenReturn(listOf(NOTE_TASK_PACKAGE_NAME))
whenever(activityManager.getRunningTasks(anyInt())).thenReturn(emptyList())
whenever(userManager.isManagedProfile(workUserInfo.id)).thenReturn(true)
+ whenever(context.resources).thenReturn(getContext().resources)
}
private fun createNoteTaskController(
@@ -337,14 +342,14 @@
}
@Test
- fun showNoteTask_intentResolverReturnsNull_shouldDoNothing() {
+ fun showNoteTask_intentResolverReturnsNull_shouldShowToast() {
whenever(resolver.resolveInfo(any(), any())).thenReturn(null)
+ val noteTaskController = spy(createNoteTaskController())
+ doNothing().whenever(noteTaskController).showNoDefaultNotesAppToast()
- createNoteTaskController()
- .showNoteTask(
- entryPoint = NoteTaskEntryPoint.TAIL_BUTTON,
- )
+ noteTaskController.showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
+ verify(noteTaskController).showNoDefaultNotesAppToast()
verifyZeroInteractions(context, bubbles, eventLogger)
}
@@ -373,17 +378,17 @@
@Test
fun showNoteTask_keyboardShortcut_shouldStartActivity() {
val expectedInfo =
- NOTE_TASK_INFO.copy(
- entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
- isKeyguardLocked = true,
- )
+ NOTE_TASK_INFO.copy(
+ entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT,
+ isKeyguardLocked = true,
+ )
whenever(keyguardManager.isKeyguardLocked).thenReturn(expectedInfo.isKeyguardLocked)
whenever(resolver.resolveInfo(any(), any())).thenReturn(expectedInfo)
createNoteTaskController()
- .showNoteTask(
- entryPoint = expectedInfo.entryPoint!!,
- )
+ .showNoteTask(
+ entryPoint = expectedInfo.entryPoint!!,
+ )
val intentCaptor = argumentCaptor<Intent>()
val userCaptor = argumentCaptor<UserHandle>()
@@ -393,9 +398,9 @@
assertThat(intent.`package`).isEqualTo(NOTE_TASK_PACKAGE_NAME)
assertThat(intent.flags and FLAG_ACTIVITY_NEW_TASK).isEqualTo(FLAG_ACTIVITY_NEW_TASK)
assertThat(intent.flags and FLAG_ACTIVITY_MULTIPLE_TASK)
- .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
+ .isEqualTo(FLAG_ACTIVITY_MULTIPLE_TASK)
assertThat(intent.flags and FLAG_ACTIVITY_NEW_DOCUMENT)
- .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
+ .isEqualTo(FLAG_ACTIVITY_NEW_DOCUMENT)
assertThat(intent.getBooleanExtra(Intent.EXTRA_USE_STYLUS_MODE, true)).isFalse()
}
assertThat(userCaptor.value).isEqualTo(userTracker.userHandle)
@@ -407,7 +412,7 @@
// region setNoteTaskShortcutEnabled
@Test
fun setNoteTaskShortcutEnabled_setTrue() {
- createNoteTaskController().setNoteTaskShortcutEnabled(value = true)
+ createNoteTaskController().setNoteTaskShortcutEnabled(value = true, userTracker.userHandle)
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -422,7 +427,7 @@
@Test
fun setNoteTaskShortcutEnabled_setFalse() {
- createNoteTaskController().setNoteTaskShortcutEnabled(value = false)
+ createNoteTaskController().setNoteTaskShortcutEnabled(value = false, userTracker.userHandle)
val argument = argumentCaptor<ComponentName>()
verify(context.packageManager)
@@ -434,6 +439,47 @@
assertThat(argument.value.className)
.isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
}
+
+ @Test
+ fun setNoteTaskShortcutEnabled_workProfileUser_setTrue() {
+ whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any()))
+ .thenReturn(workProfileContext)
+ whenever(workProfileContext.packageManager).thenReturn(workProfilePackageManager)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController().setNoteTaskShortcutEnabled(value = true, workUserInfo.userHandle)
+
+ val argument = argumentCaptor<ComponentName>()
+ verify(workProfilePackageManager)
+ .setComponentEnabledSetting(
+ argument.capture(),
+ eq(COMPONENT_ENABLED_STATE_ENABLED),
+ eq(PackageManager.DONT_KILL_APP),
+ )
+ assertThat(argument.value.className)
+ .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ }
+
+ @Test
+ fun setNoteTaskShortcutEnabled_workProfileUser_setFalse() {
+ whenever(context.createContextAsUser(eq(workUserInfo.userHandle), any()))
+ .thenReturn(workProfileContext)
+ whenever(workProfileContext.packageManager).thenReturn(workProfilePackageManager)
+ userTracker.set(mainAndWorkProfileUsers, mainAndWorkProfileUsers.indexOf(mainUserInfo))
+
+ createNoteTaskController()
+ .setNoteTaskShortcutEnabled(value = false, workUserInfo.userHandle)
+
+ val argument = argumentCaptor<ComponentName>()
+ verify(workProfilePackageManager)
+ .setComponentEnabledSetting(
+ argument.capture(),
+ eq(COMPONENT_ENABLED_STATE_DISABLED),
+ eq(PackageManager.DONT_KILL_APP),
+ )
+ assertThat(argument.value.className)
+ .isEqualTo(CreateNoteTaskShortcutActivity::class.java.name)
+ }
// endregion
// region keyguard policy
diff --git a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
index ec4daee..28ed9d2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/notetask/NoteTaskInitializerTest.kt
@@ -20,9 +20,11 @@
import android.view.KeyEvent
import androidx.test.runner.AndroidJUnit4
import com.android.systemui.SysuiTestCase
+import com.android.systemui.settings.FakeUserTracker
import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.android.wm.shell.bubbles.Bubbles
import java.util.Optional
@@ -46,6 +48,7 @@
@Mock lateinit var roleManager: RoleManager
private val clock = FakeSystemClock()
private val executor = FakeExecutor(clock)
+ private val userTracker = FakeUserTracker()
@Before
fun setUp() {
@@ -63,6 +66,7 @@
isEnabled = isEnabled,
roleManager = roleManager,
backgroundExecutor = executor,
+ userTracker = userTracker,
)
}
@@ -71,7 +75,7 @@
fun initialize() {
createNoteTaskInitializer().initialize()
- verify(controller).setNoteTaskShortcutEnabled(true)
+ verify(controller).setNoteTaskShortcutEnabled(eq(true), eq(userTracker.userHandle))
verify(commandQueue).addCallback(any())
verify(roleManager).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
}
@@ -80,7 +84,7 @@
fun initialize_flagDisabled() {
createNoteTaskInitializer(isEnabled = false).initialize()
- verify(controller, never()).setNoteTaskShortcutEnabled(any())
+ verify(controller, never()).setNoteTaskShortcutEnabled(any(), any())
verify(commandQueue, never()).addCallback(any())
verify(roleManager, never()).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
}
@@ -89,7 +93,7 @@
fun initialize_bubblesNotPresent() {
createNoteTaskInitializer(bubbles = null).initialize()
- verify(controller, never()).setNoteTaskShortcutEnabled(any())
+ verify(controller, never()).setNoteTaskShortcutEnabled(any(), any())
verify(commandQueue, never()).addCallback(any())
verify(roleManager, never()).addOnRoleHoldersChangedListenerAsUser(any(), any(), any())
}
@@ -98,24 +102,36 @@
// region handleSystemKey
@Test
fun handleSystemKey_receiveValidSystemKey_shouldShowNoteTask() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
+ createNoteTaskInitializer()
+ .callbacks
+ .handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_STYLUS_BUTTON_TAIL))
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.TAIL_BUTTON)
}
@Test
fun handleSystemKey_receiveKeyboardShortcut_shouldShowNoteTask() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(0, 0, KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_N, 0, KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON))
+ createNoteTaskInitializer()
+ .callbacks
+ .handleSystemKey(
+ KeyEvent(
+ 0,
+ 0,
+ KeyEvent.ACTION_DOWN,
+ KeyEvent.KEYCODE_N,
+ 0,
+ KeyEvent.META_META_ON or KeyEvent.META_CTRL_ON
+ )
+ )
verify(controller).showNoteTask(entryPoint = NoteTaskEntryPoint.KEYBOARD_SHORTCUT)
}
-
+
@Test
fun handleSystemKey_receiveInvalidSystemKey_shouldDoNothing() {
- createNoteTaskInitializer().callbacks.handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN,
- KeyEvent.KEYCODE_UNKNOWN))
+ createNoteTaskInitializer()
+ .callbacks
+ .handleSystemKey(KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_UNKNOWN))
verifyZeroInteractions(controller)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
index 4bfd6a2..068d933 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java
@@ -303,6 +303,7 @@
protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener>
mEmptySpaceClickListenerCaptor;
@Mock protected ActivityStarter mActivityStarter;
+ @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor;
protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor;
protected KeyguardInteractor mKeyguardInteractor;
@@ -600,7 +601,8 @@
mDumpManager,
mKeyuardLongPressViewModel,
mKeyguardInteractor,
- mActivityStarter);
+ mActivityStarter,
+ mKeyguardFaceAuthInteractor);
mNotificationPanelViewController.initDependencies(
mCentralSurfaces,
null,
@@ -667,7 +669,7 @@
mFeatureFlags,
mInteractionJankMonitor,
mShadeLog,
- mock(KeyguardFaceAuthInteractor.class)
+ mKeyguardFaceAuthInteractor
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 2db9c97..600fb5c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -930,6 +930,7 @@
mTouchHandler.onTouch(mock(View.class), mDownMotionEvent);
mEmptySpaceClickListenerCaptor.getValue().onEmptySpaceClicked(0, 0);
+ verify(mKeyguardFaceAuthInteractor).onNotificationPanelClicked();
verify(mUpdateMonitor).requestFaceAuth(
FaceAuthApiRequestReason.NOTIFICATION_PANEL_CLICKED);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
index b043e97..76f7401 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ShadeHeaderControllerTest.kt
@@ -16,6 +16,8 @@
package com.android.systemui.shade
import android.animation.Animator
+import android.app.AlarmManager
+import android.app.PendingIntent
import android.app.StatusBarManager
import android.content.Context
import android.content.res.Resources
@@ -40,8 +42,10 @@
import com.android.systemui.demomode.DemoMode
import com.android.systemui.demomode.DemoModeController
import com.android.systemui.dump.DumpManager
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.qs.ChipVisibilityListener
import com.android.systemui.qs.HeaderPrivacyIconsController
+import com.android.systemui.shade.ShadeHeaderController.Companion.DEFAULT_CLOCK_INTENT
import com.android.systemui.shade.ShadeHeaderController.Companion.LARGE_SCREEN_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QQS_HEADER_CONSTRAINT
import com.android.systemui.shade.ShadeHeaderController.Companion.QS_HEADER_CONSTRAINT
@@ -52,6 +56,7 @@
import com.android.systemui.statusbar.phone.StatusIconContainer
import com.android.systemui.statusbar.policy.Clock
import com.android.systemui.statusbar.policy.FakeConfigurationController
+import com.android.systemui.statusbar.policy.NextAlarmController
import com.android.systemui.statusbar.policy.VariableDateView
import com.android.systemui.statusbar.policy.VariableDateViewController
import com.android.systemui.util.mockito.any
@@ -114,6 +119,8 @@
@Mock private lateinit var demoModeController: DemoModeController
@Mock private lateinit var qsBatteryModeController: QsBatteryModeController
+ @Mock private lateinit var nextAlarmController: NextAlarmController
+ @Mock private lateinit var activityStarter: ActivityStarter
@JvmField @Rule val mockitoRule = MockitoJUnit.rule()
var viewVisibility = View.GONE
@@ -181,6 +188,8 @@
combinedShadeHeadersConstraintManager,
demoModeController,
qsBatteryModeController,
+ nextAlarmController,
+ activityStarter,
)
whenever(view.isAttachedToWindow).thenReturn(true)
shadeHeaderController.init()
@@ -828,6 +837,28 @@
verify(carrierGroup).setPaddingRelative(514, 0, 0, 0)
}
+ @Test
+ fun launchClock_launchesDefaultIntentWhenNoAlarmSet() {
+ shadeHeaderController.launchClockActivity()
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(DEFAULT_CLOCK_INTENT, 0)
+ }
+
+ @Test
+ fun launchClock_launchesNextAlarmWhenExists() {
+ val pendingIntent = mock<PendingIntent>()
+ val aci = AlarmManager.AlarmClockInfo(12345, pendingIntent)
+ val captor =
+ ArgumentCaptor.forClass(NextAlarmController.NextAlarmChangeCallback::class.java)
+
+ verify(nextAlarmController).addCallback(capture(captor))
+ captor.value.onNextAlarmChanged(aci)
+
+ shadeHeaderController.launchClockActivity()
+
+ verify(activityStarter).postStartActivityDismissingKeyguard(pendingIntent)
+ }
+
private fun View.executeLayoutChange(
left: Int,
top: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
index 8109e24..c2a2a40 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/KeyguardCoordinatorTest.kt
@@ -25,6 +25,10 @@
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.advanceTimeBy
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
+import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.statusbar.StatusBarState
import com.android.systemui.statusbar.notification.NotifPipelineFlags
@@ -69,6 +73,7 @@
private val headsUpManager: HeadsUpManager = mock()
private val keyguardNotifVisibilityProvider: KeyguardNotificationVisibilityProvider = mock()
private val keyguardRepository = FakeKeyguardRepository()
+ private val keyguardTransitionRepository = FakeKeyguardTransitionRepository()
private val notifPipelineFlags: NotifPipelineFlags = mock()
private val notifPipeline: NotifPipeline = mock()
private val sectionHeaderVisibilityProvider: SectionHeaderVisibilityProvider = mock()
@@ -118,6 +123,33 @@
}
@Test
+ fun unseenFilterStopsMarkingSeenNotifWhenTransitionToAod() {
+ whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
+
+ // GIVEN: Keyguard is not showing, shade is not expanded, and a notification is present
+ keyguardRepository.setKeyguardShowing(false)
+ whenever(statusBarStateController.isExpanded).thenReturn(false)
+ runKeyguardCoordinatorTest {
+ val fakeEntry = NotificationEntryBuilder().build()
+ collectionListener.onEntryAdded(fakeEntry)
+
+ // WHEN: The device transitions to AOD
+ keyguardTransitionRepository.sendTransitionStep(
+ TransitionStep(to = KeyguardState.AOD, transitionState = TransitionState.STARTED),
+ )
+ testScheduler.runCurrent()
+
+ // WHEN: The shade is expanded
+ whenever(statusBarStateController.isExpanded).thenReturn(true)
+ statusBarStateListener.onExpandedChanged(true)
+ testScheduler.runCurrent()
+
+ // THEN: The notification is still treated as "unseen" and is not filtered out.
+ assertThat(unseenFilter.shouldFilterOut(fakeEntry, 0L)).isFalse()
+ }
+ }
+
+ @Test
fun unseenFilter_headsUpMarkedAsSeen() {
whenever(notifPipelineFlags.shouldFilterUnseenNotifsOnKeyguard).thenReturn(true)
@@ -373,6 +405,7 @@
headsUpManager,
keyguardNotifVisibilityProvider,
keyguardRepository,
+ keyguardTransitionRepository,
notifPipelineFlags,
testScope.backgroundScope,
sectionHeaderVisibilityProvider,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index eb0b9b3..760a90b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -54,6 +54,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.NotificationPanelViewController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
import com.android.systemui.statusbar.policy.BatteryController;
@@ -120,6 +121,8 @@
@Mock private CommandQueue mCommandQueue;
@Mock private KeyguardLogger mLogger;
+ @Mock private NotificationMediaManager mNotificationMediaManager;
+
private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
private KeyguardStatusBarViewController mController;
@@ -167,7 +170,8 @@
mSecureSettings,
mCommandQueue,
mFakeExecutor,
- mLogger
+ mLogger,
+ mNotificationMediaManager
);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
index a2828d33..1cc0bd3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/LetterboxBackgroundProviderTest.kt
@@ -25,7 +25,6 @@
import android.view.IWindowManager
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.dump.DumpManager
import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
@@ -52,7 +51,6 @@
@get:Rule var expect: Expect = Expect.create()
@Mock private lateinit var windowManager: IWindowManager
- @Mock private lateinit var dumpManager: DumpManager
@Mock private lateinit var wallpaperManager: WallpaperManager
private lateinit var provider: LetterboxBackgroundProvider
@@ -65,8 +63,7 @@
setUpWallpaperManager()
provider =
- LetterboxBackgroundProvider(
- windowManager, fakeExecutor, dumpManager, wallpaperManager, mainHandler)
+ LetterboxBackgroundProvider(windowManager, fakeExecutor, wallpaperManager, mainHandler)
}
private fun setUpWallpaperManager() {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
index 6c0f6c2..07ffd11 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/SystemUIDialogTest.java
@@ -14,6 +14,8 @@
package com.android.systemui.statusbar.phone;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
@@ -44,6 +46,8 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.concurrent.atomic.AtomicBoolean;
+
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
@@ -109,4 +113,31 @@
dialog.dismiss();
assertFalse(dialog.isShowing());
}
+
+ @Test public void startAndStopAreCalled() {
+ AtomicBoolean calledStart = new AtomicBoolean(false);
+ AtomicBoolean calledStop = new AtomicBoolean(false);
+ SystemUIDialog dialog = new SystemUIDialog(mContext) {
+ @Override
+ protected void start() {
+ calledStart.set(true);
+ }
+
+ @Override
+ protected void stop() {
+ calledStop.set(true);
+ }
+ };
+
+ assertThat(calledStart.get()).isFalse();
+ assertThat(calledStop.get()).isFalse();
+
+ dialog.show();
+ assertThat(calledStart.get()).isTrue();
+ assertThat(calledStop.get()).isFalse();
+
+ dialog.dismiss();
+ assertThat(calledStart.get()).isTrue();
+ assertThat(calledStop.get()).isTrue();
+ }
}
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/widget/FakeListAdapter.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/widget/FakeListAdapter.kt
new file mode 100644
index 0000000..231373b
--- /dev/null
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/widget/FakeListAdapter.kt
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.widget
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.BaseAdapter
+
+class FakeListAdapter(private var items: List<FakeListAdapterItem> = emptyList()) : BaseAdapter() {
+
+ fun setItems(items: List<FakeListAdapterItem>) {
+ this.items = items
+ notifyDataSetChanged()
+ }
+
+ override fun getCount(): Int = items.size
+
+ override fun getItem(position: Int): Any = items[position].data
+
+ override fun getItemId(position: Int): Long = items[position].id
+
+ override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View =
+ items[position].view(position, convertView, parent)
+
+ class FakeListAdapterItem(
+ /** Result returned in [Adapter#getView] */
+ val view: (position: Int, convertView: View?, parent: ViewGroup?) -> View,
+ /** Returned in [Adapter#getItemId] */
+ val id: Long = 0,
+ /** Returned in [Adapter#getItem] */
+ val data: Any = Unit,
+ )
+}
diff --git a/services/core/java/android/os/BatteryStatsInternal.java b/services/core/java/android/os/BatteryStatsInternal.java
index c6f63dd..12ee131 100644
--- a/services/core/java/android/os/BatteryStatsInternal.java
+++ b/services/core/java/android/os/BatteryStatsInternal.java
@@ -39,12 +39,14 @@
public static final int CPU_WAKEUP_SUBSYSTEM_UNKNOWN = -1;
public static final int CPU_WAKEUP_SUBSYSTEM_ALARM = 1;
public static final int CPU_WAKEUP_SUBSYSTEM_WIFI = 2;
+ public static final int CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER = 3;
/** @hide */
@IntDef(prefix = {"CPU_WAKEUP_SUBSYSTEM_"}, value = {
CPU_WAKEUP_SUBSYSTEM_UNKNOWN,
CPU_WAKEUP_SUBSYSTEM_ALARM,
CPU_WAKEUP_SUBSYSTEM_WIFI,
+ CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
})
@Retention(RetentionPolicy.SOURCE)
@interface CpuWakeupSubsystem {
diff --git a/services/core/java/com/android/server/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index 3487613..5156c54 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -70,7 +70,7 @@
private boolean mUpdatesStopped;
- private final boolean mKeepDreamingWhenUndocking;
+ private final boolean mKeepDreamingWhenUnplugging;
private final boolean mAllowTheaterModeWakeFromDock;
private final List<ExtconStateConfig> mExtconStateConfigs;
@@ -167,8 +167,8 @@
mWakeLock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
mAllowTheaterModeWakeFromDock = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
- mKeepDreamingWhenUndocking = context.getResources().getBoolean(
- com.android.internal.R.bool.config_keepDreamingWhenUndocking);
+ mKeepDreamingWhenUnplugging = context.getResources().getBoolean(
+ com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
mDeviceProvisionedObserver = new DeviceProvisionedObserver(mHandler);
mExtconStateConfigs = loadExtconStateConfigs(context);
@@ -237,7 +237,7 @@
}
private boolean allowWakeFromDock() {
- if (mKeepDreamingWhenUndocking) {
+ if (mKeepDreamingWhenUnplugging) {
return false;
}
return (mAllowTheaterModeWakeFromDock
diff --git a/services/core/java/com/android/server/OWNERS b/services/core/java/com/android/server/OWNERS
index 409f054..123cd328 100644
--- a/services/core/java/com/android/server/OWNERS
+++ b/services/core/java/com/android/server/OWNERS
@@ -27,6 +27,7 @@
per-file **IpSec* = file:/services/core/java/com/android/server/vcn/OWNERS
per-file *Location* = file:/services/core/java/com/android/server/location/OWNERS
per-file *Network* = file:/services/core/java/com/android/server/net/OWNERS
+per-file *SoundTrigger* = file:/media/java/android/media/soundtrigger/OWNERS
per-file *Storage* = file:/core/java/android/os/storage/OWNERS
per-file *TimeUpdate* = file:/services/core/java/com/android/server/timezonedetector/OWNERS
per-file DynamicSystemService.java = file:/packages/DynamicSystemInstallationService/OWNERS
diff --git a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
index e080a80..17a0d62 100644
--- a/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
+++ b/services/core/java/com/android/server/am/ActivityManagerShellCommand.java
@@ -247,6 +247,10 @@
return runSendBroadcast(pw);
case "compact":
return runCompact(pw);
+ case "freeze":
+ return runFreeze(pw);
+ case "unfreeze":
+ return runUnfreeze(pw);
case "instrument":
getOutPrintWriter().println("Error: must be invoked through 'am instrument'.");
return -1;
@@ -1074,20 +1078,10 @@
boolean isFullCompact = op.equals("full");
boolean isSomeCompact = op.equals("some");
if (isFullCompact || isSomeCompact) {
- String processName = getNextArgRequired();
- synchronized (mInternal.mProcLock) {
- // Default to current user
- int userId = mInterface.getCurrentUserId();
- String userOpt = getNextOption();
- if (userOpt != null && "--user".equals(userOpt)) {
- int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
- if (inputUserId != UserHandle.USER_CURRENT) {
- userId = inputUserId;
- }
- }
- final int uid =
- mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
- app = mInternal.getProcessRecordLocked(processName, uid);
+ app = getProcessFromShell();
+ if (app == null) {
+ getErrPrintWriter().println("Error: could not find process");
+ return -1;
}
pw.println("Process record found pid: " + app.mPid);
if (isFullCompact) {
@@ -1143,6 +1137,93 @@
return 0;
}
+ @NeverCompile
+ int runFreeze(PrintWriter pw) throws RemoteException {
+ String freezerOpt = getNextOption();
+ boolean isSticky = false;
+ if (freezerOpt != null) {
+ isSticky = freezerOpt.equals("--sticky");
+ }
+ ProcessRecord app = getProcessFromShell();
+ if (app == null) {
+ getErrPrintWriter().println("Error: could not find process");
+ return -1;
+ }
+ pw.println("Freezing pid: " + app.mPid + " sticky=" + isSticky);
+ synchronized (mInternal) {
+ synchronized (mInternal.mProcLock) {
+ app.mOptRecord.setFreezeSticky(isSticky);
+ mInternal.mOomAdjuster.mCachedAppOptimizer.freezeAppAsyncInternalLSP(app, 0, true);
+ }
+ }
+ return 0;
+ }
+
+ @NeverCompile
+ int runUnfreeze(PrintWriter pw) throws RemoteException {
+ String freezerOpt = getNextOption();
+ boolean isSticky = false;
+ if (freezerOpt != null) {
+ isSticky = freezerOpt.equals("--sticky");
+ }
+ ProcessRecord app = getProcessFromShell();
+ if (app == null) {
+ getErrPrintWriter().println("Error: could not find process");
+ return -1;
+ }
+ pw.println("Unfreezing pid: " + app.mPid);
+ synchronized (mInternal) {
+ synchronized (mInternal.mProcLock) {
+ synchronized (mInternal.mOomAdjuster.mCachedAppOptimizer.mFreezerLock) {
+ app.mOptRecord.setFreezeSticky(isSticky);
+ mInternal.mOomAdjuster.mCachedAppOptimizer.unfreezeAppInternalLSP(app, 0,
+ false);
+ }
+ }
+ }
+ return 0;
+ }
+
+ /**
+ * Parses from the shell the process name and user id if provided and provides the corresponding
+ * {@link ProcessRecord)} If no user is provided, it will fallback to current user.
+ * Example usage: {@code <processname> --user current} or {@code <processname>}
+ * @return process record of process, null if none found.
+ * @throws RemoteException
+ */
+ @NeverCompile
+ ProcessRecord getProcessFromShell() throws RemoteException {
+ ProcessRecord app;
+ String processName = getNextArgRequired();
+ synchronized (mInternal.mProcLock) {
+ // Default to current user
+ int userId = getUserIdFromShellOrFallback();
+ final int uid =
+ mInternal.getPackageManagerInternal().getPackageUid(processName, 0, userId);
+ app = mInternal.getProcessRecordLocked(processName, uid);
+ }
+ return app;
+ }
+
+ /**
+ * @return User id from command line provided in the form of
+ * {@code --user <userid|current|all>} and if the argument is not found it will fallback
+ * to current user.
+ * @throws RemoteException
+ */
+ @NeverCompile
+ int getUserIdFromShellOrFallback() throws RemoteException {
+ int userId = mInterface.getCurrentUserId();
+ String userOpt = getNextOption();
+ if (userOpt != null && "--user".equals(userOpt)) {
+ int inputUserId = UserHandle.parseUserArg(getNextArgRequired());
+ if (inputUserId != UserHandle.USER_CURRENT) {
+ userId = inputUserId;
+ }
+ }
+ return userId;
+ }
+
int runDumpHeap(PrintWriter pw) throws RemoteException {
final PrintWriter err = getErrPrintWriter();
boolean managed = true;
@@ -4061,6 +4142,14 @@
pw.println(" Perform a native compaction for process with <pid>.");
pw.println(" some: execute file compaction.");
pw.println(" full: execute anon + file compaction.");
+ pw.println(" freeze [--sticky] <processname> [--user <USER_ID>]");
+ pw.println(" Freeze a process.");
+ pw.println(" --sticky: persists the frozen state for the process lifetime or");
+ pw.println(" until an unfreeze is triggered via shell");
+ pw.println(" unfreeze [--sticky] <processname> [--user <USER_ID>]");
+ pw.println(" Unfreeze a process.");
+ pw.println(" --sticky: persists the unfrozen state for the process lifetime or");
+ pw.println(" until a freeze is triggered via shell");
pw.println(" instrument [-r] [-e <NAME> <VALUE>] [-p <FILE>] [-w]");
pw.println(" [--user <USER_ID> | current]");
pw.println(" [--no-hidden-api-checks [--no-test-api-access]]");
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index ed297d0..0744f75 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -108,8 +108,8 @@
import com.android.server.power.stats.BatteryStatsImpl;
import com.android.server.power.stats.BatteryUsageStatsProvider;
import com.android.server.power.stats.BatteryUsageStatsStore;
-import com.android.server.power.stats.CpuWakeupStats;
import com.android.server.power.stats.SystemServerCpuThreadReader.SystemServiceCpuThreadTimes;
+import com.android.server.power.stats.wakeups.CpuWakeupStats;
import java.io.File;
import java.io.FileDescriptor;
@@ -515,13 +515,11 @@
@Override
public void noteCpuWakingActivity(int subsystem, long elapsedMillis, int... uids) {
Objects.requireNonNull(uids);
- mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids);
+ mHandler.post(() -> mCpuWakeupStats.noteWakingActivity(subsystem, elapsedMillis, uids));
}
-
@Override
public void noteWakingSoundTrigger(long elapsedMillis, int uid) {
- // TODO(b/267717665): Pipe to noteCpuWakingActivity once SoundTrigger starts using this.
- Slog.w(TAG, "Sound trigger event dispatched to uid " + uid);
+ noteCpuWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, elapsedMillis, uid);
}
}
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 8ad76cb..1426cfd 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -342,7 +342,7 @@
private final ActivityManagerGlobalLock mProcLock;
- private final Object mFreezerLock = new Object();
+ public final Object mFreezerLock = new Object();
private final OnPropertiesChangedListener mOnFlagsChangedListener =
new OnPropertiesChangedListener() {
@@ -763,8 +763,9 @@
pw.println(" Apps frozen: " + size);
for (int i = 0; i < size; i++) {
ProcessRecord app = mFrozenProcesses.valueAt(i);
- pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime()
- + ": " + app.getPid() + " " + app.processName);
+ pw.println(" " + app.mOptRecord.getFreezeUnfreezeTime() + ": " + app.getPid()
+ + " " + app.processName
+ + (app.mOptRecord.isFreezeSticky() ? " (sticky)" : ""));
}
if (!mPendingCompactionProcesses.isEmpty()) {
@@ -1283,12 +1284,26 @@
@GuardedBy({"mAm", "mProcLock"})
void freezeAppAsyncLSP(ProcessRecord app) {
+ freezeAppAsyncInternalLSP(app, mFreezerDebounceTimeout, false);
+ }
+
+ @GuardedBy({"mAm", "mProcLock"})
+ void freezeAppAsyncInternalLSP(ProcessRecord app, long delayMillis, boolean force) {
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
if (opt.isPendingFreeze()) {
// Skip redundant DO_FREEZE message
return;
}
+ if (opt.isFreezeSticky() && !force) {
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM,
+ "Skip freezing because unfrozen state is sticky pid=" + app.getPid() + " "
+ + app.processName);
+ }
+ return;
+ }
+
if (mAm.mConstants.USE_MODERN_TRIM
&& app.mState.getSetAdj() >= ProcessList.CACHED_APP_MIN_ADJ) {
final IApplicationThread thread = app.getThread();
@@ -1301,9 +1316,8 @@
}
}
mFreezeHandler.sendMessageDelayed(
- mFreezeHandler.obtainMessage(
- SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
- mFreezerDebounceTimeout);
+ mFreezeHandler.obtainMessage(SET_FROZEN_PROCESS_MSG, DO_FREEZE, 0, app),
+ delayMillis);
opt.setPendingFreeze(true);
if (DEBUG_FREEZER) {
Slog.d(TAG_AM, "Async freezing " + app.getPid() + " " + app.processName);
@@ -1311,9 +1325,19 @@
}
@GuardedBy({"mAm", "mProcLock", "mFreezerLock"})
- void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason) {
+ void unfreezeAppInternalLSP(ProcessRecord app, @UnfreezeReason int reason, boolean force) {
final int pid = app.getPid();
final ProcessCachedOptimizerRecord opt = app.mOptRecord;
+ boolean sticky = opt.isFreezeSticky();
+ if (sticky && !force) {
+ // Sticky freezes will not change their state unless forced out of it.
+ if (DEBUG_FREEZER) {
+ Slog.d(TAG_AM,
+ "Skip unfreezing because frozen state is sticky pid=" + pid + " "
+ + app.processName);
+ }
+ return;
+ }
if (opt.isPendingFreeze()) {
// Remove pending DO_FREEZE message
mFreezeHandler.removeMessages(SET_FROZEN_PROCESS_MSG, app);
@@ -1406,7 +1430,7 @@
@GuardedBy({"mAm", "mProcLock"})
void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) {
synchronized (mFreezerLock) {
- unfreezeAppInternalLSP(app, reason);
+ unfreezeAppInternalLSP(app, reason, false);
}
}
@@ -2080,15 +2104,6 @@
synchronized (mProcLock) {
pid = proc.getPid();
- if (proc.mState.getCurAdj() < ProcessList.CACHED_APP_MIN_ADJ
- || opt.shouldNotFreeze()) {
- if (DEBUG_FREEZER) {
- Slog.d(TAG_AM, "Skipping freeze for process " + pid
- + " " + name + " curAdj = " + proc.mState.getCurAdj()
- + ", shouldNotFreeze = " + opt.shouldNotFreeze());
- }
- return;
- }
if (mFreezerOverride) {
opt.setFreezerOverride(true);
diff --git a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
index f233107..e8c8f6d 100644
--- a/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
+++ b/services/core/java/com/android/server/am/ProcessCachedOptimizerRecord.java
@@ -75,6 +75,15 @@
private boolean mFrozen;
/**
+ * If set to true it will make the (un)freeze decision sticky which means that the freezer
+ * decision will remain the same unless a freeze is forced via {@link #mForceFreezeOps}.
+ * This property is usually set to true when external user wants to maintain a (un)frozen state
+ * after being applied.
+ */
+ @GuardedBy("mProcLock")
+ private boolean mFreezeSticky;
+
+ /**
* Set to false after the process has been frozen.
* Set to true after we have collected PSS for the frozen process.
*/
@@ -195,6 +204,15 @@
void setFrozen(boolean frozen) {
mFrozen = frozen;
}
+ @GuardedBy("mProcLock")
+ void setFreezeSticky(boolean sticky) {
+ mFreezeSticky = sticky;
+ }
+
+ @GuardedBy("mProcLock")
+ boolean isFreezeSticky() {
+ return mFreezeSticky;
+ }
boolean skipPSSCollectionBecauseFrozen() {
boolean collected = mHasCollectedFrozenPSS;
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 1268156..462942e 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -87,14 +87,14 @@
private final @NonNull Context mContext;
/** ID for Communication strategy retrieved form audio policy manager */
- private int mCommunicationStrategyId = -1;
+ /*package*/ int mCommunicationStrategyId = -1;
/** ID for Accessibility strategy retrieved form audio policy manager */
private int mAccessibilityStrategyId = -1;
/** Active communication device reported by audio policy manager */
- private AudioDeviceInfo mActiveCommunicationDevice;
+ /*package*/ AudioDeviceInfo mActiveCommunicationDevice;
/** Last preferred device set for communication strategy */
private AudioDeviceAttributes mPreferredCommunicationDevice;
@@ -204,6 +204,7 @@
private void init() {
setupMessaging(mContext);
+ initAudioHalBluetoothState();
initRoutingStrategyIds();
mPreferredCommunicationDevice = null;
updateActiveCommunicationDevice();
@@ -749,6 +750,19 @@
mIsLeOutput = false;
}
+ BtDeviceInfo(@NonNull BtDeviceInfo src, int state) {
+ mDevice = src.mDevice;
+ mState = state;
+ mProfile = src.mProfile;
+ mSupprNoisy = src.mSupprNoisy;
+ mVolume = src.mVolume;
+ mIsLeOutput = src.mIsLeOutput;
+ mEventSource = src.mEventSource;
+ mAudioSystemDevice = src.mAudioSystemDevice;
+ mMusicDevice = src.mMusicDevice;
+ mCodec = src.mCodec;
+ }
+
// redefine equality op so we can match messages intended for this device
@Override
public boolean equals(Object o) {
@@ -815,7 +829,7 @@
* @param info struct with the (dis)connection information
*/
/*package*/ void queueOnBluetoothActiveDeviceChanged(@NonNull BtDeviceChangedData data) {
- if (data.mInfo.getProfile() == BluetoothProfile.A2DP && data.mPreviousDevice != null
+ if (data.mPreviousDevice != null
&& data.mPreviousDevice.equals(data.mNewDevice)) {
final String name = TextUtils.emptyIfNull(data.mNewDevice.getName());
new MediaMetrics.Item(MediaMetrics.Name.AUDIO_DEVICE + MediaMetrics.SEPARATOR
@@ -824,7 +838,8 @@
.set(MediaMetrics.Property.STATUS, data.mInfo.getProfile())
.record();
synchronized (mDeviceStateLock) {
- postBluetoothA2dpDeviceConfigChange(data.mNewDevice);
+ postBluetoothDeviceConfigChange(createBtDeviceInfo(data, data.mNewDevice,
+ BluetoothProfile.STATE_CONNECTED));
}
} else {
synchronized (mDeviceStateLock) {
@@ -845,10 +860,100 @@
}
}
- /**
- * Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
- */
+ // Current Bluetooth SCO audio active state indicated by BtHelper via setBluetoothScoOn().
+ @GuardedBy("mDeviceStateLock")
private boolean mBluetoothScoOn;
+ // value of BT_SCO parameter currently applied to audio HAL.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothScoOnApplied;
+
+ // A2DP suspend state requested by AudioManager.setA2dpSuspended() API.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothA2dpSuspendedExt;
+ // A2DP suspend state requested by AudioDeviceInventory.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothA2dpSuspendedInt;
+ // value of BT_A2dpSuspendedSCO parameter currently applied to audio HAL.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothA2dpSuspendedApplied;
+
+ // LE Audio suspend state requested by AudioManager.setLeAudioSuspended() API.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothLeSuspendedExt;
+ // LE Audio suspend state requested by AudioDeviceInventory.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothLeSuspendedInt;
+ // value of LeAudioSuspended parameter currently applied to audio HAL.
+ @GuardedBy("mDeviceStateLock")
+ private boolean mBluetoothLeSuspendedApplied;
+
+ private void initAudioHalBluetoothState() {
+ mBluetoothScoOnApplied = false;
+ AudioSystem.setParameters("BT_SCO=off");
+ mBluetoothA2dpSuspendedApplied = false;
+ AudioSystem.setParameters("A2dpSuspended=false");
+ mBluetoothLeSuspendedApplied = false;
+ AudioSystem.setParameters("LeAudioSuspended=false");
+ }
+
+ @GuardedBy("mDeviceStateLock")
+ private void updateAudioHalBluetoothState() {
+ if (mBluetoothScoOn != mBluetoothScoOnApplied) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothScoOn: "
+ + mBluetoothScoOn + ", mBluetoothScoOnApplied: " + mBluetoothScoOnApplied);
+ }
+ if (mBluetoothScoOn) {
+ if (!mBluetoothA2dpSuspendedApplied) {
+ AudioSystem.setParameters("A2dpSuspended=true");
+ mBluetoothA2dpSuspendedApplied = true;
+ }
+ if (!mBluetoothLeSuspendedApplied) {
+ AudioSystem.setParameters("LeAudioSuspended=true");
+ mBluetoothLeSuspendedApplied = true;
+ }
+ AudioSystem.setParameters("BT_SCO=on");
+ } else {
+ AudioSystem.setParameters("BT_SCO=off");
+ }
+ mBluetoothScoOnApplied = mBluetoothScoOn;
+ }
+ if (!mBluetoothScoOnApplied) {
+ if ((mBluetoothA2dpSuspendedExt || mBluetoothA2dpSuspendedInt)
+ != mBluetoothA2dpSuspendedApplied) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothA2dpSuspendedExt: "
+ + mBluetoothA2dpSuspendedExt
+ + ", mBluetoothA2dpSuspendedInt: " + mBluetoothA2dpSuspendedInt
+ + ", mBluetoothA2dpSuspendedApplied: "
+ + mBluetoothA2dpSuspendedApplied);
+ }
+ mBluetoothA2dpSuspendedApplied =
+ mBluetoothA2dpSuspendedExt || mBluetoothA2dpSuspendedInt;
+ if (mBluetoothA2dpSuspendedApplied) {
+ AudioSystem.setParameters("A2dpSuspended=true");
+ } else {
+ AudioSystem.setParameters("A2dpSuspended=false");
+ }
+ }
+ if ((mBluetoothLeSuspendedExt || mBluetoothLeSuspendedInt)
+ != mBluetoothLeSuspendedApplied) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "updateAudioHalBluetoothState() mBluetoothLeSuspendedExt: "
+ + mBluetoothLeSuspendedExt
+ + ", mBluetoothLeSuspendedInt: " + mBluetoothLeSuspendedInt
+ + ", mBluetoothLeSuspendedApplied: " + mBluetoothLeSuspendedApplied);
+ }
+ mBluetoothLeSuspendedApplied =
+ mBluetoothLeSuspendedExt || mBluetoothLeSuspendedInt;
+ if (mBluetoothLeSuspendedApplied) {
+ AudioSystem.setParameters("LeAudioSuspended=true");
+ } else {
+ AudioSystem.setParameters("LeAudioSuspended=false");
+ }
+ }
+ }
+ }
/*package*/ void setBluetoothScoOn(boolean on, String eventSource) {
if (AudioService.DEBUG_COMM_RTE) {
@@ -856,10 +961,67 @@
}
synchronized (mDeviceStateLock) {
mBluetoothScoOn = on;
+ updateAudioHalBluetoothState();
postUpdateCommunicationRouteClient(eventSource);
}
}
+ /*package*/ void setA2dpSuspended(boolean enable, boolean internal, String eventSource) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setA2dpSuspended source: " + eventSource + ", enable: "
+ + enable + ", internal: " + internal
+ + ", mBluetoothA2dpSuspendedInt: " + mBluetoothA2dpSuspendedInt
+ + ", mBluetoothA2dpSuspendedExt: " + mBluetoothA2dpSuspendedExt);
+ }
+ synchronized (mDeviceStateLock) {
+ if (internal) {
+ mBluetoothA2dpSuspendedInt = enable;
+ } else {
+ mBluetoothA2dpSuspendedExt = enable;
+ }
+ updateAudioHalBluetoothState();
+ }
+ }
+
+ /*package*/ void clearA2dpSuspended() {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "clearA2dpSuspended");
+ }
+ synchronized (mDeviceStateLock) {
+ mBluetoothA2dpSuspendedInt = false;
+ mBluetoothA2dpSuspendedExt = false;
+ updateAudioHalBluetoothState();
+ }
+ }
+
+ /*package*/ void setLeAudioSuspended(boolean enable, boolean internal, String eventSource) {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "setLeAudioSuspended source: " + eventSource + ", enable: "
+ + enable + ", internal: " + internal
+ + ", mBluetoothLeSuspendedInt: " + mBluetoothA2dpSuspendedInt
+ + ", mBluetoothLeSuspendedExt: " + mBluetoothA2dpSuspendedExt);
+ }
+ synchronized (mDeviceStateLock) {
+ if (internal) {
+ mBluetoothLeSuspendedInt = enable;
+ } else {
+ mBluetoothLeSuspendedExt = enable;
+ }
+ updateAudioHalBluetoothState();
+ }
+ }
+
+ /*package*/ void clearLeAudioSuspended() {
+ if (AudioService.DEBUG_COMM_RTE) {
+ Log.v(TAG, "clearLeAudioSuspended");
+ }
+ synchronized (mDeviceStateLock) {
+ mBluetoothLeSuspendedInt = false;
+ mBluetoothLeSuspendedExt = false;
+ updateAudioHalBluetoothState();
+ }
+ }
+
/*package*/ AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
synchronized (mDeviceStateLock) {
return mDeviceInventory.startWatchingRoutes(observer);
@@ -902,8 +1064,8 @@
new AudioModeInfo(mode, pid, uid));
}
- /*package*/ void postBluetoothA2dpDeviceConfigChange(@NonNull BluetoothDevice device) {
- sendLMsgNoDelay(MSG_L_A2DP_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, device);
+ /*package*/ void postBluetoothDeviceConfigChange(@NonNull BtDeviceInfo info) {
+ sendLMsgNoDelay(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE, SENDMSG_QUEUE, info);
}
/*package*/ void startBluetoothScoForClient(IBinder cb, int pid, int scoAudioMode,
@@ -1160,6 +1322,10 @@
sendIMsgNoDelay(MSG_I_SCO_AUDIO_STATE_CHANGED, SENDMSG_QUEUE, state);
}
+ /*package*/ void postNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) {
+ sendLMsgNoDelay(MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED, SENDMSG_QUEUE, btDevice);
+ }
+
/*package*/ static final class CommunicationDeviceInfo {
final @NonNull IBinder mCb; // Identifies the requesting client for death handler
final int mPid; // Requester process ID
@@ -1235,9 +1401,11 @@
}
}
- /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) {
+ /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes,
+ boolean connect, @Nullable BluetoothDevice btDevice) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/);
+ return mDeviceInventory.handleDeviceConnection(
+ attributes, connect, false /*for test*/, btDevice);
}
}
@@ -1478,13 +1646,10 @@
(String) msg.obj, msg.arg1);
}
break;
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
- final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
+ case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
synchronized (mDeviceStateLock) {
- final int a2dpCodec = mBtHelper.getA2dpCodec(btDevice);
- mDeviceInventory.onBluetoothA2dpDeviceConfigChange(
- new BtHelper.BluetoothA2dpDeviceInfo(btDevice, -1, a2dpCodec),
- BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
+ mDeviceInventory.onBluetoothDeviceConfigChange(
+ (BtDeviceInfo) msg.obj, BtHelper.EVENT_DEVICE_CONFIG_CHANGE);
}
break;
case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
@@ -1642,6 +1807,10 @@
final int capturePreset = msg.arg1;
mDeviceInventory.onSaveClearPreferredDevicesForCapturePreset(capturePreset);
} break;
+ case MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED: {
+ final BluetoothDevice btDevice = (BluetoothDevice) msg.obj;
+ BtHelper.onNotifyPreferredAudioProfileApplied(btDevice);
+ } break;
default:
Log.wtf(TAG, "Invalid message " + msg.what);
}
@@ -1677,7 +1846,7 @@
private static final int MSG_IL_BTA2DP_TIMEOUT = 10;
// process change of A2DP device configuration, obj is BluetoothDevice
- private static final int MSG_L_A2DP_DEVICE_CONFIG_CHANGE = 11;
+ private static final int MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE = 11;
private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 12;
private static final int MSG_REPORT_NEW_ROUTES = 13;
@@ -1717,13 +1886,15 @@
private static final int MSG_IL_SAVE_REMOVE_NDEF_DEVICE_FOR_STRATEGY = 48;
private static final int MSG_IL_BTLEAUDIO_TIMEOUT = 49;
+ private static final int MSG_L_NOTIFY_PREFERRED_AUDIOPROFILE_APPLIED = 50;
+
private static boolean isMessageHandledUnderWakelock(int msgId) {
switch(msgId) {
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_L_SET_BT_ACTIVE_DEVICE:
case MSG_IL_BTA2DP_TIMEOUT:
case MSG_IL_BTLEAUDIO_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
case MSG_TOGGLE_HDMI:
case MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT:
case MSG_L_HEARING_AID_DEVICE_CONNECTION_CHANGE_EXT:
@@ -1815,7 +1986,7 @@
case MSG_L_SET_WIRED_DEVICE_CONNECTION_STATE:
case MSG_IL_BTA2DP_TIMEOUT:
case MSG_IL_BTLEAUDIO_TIMEOUT:
- case MSG_L_A2DP_DEVICE_CONFIG_CHANGE:
+ case MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE:
if (sLastDeviceConnectMsgTime >= time) {
// add a little delay to make sure messages are ordered as expected
time = sLastDeviceConnectMsgTime + 30;
@@ -1835,7 +2006,7 @@
static {
MESSAGES_MUTE_MUSIC = new HashSet<>();
MESSAGES_MUTE_MUSIC.add(MSG_L_SET_BT_ACTIVE_DEVICE);
- MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONFIG_CHANGE);
+ MESSAGES_MUTE_MUSIC.add(MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE);
MESSAGES_MUTE_MUSIC.add(MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT);
MESSAGES_MUTE_MUSIC.add(MSG_IIL_SET_FORCE_BT_A2DP_USE);
}
@@ -1856,7 +2027,7 @@
// Do not mute on bluetooth event if music is playing on a wired headset.
if ((message == MSG_L_SET_BT_ACTIVE_DEVICE
|| message == MSG_L_A2DP_DEVICE_CONNECTION_CHANGE_EXT
- || message == MSG_L_A2DP_DEVICE_CONFIG_CHANGE)
+ || message == MSG_L_BLUETOOTH_DEVICE_CONFIG_CHANGE)
&& AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)
&& hasIntersection(mDeviceInventory.DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET,
mAudioService.getDeviceSetForStream(AudioSystem.STREAM_MUSIC))) {
@@ -1992,27 +2163,22 @@
"updateCommunicationRoute, preferredCommunicationDevice: "
+ preferredCommunicationDevice + " eventSource: " + eventSource)));
- if (preferredCommunicationDevice == null
- || preferredCommunicationDevice.getType() != AudioDeviceInfo.TYPE_BLUETOOTH_SCO) {
- AudioSystem.setParameters("BT_SCO=off");
- } else {
- AudioSystem.setParameters("BT_SCO=on");
- }
if (preferredCommunicationDevice == null) {
AudioDeviceAttributes defaultDevice = getDefaultCommunicationDevice();
if (defaultDevice != null) {
- setPreferredDevicesForStrategySync(
+ mDeviceInventory.setPreferredDevicesForStrategy(
mCommunicationStrategyId, Arrays.asList(defaultDevice));
- setPreferredDevicesForStrategySync(
+ mDeviceInventory.setPreferredDevicesForStrategy(
mAccessibilityStrategyId, Arrays.asList(defaultDevice));
} else {
- removePreferredDevicesForStrategySync(mCommunicationStrategyId);
- removePreferredDevicesForStrategySync(mAccessibilityStrategyId);
+ mDeviceInventory.removePreferredDevicesForStrategy(mCommunicationStrategyId);
+ mDeviceInventory.removePreferredDevicesForStrategy(mAccessibilityStrategyId);
}
+ mDeviceInventory.applyConnectedDevicesRoles();
} else {
- setPreferredDevicesForStrategySync(
+ mDeviceInventory.setPreferredDevicesForStrategy(
mCommunicationStrategyId, Arrays.asList(preferredCommunicationDevice));
- setPreferredDevicesForStrategySync(
+ mDeviceInventory.setPreferredDevicesForStrategy(
mAccessibilityStrategyId, Arrays.asList(preferredCommunicationDevice));
}
onUpdatePhoneStrategyDevice(preferredCommunicationDevice);
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 43063af..1eb39f7 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -34,26 +34,36 @@
import android.media.IStrategyNonDefaultDevicesDispatcher;
import android.media.IStrategyPreferredDevicesDispatcher;
import android.media.MediaMetrics;
+import android.media.MediaRecorder.AudioSource;
+import android.media.audiopolicy.AudioProductStrategy;
import android.media.permission.ClearCallingIdentityContext;
import android.media.permission.SafeCloseable;
import android.os.Binder;
+import android.os.Bundle;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.EventLogger;
+import com.google.android.collect.Sets;
+
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
@@ -175,18 +185,26 @@
final RemoteCallbackList<ICapturePresetDevicesRoleDispatcher> mDevRoleCapturePresetDispatchers =
new RemoteCallbackList<ICapturePresetDevicesRoleDispatcher>();
+ final List<AudioProductStrategy> mStrategies;
+
/*package*/ AudioDeviceInventory(@NonNull AudioDeviceBroker broker) {
- mDeviceBroker = broker;
- mAudioSystem = AudioSystemAdapter.getDefaultAdapter();
+ this(broker, AudioSystemAdapter.getDefaultAdapter());
}
//-----------------------------------------------------------
/** for mocking only, allows to inject AudioSystem adapter */
/*package*/ AudioDeviceInventory(@NonNull AudioSystemAdapter audioSystem) {
- mDeviceBroker = null;
- mAudioSystem = audioSystem;
+ this(null, audioSystem);
}
+ private AudioDeviceInventory(@Nullable AudioDeviceBroker broker,
+ @Nullable AudioSystemAdapter audioSystem) {
+ mDeviceBroker = broker;
+ mAudioSystem = audioSystem;
+ mStrategies = AudioProductStrategy.getAudioProductStrategies();
+ mBluetoothDualModeEnabled = SystemProperties.getBoolean(
+ "persist.bluetooth.enable_dual_mode_audio", false);
+ }
/*package*/ void setDeviceBroker(@NonNull AudioDeviceBroker broker) {
mDeviceBroker = broker;
}
@@ -203,8 +221,13 @@
int mDeviceCodecFormat;
final UUID mSensorUuid;
+ /** Disabled operating modes for this device. Use a negative logic so that by default
+ * an empty list means all modes are allowed.
+ * See BluetoothAdapter.AUDIO_MODE_DUPLEX and BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY */
+ @NonNull ArraySet<String> mDisabledModes = new ArraySet(0);
+
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
- int deviceCodecFormat, UUID sensorUuid) {
+ int deviceCodecFormat, @Nullable UUID sensorUuid) {
mDeviceType = deviceType;
mDeviceName = deviceName == null ? "" : deviceName;
mDeviceAddress = deviceAddress == null ? "" : deviceAddress;
@@ -212,11 +235,31 @@
mSensorUuid = sensorUuid;
}
+ void setModeDisabled(String mode) {
+ mDisabledModes.add(mode);
+ }
+ void setModeEnabled(String mode) {
+ mDisabledModes.remove(mode);
+ }
+ boolean isModeEnabled(String mode) {
+ return !mDisabledModes.contains(mode);
+ }
+ boolean isOutputOnlyModeEnabled() {
+ return isModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
+ }
+ boolean isDuplexModeEnabled() {
+ return isModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
+ }
+
DeviceInfo(int deviceType, String deviceName, String deviceAddress,
int deviceCodecFormat) {
this(deviceType, deviceName, deviceAddress, deviceCodecFormat, null);
}
+ DeviceInfo(int deviceType, String deviceName, String deviceAddress) {
+ this(deviceType, deviceName, deviceAddress, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ }
+
@Override
public String toString() {
return "[DeviceInfo: type:0x" + Integer.toHexString(mDeviceType)
@@ -224,7 +267,8 @@
+ ") name:" + mDeviceName
+ " addr:" + mDeviceAddress
+ " codec: " + Integer.toHexString(mDeviceCodecFormat)
- + " sensorUuid: " + Objects.toString(mSensorUuid) + "]";
+ + " sensorUuid: " + Objects.toString(mSensorUuid)
+ + " disabled modes: " + mDisabledModes + "]";
}
@NonNull String getKey() {
@@ -276,9 +320,18 @@
pw.println(" " + prefix + " type:0x" + Integer.toHexString(keyType)
+ " (" + AudioSystem.getDeviceName(keyType)
+ ") addr:" + valueAddress); });
+ pw.println("\n" + prefix + "Preferred devices for capture preset:");
mPreferredDevicesForCapturePreset.forEach((capturePreset, devices) -> {
pw.println(" " + prefix + "capturePreset:" + capturePreset
+ " devices:" + devices); });
+ pw.println("\n" + prefix + "Applied devices roles for strategies:");
+ mAppliedStrategyRoles.forEach((key, devices) -> {
+ pw.println(" " + prefix + "strategy: " + key.first
+ + " role:" + key.second + " devices:" + devices); });
+ pw.println("\n" + prefix + "Applied devices roles for presets:");
+ mAppliedPresetRoles.forEach((key, devices) -> {
+ pw.println(" " + prefix + "preset: " + key.first
+ + " role:" + key.second + " devices:" + devices); });
}
//------------------------------------------------------------
@@ -299,15 +352,16 @@
AudioSystem.DEVICE_STATE_AVAILABLE,
di.mDeviceCodecFormat);
}
+ mAppliedStrategyRoles.clear();
+ applyConnectedDevicesRoles_l();
}
synchronized (mPreferredDevices) {
mPreferredDevices.forEach((strategy, devices) -> {
- mAudioSystem.setDevicesRoleForStrategy(
- strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices); });
+ setPreferredDevicesForStrategy(strategy, devices); });
}
synchronized (mNonDefaultDevices) {
mNonDefaultDevices.forEach((strategy, devices) -> {
- mAudioSystem.setDevicesRoleForStrategy(
+ addDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices); });
}
synchronized (mPreferredDevicesForCapturePreset) {
@@ -380,8 +434,7 @@
btInfo.mVolume * 10, btInfo.mAudioSystemDevice,
"onSetBtActiveDevice");
}
- makeA2dpDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
- "onSetBtActiveDevice", btInfo.mCodec);
+ makeA2dpDeviceAvailable(btInfo, "onSetBtActiveDevice");
}
break;
case BluetoothProfile.HEARING_AID:
@@ -397,10 +450,7 @@
if (switchToUnavailable) {
makeLeAudioDeviceUnavailableNow(address, btInfo.mAudioSystemDevice);
} else if (switchToAvailable) {
- makeLeAudioDeviceAvailable(address, BtHelper.getName(btInfo.mDevice),
- streamType, btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10,
- btInfo.mAudioSystemDevice,
- "onSetBtActiveDevice");
+ makeLeAudioDeviceAvailable(btInfo, streamType, "onSetBtActiveDevice");
}
break;
default: throw new IllegalArgumentException("Invalid profile "
@@ -411,30 +461,30 @@
@GuardedBy("AudioDeviceBroker.mDeviceStateLock")
- /*package*/ void onBluetoothA2dpDeviceConfigChange(
- @NonNull BtHelper.BluetoothA2dpDeviceInfo btInfo, int event) {
+ /*package*/ void onBluetoothDeviceConfigChange(
+ @NonNull AudioDeviceBroker.BtDeviceInfo btInfo, int event) {
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
- + "onBluetoothA2dpDeviceConfigChange")
- .set(MediaMetrics.Property.EVENT, BtHelper.a2dpDeviceEventToString(event));
+ + "onBluetoothDeviceConfigChange")
+ .set(MediaMetrics.Property.EVENT, BtHelper.deviceEventToString(event));
- final BluetoothDevice btDevice = btInfo.getBtDevice();
+ final BluetoothDevice btDevice = btInfo.mDevice;
if (btDevice == null) {
mmi.set(MediaMetrics.Property.EARLY_RETURN, "btDevice null").record();
return;
}
if (AudioService.DEBUG_DEVICES) {
- Log.d(TAG, "onBluetoothA2dpDeviceConfigChange btDevice=" + btDevice);
+ Log.d(TAG, "onBluetoothDeviceConfigChange btDevice=" + btDevice);
}
- int a2dpVolume = btInfo.getVolume();
- @AudioSystem.AudioFormatNativeEnumForBtCodec final int a2dpCodec = btInfo.getCodec();
+ int volume = btInfo.mVolume;
+ @AudioSystem.AudioFormatNativeEnumForBtCodec final int audioCodec = btInfo.mCodec;
String address = btDevice.getAddress();
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "onBluetoothA2dpDeviceConfigChange addr=" + address
- + " event=" + BtHelper.a2dpDeviceEventToString(event)));
+ "onBluetoothDeviceConfigChange addr=" + address
+ + " event=" + BtHelper.deviceEventToString(event)));
synchronized (mDevicesLock) {
if (mDeviceBroker.hasScheduledA2dpConnection(btDevice)) {
@@ -449,53 +499,54 @@
AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
final DeviceInfo di = mConnectedDevices.get(key);
if (di == null) {
- Log.e(TAG, "invalid null DeviceInfo in onBluetoothA2dpDeviceConfigChange");
+ Log.e(TAG, "invalid null DeviceInfo in onBluetoothDeviceConfigChange");
mmi.set(MediaMetrics.Property.EARLY_RETURN, "null DeviceInfo").record();
return;
}
mmi.set(MediaMetrics.Property.ADDRESS, address)
.set(MediaMetrics.Property.ENCODING,
- AudioSystem.audioFormatToString(a2dpCodec))
- .set(MediaMetrics.Property.INDEX, a2dpVolume)
+ AudioSystem.audioFormatToString(audioCodec))
+ .set(MediaMetrics.Property.INDEX, volume)
.set(MediaMetrics.Property.NAME, di.mDeviceName);
- if (event == BtHelper.EVENT_ACTIVE_DEVICE_CHANGE) {
- // Device is connected
- if (a2dpVolume != -1) {
- mDeviceBroker.postSetVolumeIndexOnDevice(AudioSystem.STREAM_MUSIC,
- // convert index to internal representation in VolumeStreamState
- a2dpVolume * 10,
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- "onBluetoothA2dpDeviceConfigChange");
- }
- } else if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
- if (di.mDeviceCodecFormat != a2dpCodec) {
- di.mDeviceCodecFormat = a2dpCodec;
- mConnectedDevices.replace(key, di);
- }
- }
- final int res = mAudioSystem.handleDeviceConfigChange(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address,
- BtHelper.getName(btDevice), a2dpCodec);
- if (res != AudioSystem.AUDIO_STATUS_OK) {
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM handleDeviceConfigChange failed for A2DP device addr=" + address
- + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
- .printLog(TAG));
+ if (event == BtHelper.EVENT_DEVICE_CONFIG_CHANGE) {
+ boolean a2dpCodecChange = false;
+ if (btInfo.mProfile == BluetoothProfile.A2DP) {
+ if (di.mDeviceCodecFormat != audioCodec) {
+ di.mDeviceCodecFormat = audioCodec;
+ mConnectedDevices.replace(key, di);
+ a2dpCodecChange = true;
+ }
+ final int res = mAudioSystem.handleDeviceConfigChange(
+ btInfo.mAudioSystemDevice, address,
+ BtHelper.getName(btDevice), audioCodec);
- int musicDevice = mDeviceBroker.getDeviceForStream(AudioSystem.STREAM_MUSIC);
- // force A2DP device disconnection in case of error so that AudioService state is
- // consistent with audio policy manager state
- setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btDevice,
- BluetoothProfile.A2DP, BluetoothProfile.STATE_DISCONNECTED,
- musicDevice, AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
- } else {
- AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
- "APM handleDeviceConfigChange success for A2DP device addr=" + address
- + " codec=" + AudioSystem.audioFormatToString(a2dpCodec))
- .printLog(TAG));
+ if (res != AudioSystem.AUDIO_STATUS_OK) {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM handleDeviceConfigChange failed for A2DP device addr="
+ + address + " codec="
+ + AudioSystem.audioFormatToString(audioCodec))
+ .printLog(TAG));
+
+ // force A2DP device disconnection in case of error so that AudioService
+ // state is consistent with audio policy manager state
+ setBluetoothActiveDevice(new AudioDeviceBroker.BtDeviceInfo(btInfo,
+ BluetoothProfile.STATE_DISCONNECTED));
+ } else {
+ AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
+ "APM handleDeviceConfigChange success for A2DP device addr="
+ + address
+ + " codec=" + AudioSystem.audioFormatToString(audioCodec))
+ .printLog(TAG));
+
+ }
+ }
+ if (!a2dpCodecChange) {
+ updateBluetoothPreferredModes_l();
+ mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice);
+ }
}
}
mmi.record();
@@ -578,7 +629,7 @@
}
if (!handleDeviceConnection(wdcs.mAttributes,
- wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) {
+ wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest, null)) {
// change of connection state failed, bailout
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
.record();
@@ -712,23 +763,35 @@
/*package*/ int setPreferredDevicesForStrategySync(int strategy,
@NonNull List<AudioDeviceAttributes> devices) {
- int status = AudioSystem.ERROR;
-
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
- "setPreferredDevicesForStrategySync, strategy: " + strategy
- + " devices: " + devices)).printLog(TAG));
- status = mAudioSystem.setDevicesRoleForStrategy(
- strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
- }
-
+ final int status = setPreferredDevicesForStrategy(strategy, devices);
if (status == AudioSystem.SUCCESS) {
mDeviceBroker.postSaveSetPreferredDevicesForStrategy(strategy, devices);
}
return status;
}
+ /*package*/ int setPreferredDevicesForStrategy(int strategy,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ int status = AudioSystem.ERROR;
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
+ "setPreferredDevicesForStrategy, strategy: " + strategy
+ + " devices: " + devices)).printLog(TAG));
+ status = setDevicesRoleForStrategy(
+ strategy, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
+ }
+ return status;
+ }
+
/*package*/ int removePreferredDevicesForStrategySync(int strategy) {
+ final int status = removePreferredDevicesForStrategy(strategy);
+ if (status == AudioSystem.SUCCESS) {
+ mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
+ }
+ return status;
+ }
+
+ /*package*/ int removePreferredDevicesForStrategy(int strategy) {
int status = AudioSystem.ERROR;
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
@@ -736,13 +799,9 @@
"removePreferredDevicesForStrategySync, strategy: "
+ strategy)).printLog(TAG));
- status = mAudioSystem.clearDevicesRoleForStrategy(
+ status = clearDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_PREFERRED);
}
-
- if (status == AudioSystem.SUCCESS) {
- mDeviceBroker.postSaveRemovePreferredDevicesForStrategy(strategy);
- }
return status;
}
@@ -757,7 +816,7 @@
AudioService.sDeviceLogger.enqueue((new EventLogger.StringEvent(
"setDeviceAsNonDefaultForStrategySync, strategy: " + strategy
+ " device: " + device)).printLog(TAG));
- status = mAudioSystem.setDevicesRoleForStrategy(
+ status = addDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices);
}
@@ -779,7 +838,7 @@
"removeDeviceAsNonDefaultForStrategySync, strategy: "
+ strategy + " devices: " + device)).printLog(TAG));
- status = mAudioSystem.removeDevicesRoleForStrategy(
+ status = removeDevicesRoleForStrategy(
strategy, AudioSystem.DEVICE_ROLE_DISABLED, devices);
}
@@ -812,33 +871,70 @@
/*package*/ int setPreferredDevicesForCapturePresetSync(
int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
- int status = AudioSystem.ERROR;
-
- try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- status = mAudioSystem.setDevicesRoleForCapturePreset(
- capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
- }
-
+ final int status = setPreferredDevicesForCapturePreset(capturePreset, devices);
if (status == AudioSystem.SUCCESS) {
mDeviceBroker.postSaveSetPreferredDevicesForCapturePreset(capturePreset, devices);
}
return status;
}
- /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
- int status = AudioSystem.ERROR;
-
+ private int setPreferredDevicesForCapturePreset(
+ int capturePreset, @NonNull List<AudioDeviceAttributes> devices) {
+ int status = AudioSystem.ERROR;
try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
- status = mAudioSystem.clearDevicesRoleForCapturePreset(
- capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+ status = setDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED, devices);
}
+ return status;
+ }
+ /*package*/ int clearPreferredDevicesForCapturePresetSync(int capturePreset) {
+ final int status = clearPreferredDevicesForCapturePreset(capturePreset);
if (status == AudioSystem.SUCCESS) {
mDeviceBroker.postSaveClearPreferredDevicesForCapturePreset(capturePreset);
}
return status;
}
+ private int clearPreferredDevicesForCapturePreset(int capturePreset) {
+ int status = AudioSystem.ERROR;
+
+ try (SafeCloseable ignored = ClearCallingIdentityContext.create()) {
+ status = clearDevicesRoleForCapturePreset(
+ capturePreset, AudioSystem.DEVICE_ROLE_PREFERRED);
+ }
+ return status;
+ }
+
+ private int addDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return addDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
+ return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d);
+ }, capturePreset, role, devices);
+ }
+
+ private int removeDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return removeDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
+ return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d);
+ }, capturePreset, role, devices);
+ }
+
+ private int setDevicesRoleForCapturePreset(int capturePreset, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return setDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
+ return mAudioSystem.addDevicesRoleForCapturePreset(p, r, d);
+ }, (p, r, d) -> {
+ return mAudioSystem.clearDevicesRoleForCapturePreset(p, r);
+ }, capturePreset, role, devices);
+ }
+
+ private int clearDevicesRoleForCapturePreset(int capturePreset, int role) {
+ return clearDevicesRole(mAppliedPresetRoles, (p, r, d) -> {
+ return mAudioSystem.clearDevicesRoleForCapturePreset(p, r);
+ }, capturePreset, role);
+ }
+
/*package*/ void registerCapturePresetDevicesRoleDispatcher(
@NonNull ICapturePresetDevicesRoleDispatcher dispatcher) {
mDevRoleCapturePresetDispatchers.register(dispatcher);
@@ -849,7 +945,208 @@
mDevRoleCapturePresetDispatchers.unregister(dispatcher);
}
- //-----------------------------------------------------------------------
+ private int addDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return addDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
+ return mAudioSystem.setDevicesRoleForStrategy(s, r, d);
+ }, strategy, role, devices);
+ }
+
+ private int removeDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return removeDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
+ return mAudioSystem.removeDevicesRoleForStrategy(s, r, d);
+ }, strategy, role, devices);
+ }
+
+ private int setDevicesRoleForStrategy(int strategy, int role,
+ @NonNull List<AudioDeviceAttributes> devices) {
+ return setDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
+ return mAudioSystem.setDevicesRoleForStrategy(s, r, d);
+ }, (s, r, d) -> {
+ return mAudioSystem.clearDevicesRoleForStrategy(s, r);
+ }, strategy, role, devices);
+ }
+
+ private int clearDevicesRoleForStrategy(int strategy, int role) {
+ return clearDevicesRole(mAppliedStrategyRoles, (s, r, d) -> {
+ return mAudioSystem.clearDevicesRoleForStrategy(s, r);
+ }, strategy, role);
+ }
+
+ //------------------------------------------------------------
+ // Cache for applied roles for strategies and devices. The cache avoids reapplying the
+ // same list of devices for a given role and strategy and the corresponding systematic
+ // redundant work in audio policy manager and audio flinger.
+ // The key is the pair <Strategy , Role> and the value is the current list of devices.
+
+ private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>>
+ mAppliedStrategyRoles = new ArrayMap<>();
+
+ // Cache for applied roles for capture presets and devices. The cache avoids reapplying the
+ // same list of devices for a given role and capture preset and the corresponding systematic
+ // redundant work in audio policy manager and audio flinger.
+ // The key is the pair <Preset , Role> and the value is the current list of devices.
+ private final ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>>
+ mAppliedPresetRoles = new ArrayMap<>();
+
+ interface AudioSystemInterface {
+ int deviceRoleAction(int usecase, int role, @Nullable List<AudioDeviceAttributes> devices);
+ }
+
+ private int addDevicesRole(
+ ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
+ AudioSystemInterface asi,
+ int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ synchronized (rolesMap) {
+ Pair<Integer, Integer> key = new Pair<>(useCase, role);
+ List<AudioDeviceAttributes> roleDevices = new ArrayList<>();
+ List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
+
+ if (rolesMap.containsKey(key)) {
+ roleDevices = rolesMap.get(key);
+ for (AudioDeviceAttributes device : devices) {
+ if (!roleDevices.contains(device)) {
+ appliedDevices.add(device);
+ }
+ }
+ } else {
+ appliedDevices.addAll(devices);
+ }
+ if (appliedDevices.isEmpty()) {
+ return AudioSystem.SUCCESS;
+ }
+ final int status = asi.deviceRoleAction(useCase, role, appliedDevices);
+ if (status == AudioSystem.SUCCESS) {
+ roleDevices.addAll(appliedDevices);
+ rolesMap.put(key, roleDevices);
+ }
+ return status;
+ }
+ }
+
+ private int removeDevicesRole(
+ ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
+ AudioSystemInterface asi,
+ int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ synchronized (rolesMap) {
+ Pair<Integer, Integer> key = new Pair<>(useCase, role);
+ if (!rolesMap.containsKey(key)) {
+ return AudioSystem.SUCCESS;
+ }
+ List<AudioDeviceAttributes> roleDevices = rolesMap.get(key);
+ List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
+ for (AudioDeviceAttributes device : devices) {
+ if (roleDevices.contains(device)) {
+ appliedDevices.add(device);
+ }
+ }
+ if (appliedDevices.isEmpty()) {
+ return AudioSystem.SUCCESS;
+ }
+ final int status = asi.deviceRoleAction(useCase, role, appliedDevices);
+ if (status == AudioSystem.SUCCESS) {
+ roleDevices.removeAll(appliedDevices);
+ if (roleDevices.isEmpty()) {
+ rolesMap.remove(key);
+ } else {
+ rolesMap.put(key, roleDevices);
+ }
+ }
+ return status;
+ }
+ }
+
+ private int setDevicesRole(
+ ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
+ AudioSystemInterface addOp,
+ AudioSystemInterface clearOp,
+ int useCase, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ synchronized (rolesMap) {
+ Pair<Integer, Integer> key = new Pair<>(useCase, role);
+ List<AudioDeviceAttributes> roleDevices = new ArrayList<>();
+ List<AudioDeviceAttributes> appliedDevices = new ArrayList<>();
+
+ if (rolesMap.containsKey(key)) {
+ roleDevices = rolesMap.get(key);
+ boolean equal = false;
+ if (roleDevices.size() == devices.size()) {
+ roleDevices.retainAll(devices);
+ equal = roleDevices.size() == devices.size();
+ }
+ if (!equal) {
+ clearOp.deviceRoleAction(useCase, role, null);
+ roleDevices.clear();
+ appliedDevices.addAll(devices);
+ }
+ } else {
+ appliedDevices.addAll(devices);
+ }
+ if (appliedDevices.isEmpty()) {
+ return AudioSystem.SUCCESS;
+ }
+ final int status = addOp.deviceRoleAction(useCase, role, appliedDevices);
+ if (status == AudioSystem.SUCCESS) {
+ roleDevices.addAll(appliedDevices);
+ rolesMap.put(key, roleDevices);
+ }
+ return status;
+ }
+ }
+
+ private int clearDevicesRole(
+ ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
+ AudioSystemInterface asi, int useCase, int role) {
+ synchronized (rolesMap) {
+ Pair<Integer, Integer> key = new Pair<>(useCase, role);
+ if (!rolesMap.containsKey(key)) {
+ return AudioSystem.SUCCESS;
+ }
+ final int status = asi.deviceRoleAction(useCase, role, null);
+ if (status == AudioSystem.SUCCESS) {
+ rolesMap.remove(key);
+ }
+ return status;
+ }
+ }
+
+ @GuardedBy("mDevicesLock")
+ private void purgeDevicesRoles_l() {
+ purgeRoles(mAppliedStrategyRoles, (s, r, d) -> {
+ return mAudioSystem.removeDevicesRoleForStrategy(s, r, d); });
+ purgeRoles(mAppliedPresetRoles, (p, r, d) -> {
+ return mAudioSystem.removeDevicesRoleForCapturePreset(p, r, d); });
+ }
+
+ @GuardedBy("mDevicesLock")
+ private void purgeRoles(
+ ArrayMap<Pair<Integer, Integer>, List<AudioDeviceAttributes>> rolesMap,
+ AudioSystemInterface asi) {
+ synchronized (rolesMap) {
+ Iterator<Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>>> itRole =
+ rolesMap.entrySet().iterator();
+ while (itRole.hasNext()) {
+ Map.Entry<Pair<Integer, Integer>, List<AudioDeviceAttributes>> entry =
+ itRole.next();
+ Pair<Integer, Integer> keyRole = entry.getKey();
+ Iterator<AudioDeviceAttributes> itDev = rolesMap.get(keyRole).iterator();
+ while (itDev.hasNext()) {
+ AudioDeviceAttributes ada = itDev.next();
+ final String devKey = DeviceInfo.makeDeviceListKey(ada.getInternalType(),
+ ada.getAddress());
+ if (mConnectedDevices.get(devKey) == null) {
+ asi.deviceRoleAction(keyRole.first, keyRole.second, Arrays.asList(ada));
+ itDev.remove();
+ }
+ }
+ if (rolesMap.get(keyRole).isEmpty()) {
+ itRole.remove();
+ }
+ }
+ }
+ }
+
+//-----------------------------------------------------------------------
/**
* Check if a device is in the list of connected devices
@@ -871,10 +1168,11 @@
* @param connect true if connection
* @param isForTesting if true, not calling AudioSystem for the connection as this is
* just for testing
+ * @param btDevice the corresponding Bluetooth device when relevant.
* @return false if an error was reported by AudioSystem
*/
/*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
- boolean isForTesting) {
+ boolean isForTesting, @Nullable BluetoothDevice btDevice) {
int device = attributes.getInternalType();
String address = attributes.getAddress();
String deviceName = attributes.getName();
@@ -889,6 +1187,7 @@
.set(MediaMetrics.Property.MODE, connect
? MediaMetrics.Value.CONNECT : MediaMetrics.Value.DISCONNECT)
.set(MediaMetrics.Property.NAME, deviceName);
+ boolean status = false;
synchronized (mDevicesLock) {
final String deviceKey = DeviceInfo.makeDeviceListKey(device, address);
if (AudioService.DEBUG_DEVICES) {
@@ -916,24 +1215,33 @@
.record();
return false;
}
- mConnectedDevices.put(deviceKey, new DeviceInfo(
- device, deviceName, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
+ mConnectedDevices.put(deviceKey, new DeviceInfo(device, deviceName, address));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
- mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
- return true;
+ status = true;
} else if (!connect && isConnected) {
mAudioSystem.setDeviceConnectionState(attributes,
AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
- mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
- return true;
+ status = true;
}
- Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
- + ", deviceSpec=" + di + ", connect=" + connect);
+ if (status) {
+ if (AudioSystem.isBluetoothScoDevice(device)) {
+ updateBluetoothPreferredModes_l();
+ if (connect) {
+ mDeviceBroker.postNotifyPreferredAudioProfileApplied(btDevice);
+ } else {
+ purgeDevicesRoles_l();
+ }
+ }
+ mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
+ } else {
+ Log.w(TAG, "handleDeviceConnection() failed, deviceKey=" + deviceKey
+ + ", deviceSpec=" + di + ", connect=" + connect);
+ mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
+ }
}
- mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.DISCONNECTED).record();
- return false;
+ return status;
}
@@ -1142,15 +1450,20 @@
// Internal utilities
@GuardedBy("mDevicesLock")
- private void makeA2dpDeviceAvailable(String address, String name, String eventSource,
- int a2dpCodec) {
+ private void makeA2dpDeviceAvailable(AudioDeviceBroker.BtDeviceInfo btInfo,
+ String eventSource) {
+ final String address = btInfo.mDevice.getAddress();
+ final String name = BtHelper.getName(btInfo.mDevice);
+ final int a2dpCodec = btInfo.mCodec;
+
// enable A2DP before notifying A2DP connection to avoid unnecessary processing in
// audio policy manager
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
// at this point there could be another A2DP device already connected in APM, but it
// doesn't matter as this new one will overwrite the previous one
- final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name);
+ final int res = mAudioSystem.setDeviceConnectionState(ada,
AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
// TODO: log in MediaMetrics once distinction between connection failure and
@@ -1167,13 +1480,12 @@
}
// Reset A2DP suspend state each time a new sink is connected
- mAudioSystem.setParameters("A2dpSuspended=false");
+ mDeviceBroker.clearA2dpSuspended();
// The convention for head tracking sensors associated with A2DP devices is to
// use a UUID derived from the MAC address as follows:
// time_low = 0, time_mid = 0, time_hi = 0, clock_seq = 0, node = MAC Address
- UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(
- new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address));
+ UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
final DeviceInfo di = new DeviceInfo(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, name,
address, a2dpCodec, sensorUuid);
final String diKey = di.getKey();
@@ -1184,6 +1496,206 @@
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
setCurrentAudioRouteNameIfPossible(name, true /*fromA2dp*/);
+
+ updateBluetoothPreferredModes_l();
+ mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice);
+ }
+
+ static final int[] CAPTURE_PRESETS = new int[] {AudioSource.MIC, AudioSource.CAMCORDER,
+ AudioSource.VOICE_RECOGNITION, AudioSource.VOICE_COMMUNICATION,
+ AudioSource.UNPROCESSED, AudioSource.VOICE_PERFORMANCE, AudioSource.HOTWORD};
+
+ // reflects system property persist.bluetooth.enable_dual_mode_audio
+ final boolean mBluetoothDualModeEnabled;
+ /**
+ * Goes over all connected Bluetooth devices and set the audio policy device role to DISABLED
+ * or not according to their own and other devices modes.
+ * The top priority is given to LE devices, then SCO ,then A2DP.
+ */
+ @GuardedBy("mDevicesLock")
+ private void applyConnectedDevicesRoles_l() {
+ if (!mBluetoothDualModeEnabled) {
+ return;
+ }
+ DeviceInfo leOutDevice =
+ getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_BLE_SET);
+ DeviceInfo leInDevice =
+ getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_BLE_SET);
+ DeviceInfo a2dpDevice =
+ getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_A2DP_SET);
+ DeviceInfo scoOutDevice =
+ getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_OUT_ALL_SCO_SET);
+ DeviceInfo scoInDevice =
+ getFirstConnectedDeviceOfTypes(AudioSystem.DEVICE_IN_ALL_SCO_SET);
+ boolean disableA2dp = (leOutDevice != null && leOutDevice.isOutputOnlyModeEnabled());
+ boolean disableSco = (leOutDevice != null && leOutDevice.isDuplexModeEnabled())
+ || (leInDevice != null && leInDevice.isDuplexModeEnabled());
+ AudioDeviceAttributes communicationDevice =
+ mDeviceBroker.mActiveCommunicationDevice == null
+ ? null : ((mDeviceBroker.isInCommunication()
+ && mDeviceBroker.mActiveCommunicationDevice != null)
+ ? new AudioDeviceAttributes(mDeviceBroker.mActiveCommunicationDevice)
+ : null);
+
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, "applyConnectedDevicesRoles_l\n - leOutDevice: " + leOutDevice
+ + "\n - leInDevice: " + leInDevice
+ + "\n - a2dpDevice: " + a2dpDevice
+ + "\n - scoOutDevice: " + scoOutDevice
+ + "\n - scoInDevice: " + scoInDevice
+ + "\n - disableA2dp: " + disableA2dp
+ + ", disableSco: " + disableSco);
+ }
+
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (!AudioSystem.isBluetoothDevice(di.mDeviceType)) {
+ continue;
+ }
+ AudioDeviceAttributes ada =
+ new AudioDeviceAttributes(di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, " + checking Device: " + ada);
+ }
+ if (ada.equalTypeAddress(communicationDevice)) {
+ continue;
+ }
+
+ if (AudioSystem.isBluetoothOutDevice(di.mDeviceType)) {
+ for (AudioProductStrategy strategy : mStrategies) {
+ boolean disable = false;
+ if (strategy.getId() == mDeviceBroker.mCommunicationStrategyId) {
+ if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
+ disable = disableSco || !di.isDuplexModeEnabled();
+ } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
+ disable = !di.isDuplexModeEnabled();
+ }
+ } else {
+ if (AudioSystem.isBluetoothA2dpOutDevice(di.mDeviceType)) {
+ disable = disableA2dp || !di.isOutputOnlyModeEnabled();
+ } else if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
+ disable = disableSco || !di.isOutputOnlyModeEnabled();
+ } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
+ disable = !di.isOutputOnlyModeEnabled();
+ }
+ }
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, " - strategy: " + strategy.getId()
+ + ", disable: " + disable);
+ }
+ if (disable) {
+ addDevicesRoleForStrategy(strategy.getId(),
+ AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
+ } else {
+ removeDevicesRoleForStrategy(strategy.getId(),
+ AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
+ }
+ }
+ }
+ if (AudioSystem.isBluetoothInDevice(di.mDeviceType)) {
+ for (int capturePreset : CAPTURE_PRESETS) {
+ boolean disable = false;
+ if (AudioSystem.isBluetoothScoDevice(di.mDeviceType)) {
+ disable = disableSco || !di.isDuplexModeEnabled();
+ } else if (AudioSystem.isBluetoothLeDevice(di.mDeviceType)) {
+ disable = !di.isDuplexModeEnabled();
+ }
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, " - capturePreset: " + capturePreset
+ + ", disable: " + disable);
+ }
+ if (disable) {
+ addDevicesRoleForCapturePreset(capturePreset,
+ AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
+ } else {
+ removeDevicesRoleForCapturePreset(capturePreset,
+ AudioSystem.DEVICE_ROLE_DISABLED, Arrays.asList(ada));
+ }
+ }
+ }
+ }
+ }
+
+ /* package */ void applyConnectedDevicesRoles() {
+ synchronized (mDevicesLock) {
+ applyConnectedDevicesRoles_l();
+ }
+ }
+
+ @GuardedBy("mDevicesLock")
+ int checkProfileIsConnected(int profile) {
+ switch (profile) {
+ case BluetoothProfile.HEADSET:
+ if (getFirstConnectedDeviceOfTypes(
+ AudioSystem.DEVICE_OUT_ALL_SCO_SET) != null
+ || getFirstConnectedDeviceOfTypes(
+ AudioSystem.DEVICE_IN_ALL_SCO_SET) != null) {
+ return profile;
+ }
+ break;
+ case BluetoothProfile.A2DP:
+ if (getFirstConnectedDeviceOfTypes(
+ AudioSystem.DEVICE_OUT_ALL_A2DP_SET) != null) {
+ return profile;
+ }
+ break;
+ case BluetoothProfile.LE_AUDIO:
+ case BluetoothProfile.LE_AUDIO_BROADCAST:
+ if (getFirstConnectedDeviceOfTypes(
+ AudioSystem.DEVICE_OUT_ALL_BLE_SET) != null
+ || getFirstConnectedDeviceOfTypes(
+ AudioSystem.DEVICE_IN_ALL_BLE_SET) != null) {
+ return profile;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0;
+ }
+
+ @GuardedBy("mDevicesLock")
+ private void updateBluetoothPreferredModes_l() {
+ if (!mBluetoothDualModeEnabled) {
+ return;
+ }
+ HashSet<String> processedAddresses = new HashSet<>(0);
+ for (DeviceInfo di : mConnectedDevices.values()) {
+ if (!AudioSystem.isBluetoothDevice(di.mDeviceType)
+ || processedAddresses.contains(di.mDeviceAddress)) {
+ continue;
+ }
+ Bundle preferredProfiles = BtHelper.getPreferredAudioProfiles(di.mDeviceAddress);
+ if (AudioService.DEBUG_DEVICES) {
+ Log.i(TAG, "updateBluetoothPreferredModes_l processing device address: "
+ + di.mDeviceAddress + ", preferredProfiles: " + preferredProfiles);
+ }
+ for (DeviceInfo di2 : mConnectedDevices.values()) {
+ if (!AudioSystem.isBluetoothDevice(di2.mDeviceType)
+ || !di.mDeviceAddress.equals(di2.mDeviceAddress)) {
+ continue;
+ }
+ int profile = BtHelper.getProfileFromType(di2.mDeviceType);
+ if (profile == 0) {
+ continue;
+ }
+ int preferredProfile = checkProfileIsConnected(
+ preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_DUPLEX));
+ if (preferredProfile == profile || preferredProfile == 0) {
+ di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
+ } else {
+ di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_DUPLEX);
+ }
+ preferredProfile = checkProfileIsConnected(
+ preferredProfiles.getInt(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY));
+ if (preferredProfile == profile || preferredProfile == 0) {
+ di2.setModeEnabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
+ } else {
+ di2.setModeDisabled(BluetoothAdapter.AUDIO_MODE_OUTPUT_ONLY);
+ }
+ }
+ processedAddresses.add(di.mDeviceAddress);
+ }
+ applyConnectedDevicesRoles_l();
}
@GuardedBy("mDevicesLock")
@@ -1231,13 +1743,17 @@
// Remove A2DP routes as well
setCurrentAudioRouteNameIfPossible(null, true /*fromA2dp*/);
mmi.record();
+
+ updateBluetoothPreferredModes_l();
+ purgeDevicesRoles_l();
}
@GuardedBy("mDevicesLock")
private void makeA2dpDeviceUnavailableLater(String address, int delayMs) {
// prevent any activity on the A2DP audio output to avoid unwanted
// reconnection of the sink.
- mAudioSystem.setParameters("A2dpSuspended=true");
+ mDeviceBroker.setA2dpSuspended(
+ true /*enable*/, true /*internal*/, "makeA2dpDeviceUnavailableLater");
// retrieve DeviceInfo before removing device
final String deviceKey =
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address);
@@ -1259,8 +1775,7 @@
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
- new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "",
- address, AudioSystem.AUDIO_FORMAT_DEFAULT));
+ new DeviceInfo(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, "", address));
}
@GuardedBy("mDevicesLock")
@@ -1286,8 +1801,7 @@
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
- new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name,
- address, AudioSystem.AUDIO_FORMAT_DEFAULT));
+ new DeviceInfo(AudioSystem.DEVICE_OUT_HEARING_AID, name, address));
mDeviceBroker.postAccessoryPlugMediaUnmute(AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postApplyVolumeOnDevice(streamType,
AudioSystem.DEVICE_OUT_HEARING_AID, "makeHearingAidDeviceAvailable");
@@ -1325,29 +1839,56 @@
* @return true if a DEVICE_OUT_HEARING_AID is connected, false otherwise.
*/
boolean isHearingAidConnected() {
+ return getFirstConnectedDeviceOfTypes(
+ Sets.newHashSet(AudioSystem.DEVICE_OUT_HEARING_AID)) != null;
+ }
+
+ /**
+ * Returns a DeviceInfo for the fist connected device matching one of the supplied types
+ */
+ private DeviceInfo getFirstConnectedDeviceOfTypes(Set<Integer> internalTypes) {
+ List<DeviceInfo> devices = getConnectedDevicesOfTypes(internalTypes);
+ return devices.isEmpty() ? null : devices.get(0);
+ }
+
+ /**
+ * Returns a list of connected devices matching one one of the supplied types
+ */
+ private List<DeviceInfo> getConnectedDevicesOfTypes(Set<Integer> internalTypes) {
+ ArrayList<DeviceInfo> devices = new ArrayList<>();
synchronized (mDevicesLock) {
for (DeviceInfo di : mConnectedDevices.values()) {
- if (di.mDeviceType == AudioSystem.DEVICE_OUT_HEARING_AID) {
- return true;
+ if (internalTypes.contains(di.mDeviceType)) {
+ devices.add(di);
}
}
- return false;
}
+ return devices;
+ }
+
+ /* package */ AudioDeviceAttributes getDeviceOfType(int type) {
+ DeviceInfo di = getFirstConnectedDeviceOfTypes(Sets.newHashSet(type));
+ return di == null ? null : new AudioDeviceAttributes(
+ di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
}
@GuardedBy("mDevicesLock")
- private void makeLeAudioDeviceAvailable(String address, String name, int streamType,
- int volumeIndex, int device, String eventSource) {
+ private void makeLeAudioDeviceAvailable(
+ AudioDeviceBroker.BtDeviceInfo btInfo, int streamType, String eventSource) {
+ final String address = btInfo.mDevice.getAddress();
+ final String name = BtHelper.getName(btInfo.mDevice);
+ final int volumeIndex = btInfo.mVolume == -1 ? -1 : btInfo.mVolume * 10;
+ final int device = btInfo.mAudioSystemDevice;
+
if (device != AudioSystem.DEVICE_NONE) {
/* Audio Policy sees Le Audio similar to A2DP. Let's make sure
* AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
*/
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
- final int res = AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
- device, address, name),
- AudioSystem.DEVICE_STATE_AVAILABLE,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioDeviceAttributes ada = new AudioDeviceAttributes(device, address, name);
+ final int res = AudioSystem.setDeviceConnectionState(ada,
+ AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"APM failed to make available LE Audio device addr=" + address
@@ -1358,12 +1899,13 @@
AudioService.sDeviceLogger.enqueue(new EventLogger.StringEvent(
"LE Audio device addr=" + address + " now available").printLog(TAG));
}
-
// Reset LEA suspend state each time a new sink is connected
- mAudioSystem.setParameters("LeAudioSuspended=false");
+ mDeviceBroker.clearLeAudioSuspended();
+ UUID sensorUuid = UuidUtils.uuidFromAudioDeviceAttributes(ada);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
- new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
+ new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT,
+ sensorUuid));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
setCurrentAudioRouteNameIfPossible(name, /*fromA2dp=*/false);
}
@@ -1379,6 +1921,9 @@
final int maxIndex = mDeviceBroker.getMaxVssVolumeForStream(streamType);
mDeviceBroker.postSetLeAudioVolumeIndex(leAudioVolIndex, maxIndex, streamType);
mDeviceBroker.postApplyVolumeOnDevice(streamType, device, "makeLeAudioDeviceAvailable");
+
+ updateBluetoothPreferredModes_l();
+ mDeviceBroker.postNotifyPreferredAudioProfileApplied(btInfo.mDevice);
}
@GuardedBy("mDevicesLock")
@@ -1403,13 +1948,17 @@
}
setCurrentAudioRouteNameIfPossible(null, false /*fromA2dp*/);
+
+ updateBluetoothPreferredModes_l();
+ purgeDevicesRoles_l();
}
@GuardedBy("mDevicesLock")
private void makeLeAudioDeviceUnavailableLater(String address, int device, int delayMs) {
// prevent any activity on the LEA output to avoid unwanted
// reconnection of the sink.
- mAudioSystem.setParameters("LeAudioSuspended=true");
+ mDeviceBroker.setLeAudioSuspended(
+ true /*enable*/, true /*internal*/, "makeLeAudioDeviceUnavailableLater");
// the device will be made unavailable later, so consider it disconnected right away
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
// send the delayed message to make the device unavailable later
@@ -1737,18 +2286,6 @@
}
}
- /* package */ AudioDeviceAttributes getDeviceOfType(int type) {
- synchronized (mDevicesLock) {
- for (DeviceInfo di : mConnectedDevices.values()) {
- if (di.mDeviceType == type) {
- return new AudioDeviceAttributes(
- di.mDeviceType, di.mDeviceAddress, di.mDeviceName);
- }
- }
- }
- return null;
- }
-
//----------------------------------------------------------
// For tests only
@@ -1759,10 +2296,12 @@
*/
@VisibleForTesting
public boolean isA2dpDeviceConnected(@NonNull BluetoothDevice device) {
- final String key = DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- device.getAddress());
- synchronized (mDevicesLock) {
- return (mConnectedDevices.get(key) != null);
+ for (DeviceInfo di : getConnectedDevicesOfTypes(
+ Sets.newHashSet(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP))) {
+ if (di.mDeviceAddress.equals(device.getAddress())) {
+ return true;
+ }
}
+ return false;
}
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index d1cbbfc..a3163e0 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -6376,6 +6376,26 @@
mDeviceBroker.setBluetoothScoOn(on, eventSource);
}
+ /** @see AudioManager#setA2dpSuspended(boolean) */
+ @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setA2dpSuspended(boolean enable) {
+ super.setA2dpSuspended_enforcePermission();
+ final String eventSource = new StringBuilder("setA2dpSuspended(").append(enable)
+ .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
+ .append(Binder.getCallingPid()).toString();
+ mDeviceBroker.setA2dpSuspended(enable, false /*internal*/, eventSource);
+ }
+
+ /** @see AudioManager#setA2dpSuspended(boolean) */
+ @android.annotation.EnforcePermission(android.Manifest.permission.BLUETOOTH_STACK)
+ public void setLeAudioSuspended(boolean enable) {
+ super.setLeAudioSuspended_enforcePermission();
+ final String eventSource = new StringBuilder("setLeAudioSuspended(").append(enable)
+ .append(") from u/pid:").append(Binder.getCallingUid()).append("/")
+ .append(Binder.getCallingPid()).toString();
+ mDeviceBroker.setLeAudioSuspended(enable, false /*internal*/, eventSource);
+ }
+
/** @see AudioManager#isBluetoothScoOn()
* Note that it doesn't report internal state, but state seen by apps (which may have
* called setBluetoothScoOn() */
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index 7af7ed5..1142a8d 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -435,7 +435,7 @@
}
/**
- * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, int[], String[])}
+ * Same as {@link AudioSystem#removeDevicesRoleForCapturePreset(int, int, List)}
* @param capturePreset
* @param role
* @param devicesToRemove
@@ -448,6 +448,19 @@
}
/**
+ * Same as {@link AudioSystem#addDevicesRoleForCapturePreset(int, int, List)}
+ * @param capturePreset the capture preset to configure
+ * @param role the role of the devices
+ * @param devices the list of devices to be added as role for the given capture preset
+ * @return {@link #SUCCESS} if successfully add
+ */
+ public int addDevicesRoleForCapturePreset(
+ int capturePreset, int role, @NonNull List<AudioDeviceAttributes> devices) {
+ invalidateRoutingCache();
+ return AudioSystem.addDevicesRoleForCapturePreset(capturePreset, role, devices);
+ }
+
+ /**
* Same as {@link AudioSystem#}
* @param capturePreset
* @param role
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 631d7f5..e46c3cc 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -33,6 +33,7 @@
import android.media.AudioSystem;
import android.media.BluetoothProfileConnectionInfo;
import android.os.Binder;
+import android.os.Bundle;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
@@ -150,60 +151,12 @@
}
}
- //----------------------------------------------------------------------
- /*package*/ static class BluetoothA2dpDeviceInfo {
- private final @NonNull BluetoothDevice mBtDevice;
- private final int mVolume;
- private final @AudioSystem.AudioFormatNativeEnumForBtCodec int mCodec;
-
- BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice) {
- this(btDevice, -1, AudioSystem.AUDIO_FORMAT_DEFAULT);
- }
-
- BluetoothA2dpDeviceInfo(@NonNull BluetoothDevice btDevice, int volume, int codec) {
- mBtDevice = btDevice;
- mVolume = volume;
- mCodec = codec;
- }
-
- public @NonNull BluetoothDevice getBtDevice() {
- return mBtDevice;
- }
-
- public int getVolume() {
- return mVolume;
- }
-
- public @AudioSystem.AudioFormatNativeEnumForBtCodec int getCodec() {
- return mCodec;
- }
-
- // redefine equality op so we can match messages intended for this device
- @Override
- public boolean equals(Object o) {
- if (o == null) {
- return false;
- }
- if (this == o) {
- return true;
- }
- if (o instanceof BluetoothA2dpDeviceInfo) {
- return mBtDevice.equals(((BluetoothA2dpDeviceInfo) o).getBtDevice());
- }
- return false;
- }
-
-
- }
-
// A2DP device events
/*package*/ static final int EVENT_DEVICE_CONFIG_CHANGE = 0;
- /*package*/ static final int EVENT_ACTIVE_DEVICE_CHANGE = 1;
- /*package*/ static String a2dpDeviceEventToString(int event) {
+ /*package*/ static String deviceEventToString(int event) {
switch (event) {
case EVENT_DEVICE_CONFIG_CHANGE: return "DEVICE_CONFIG_CHANGE";
- case EVENT_ACTIVE_DEVICE_CHANGE: return "ACTIVE_DEVICE_CHANGE";
default:
return new String("invalid event:" + event);
}
@@ -492,8 +445,8 @@
/*package*/ synchronized void resetBluetoothSco() {
mScoAudioState = SCO_STATE_INACTIVE;
broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- AudioSystem.setParameters("A2dpSuspended=false");
- AudioSystem.setParameters("LeAudioSuspended=false");
+ mDeviceBroker.clearA2dpSuspended();
+ mDeviceBroker.clearLeAudioSuspended();
mDeviceBroker.setBluetoothScoOn(false, "resetBluetoothSco");
}
@@ -620,11 +573,12 @@
return btHeadsetDeviceToAudioDevice(mBluetoothHeadsetDevice);
}
- private AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
+ private static AudioDeviceAttributes btHeadsetDeviceToAudioDevice(BluetoothDevice btDevice) {
if (btDevice == null) {
return new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_BLUETOOTH_SCO, "");
}
String address = btDevice.getAddress();
+ String name = getName(btDevice);
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
address = "";
}
@@ -646,7 +600,7 @@
+ " btClass: " + (btClass == null ? "Unknown" : btClass)
+ " nativeType: " + nativeType + " address: " + address);
}
- return new AudioDeviceAttributes(nativeType, address);
+ return new AudioDeviceAttributes(nativeType, address, name);
}
private boolean handleBtScoActiveDeviceChange(BluetoothDevice btDevice, boolean isActive) {
@@ -655,12 +609,9 @@
}
int inDevice = AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET;
AudioDeviceAttributes audioDevice = btHeadsetDeviceToAudioDevice(btDevice);
- String btDeviceName = getName(btDevice);
boolean result = false;
if (isActive) {
- result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
- audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName),
- isActive);
+ result |= mDeviceBroker.handleDeviceConnection(audioDevice, isActive, btDevice);
} else {
int[] outDeviceTypes = {
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
@@ -669,14 +620,14 @@
};
for (int outDeviceType : outDeviceTypes) {
result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
- outDeviceType, audioDevice.getAddress(), btDeviceName),
- isActive);
+ outDeviceType, audioDevice.getAddress(), audioDevice.getName()),
+ isActive, btDevice);
}
}
// handleDeviceConnection() && result to make sure the method get executed
result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
- inDevice, audioDevice.getAddress(), btDeviceName),
- isActive) && result;
+ inDevice, audioDevice.getAddress(), audioDevice.getName()),
+ isActive, btDevice) && result;
return result;
}
@@ -973,6 +924,30 @@
}
}
+ /*package */ static int getProfileFromType(int deviceType) {
+ if (AudioSystem.isBluetoothA2dpOutDevice(deviceType)) {
+ return BluetoothProfile.A2DP;
+ } else if (AudioSystem.isBluetoothScoDevice(deviceType)) {
+ return BluetoothProfile.HEADSET;
+ } else if (AudioSystem.isBluetoothLeDevice(deviceType)) {
+ return BluetoothProfile.LE_AUDIO;
+ }
+ return 0; // 0 is not a valid profile
+ }
+
+ /*package */ static Bundle getPreferredAudioProfiles(String address) {
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ return adapter.getPreferredAudioProfiles(adapter.getRemoteDevice(address));
+ }
+
+ /**
+ * Notifies Bluetooth framework that new preferred audio profiles for Bluetooth devices
+ * have been applied.
+ */
+ public static void onNotifyPreferredAudioProfileApplied(BluetoothDevice btDevice) {
+ BluetoothAdapter.getDefaultAdapter().notifyActiveDeviceChangeApplied(btDevice);
+ }
+
/**
* Returns the string equivalent for the btDeviceClass class.
*/
diff --git a/services/core/java/com/android/server/connectivity/Vpn.java b/services/core/java/com/android/server/connectivity/Vpn.java
index 7e48f68..0f17139 100644
--- a/services/core/java/com/android/server/connectivity/Vpn.java
+++ b/services/core/java/com/android/server/connectivity/Vpn.java
@@ -969,15 +969,21 @@
// Allow VpnManager app to temporarily run background services to handle this error.
// If an app requires anything beyond this grace period, they MUST either declare
// themselves as a foreground service, or schedule a job/workitem.
- DeviceIdleInternal idleController = mDeps.getDeviceIdleInternal();
- idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
- VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS, mUserId, false, REASON_VPN,
- "VpnManager event");
+ final long token = Binder.clearCallingIdentity();
try {
- return mUserIdContext.startService(intent) != null;
- } catch (RuntimeException e) {
- Log.e(TAG, "Service of VpnManager app " + intent + " failed to start", e);
- return false;
+ final DeviceIdleInternal idleController = mDeps.getDeviceIdleInternal();
+ idleController.addPowerSaveTempWhitelistApp(Process.myUid(), packageName,
+ VPN_MANAGER_EVENT_ALLOWLIST_DURATION_MS, mUserId, false, REASON_VPN,
+ "VpnManager event");
+
+ try {
+ return mUserIdContext.startService(intent) != null;
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Service of VpnManager app " + intent + " failed to start", e);
+ return false;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
}
}
diff --git a/services/core/java/com/android/server/display/PersistentDataStore.java b/services/core/java/com/android/server/display/PersistentDataStore.java
index ec70c89..6d6ed72 100644
--- a/services/core/java/com/android/server/display/PersistentDataStore.java
+++ b/services/core/java/com/android/server/display/PersistentDataStore.java
@@ -306,8 +306,11 @@
}
public boolean setBrightness(DisplayDevice displayDevice, float brightness) {
+ if (displayDevice == null || !displayDevice.hasStableUniqueId()) {
+ return false;
+ }
final String displayDeviceUniqueId = displayDevice.getUniqueId();
- if (!displayDevice.hasStableUniqueId() || displayDeviceUniqueId == null) {
+ if (displayDeviceUniqueId == null) {
return false;
}
final DisplayState state = getDisplayState(displayDeviceUniqueId, true);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 7802b9d..0e26d46 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -124,7 +124,7 @@
private final boolean mDreamsEnabledByDefaultConfig;
private final boolean mDreamsActivatedOnChargeByDefault;
private final boolean mDreamsActivatedOnDockByDefault;
- private final boolean mKeepDreamingWhenUndockedDefault;
+ private final boolean mKeepDreamingWhenUnpluggingDefault;
private final CopyOnWriteArrayList<DreamManagerInternal.DreamManagerStateListener>
mDreamManagerStateListeners = new CopyOnWriteArrayList<>();
@@ -236,8 +236,8 @@
mDreamsActivatedOnDockByDefault = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_dreamsActivatedOnDockByDefault);
mSettingsObserver = new SettingsObserver(mHandler);
- mKeepDreamingWhenUndockedDefault = mContext.getResources().getBoolean(
- com.android.internal.R.bool.config_keepDreamingWhenUndocking);
+ mKeepDreamingWhenUnpluggingDefault = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_keepDreamingWhenUnplugging);
}
@Override
@@ -311,7 +311,7 @@
pw.println("mIsDocked=" + mIsDocked);
pw.println("mIsCharging=" + mIsCharging);
pw.println("mWhenToDream=" + mWhenToDream);
- pw.println("mKeepDreamingWhenUndockedDefault=" + mKeepDreamingWhenUndockedDefault);
+ pw.println("mKeepDreamingWhenUnpluggingDefault=" + mKeepDreamingWhenUnpluggingDefault);
pw.println("getDozeComponent()=" + getDozeComponent());
pw.println();
@@ -340,11 +340,11 @@
}
}
- private void reportKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+ private void reportKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
mHandler.post(() -> {
for (DreamManagerInternal.DreamManagerStateListener listener
: mDreamManagerStateListeners) {
- listener.onKeepDreamingWhenUndockedChanged(keepDreaming);
+ listener.onKeepDreamingWhenUnpluggingChanged(keepDreaming);
}
});
}
@@ -600,8 +600,7 @@
}
mSystemDreamComponent = componentName;
- reportKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked());
-
+ reportKeepDreamingWhenUnpluggingChanged(shouldKeepDreamingWhenUnplugging());
// Switch dream if currently dreaming and not dozing.
if (isDreamingInternal() && !isDozingInternal()) {
startDreamInternal(false /*doze*/, (mSystemDreamComponent == null ? "clear" : "set")
@@ -610,8 +609,8 @@
}
}
- private boolean shouldKeepDreamingWhenUndocked() {
- return mKeepDreamingWhenUndockedDefault && mSystemDreamComponent == null;
+ private boolean shouldKeepDreamingWhenUnplugging() {
+ return mKeepDreamingWhenUnpluggingDefault && mSystemDreamComponent == null;
}
private ComponentName getDefaultDreamComponentForUser(int userId) {
@@ -1057,7 +1056,7 @@
public void registerDreamManagerStateListener(DreamManagerStateListener listener) {
mDreamManagerStateListeners.add(listener);
// Initialize the listener's state.
- listener.onKeepDreamingWhenUndockedChanged(shouldKeepDreamingWhenUndocked());
+ listener.onKeepDreamingWhenUnpluggingChanged(shouldKeepDreamingWhenUnplugging());
}
@Override
diff --git a/services/core/java/com/android/server/inputmethod/OWNERS b/services/core/java/com/android/server/inputmethod/OWNERS
index 00cd700..6e5eb56 100644
--- a/services/core/java/com/android/server/inputmethod/OWNERS
+++ b/services/core/java/com/android/server/inputmethod/OWNERS
@@ -1,8 +1,8 @@
set noparent
-ogunwale@google.com
+roosa@google.com
yukawa@google.com
tarandeep@google.com
-lumark@google.com
-roosa@google.com
-wilsonwu@google.com
+
+ogunwale@google.com #{LAST_RESORT_SUGGESTION}
+jjaggi@google.com #{LAST_RESORT_SUGGESTION}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 73be549..5f8efe2 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2641,6 +2641,7 @@
private void setUserRestrictionInner(int userId, @NonNull String key, boolean value) {
if (!UserRestrictionsUtils.isValidRestriction(key)) {
+ Slog.e(LOG_TAG, "Setting invalid restriction " + key);
return;
}
synchronized (mRestrictionsLock) {
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 2e8a150..e392c24 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -667,15 +667,15 @@
// but the DreamService has not yet been told to start (it's an async process).
private boolean mDozeStartInProgress;
- // Whether to keep dreaming when the device is undocked.
- private boolean mKeepDreamingWhenUndocked;
+ // Whether to keep dreaming when the device is unplugging.
+ private boolean mKeepDreamingWhenUnplugging;
private final class DreamManagerStateListener implements
DreamManagerInternal.DreamManagerStateListener {
@Override
- public void onKeepDreamingWhenUndockedChanged(boolean keepDreaming) {
+ public void onKeepDreamingWhenUnpluggingChanged(boolean keepDreaming) {
synchronized (mLock) {
- mKeepDreamingWhenUndocked = keepDreaming;
+ mKeepDreamingWhenUnplugging = keepDreaming;
}
}
}
@@ -2504,14 +2504,12 @@
return false;
}
- // Don't wake when undocking while dreaming if configured not to.
- if (mKeepDreamingWhenUndocked
+ // Don't wake when unplugging while dreaming if configured not to.
+ if (mKeepDreamingWhenUnplugging
&& getGlobalWakefulnessLocked() == WAKEFULNESS_DREAMING
- && wasPowered && !mIsPowered
- && oldPlugType == BatteryManager.BATTERY_PLUGGED_DOCK) {
+ && wasPowered && !mIsPowered) {
return false;
}
-
// Don't wake when undocked from wireless charger.
// See WirelessChargerDetector for justification.
if (wasPowered && !mIsPowered
@@ -4477,7 +4475,7 @@
+ mWakeUpWhenPluggedOrUnpluggedInTheaterModeConfig);
pw.println(" mTheaterModeEnabled="
+ mTheaterModeEnabled);
- pw.println(" mKeepDreamingWhenUndocked=" + mKeepDreamingWhenUndocked);
+ pw.println(" mKeepDreamingWhenUnplugging=" + mKeepDreamingWhenUnplugging);
pw.println(" mSuspendWhenScreenOffDueToProximityConfig="
+ mSuspendWhenScreenOffDueToProximityConfig);
pw.println(" mDreamsSupportedConfig=" + mDreamsSupportedConfig);
diff --git a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
similarity index 91%
rename from services/core/java/com/android/server/power/stats/CpuWakeupStats.java
rename to services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
index 231ffc6..1d63489 100644
--- a/services/core/java/com/android/server/power/stats/CpuWakeupStats.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/CpuWakeupStats.java
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
@@ -55,7 +56,8 @@
private static final String TAG = "CpuWakeupStats";
private static final String SUBSYSTEM_ALARM_STRING = "Alarm";
- private static final String SUBSYSTEM_ALARM_WIFI = "Wifi";
+ private static final String SUBSYSTEM_WIFI_STRING = "Wifi";
+ private static final String SUBSYSTEM_SOUND_TRIGGER_STRING = "Sound_trigger";
private static final String TRACE_TRACK_WAKEUP_ATTRIBUTION = "wakeup_attribution";
@VisibleForTesting
static final long WAKEUP_REASON_HALF_WINDOW_MS = 500;
@@ -91,12 +93,24 @@
mConfig.register(new HandlerExecutor(mHandler));
}
+ private static int typeToStatsType(int wakeupType) {
+ switch (wakeupType) {
+ case Wakeup.TYPE_ABNORMAL:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_ABNORMAL;
+ case Wakeup.TYPE_IRQ:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ;
+ }
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_UNKNOWN;
+ }
+
private static int subsystemToStatsReason(int subsystem) {
switch (subsystem) {
case CPU_WAKEUP_SUBSYSTEM_ALARM:
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__ALARM;
case CPU_WAKEUP_SUBSYSTEM_WIFI:
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__WIFI;
+ case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
+ return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__SOUND_TRIGGER;
}
return FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__REASON__UNKNOWN;
}
@@ -144,7 +158,7 @@
}
}
FrameworkStatsLog.write(FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED,
- FrameworkStatsLog.KERNEL_WAKEUP_ATTRIBUTED__TYPE__TYPE_IRQ,
+ typeToStatsType(wakeupToLog.mType),
subsystemToStatsReason(subsystem),
uids,
wakeupToLog.mElapsedMillis,
@@ -524,8 +538,10 @@
switch (rawSubsystem) {
case SUBSYSTEM_ALARM_STRING:
return CPU_WAKEUP_SUBSYSTEM_ALARM;
- case SUBSYSTEM_ALARM_WIFI:
+ case SUBSYSTEM_WIFI_STRING:
return CPU_WAKEUP_SUBSYSTEM_WIFI;
+ case SUBSYSTEM_SOUND_TRIGGER_STRING:
+ return CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
}
return CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
}
@@ -535,25 +551,43 @@
case CPU_WAKEUP_SUBSYSTEM_ALARM:
return SUBSYSTEM_ALARM_STRING;
case CPU_WAKEUP_SUBSYSTEM_WIFI:
- return SUBSYSTEM_ALARM_WIFI;
+ return SUBSYSTEM_WIFI_STRING;
+ case CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER:
+ return SUBSYSTEM_SOUND_TRIGGER_STRING;
case CPU_WAKEUP_SUBSYSTEM_UNKNOWN:
return "Unknown";
}
return "N/A";
}
- private static final class Wakeup {
+ @VisibleForTesting
+ static final class Wakeup {
private static final String PARSER_TAG = "CpuWakeupStats.Wakeup";
private static final String ABORT_REASON_PREFIX = "Abort";
- private static final Pattern sIrqPattern = Pattern.compile("^(\\d+)\\s+(\\S+)");
+ private static final Pattern sIrqPattern = Pattern.compile("^(\\-?\\d+)\\s+(\\S+)");
+
+ /**
+ * Classical interrupts, which arrive on a dedicated GPIO pin into the main CPU.
+ * Sometimes, when multiple IRQs happen close to each other, they may get batched together.
+ */
+ static final int TYPE_IRQ = 1;
+
+ /**
+ * Non-IRQ wakeups. The exact mechanism for these is unknown, except that these explicitly
+ * do not use an interrupt line or a GPIO pin.
+ */
+ static final int TYPE_ABNORMAL = 2;
+
+ int mType;
long mElapsedMillis;
long mUptimeMillis;
IrqDevice[] mDevices;
- private Wakeup(IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
+ private Wakeup(int type, IrqDevice[] devices, long elapsedMillis, long uptimeMillis) {
+ mType = type;
+ mDevices = devices;
mElapsedMillis = elapsedMillis;
mUptimeMillis = uptimeMillis;
- mDevices = devices;
}
static Wakeup parseWakeup(String rawReason, long elapsedMillis, long uptimeMillis) {
@@ -563,6 +597,7 @@
return null;
}
+ int type = TYPE_IRQ;
int parsedDeviceCount = 0;
final IrqDevice[] parsedDevices = new IrqDevice[components.length];
@@ -574,6 +609,10 @@
try {
line = Integer.parseInt(matcher.group(1));
device = matcher.group(2);
+ if (line < 0) {
+ // Assuming that IRQ wakeups cannot come batched with non-IRQ wakeups.
+ type = TYPE_ABNORMAL;
+ }
} catch (NumberFormatException e) {
Slog.e(PARSER_TAG,
"Exception while parsing device names from part: " + component, e);
@@ -585,15 +624,16 @@
if (parsedDeviceCount == 0) {
return null;
}
- return new Wakeup(Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
+ return new Wakeup(type, Arrays.copyOf(parsedDevices, parsedDeviceCount), elapsedMillis,
uptimeMillis);
}
@Override
public String toString() {
return "Wakeup{"
- + "mElapsedMillis=" + mElapsedMillis
- + ", mUptimeMillis=" + TimeUtils.formatDuration(mUptimeMillis)
+ + "mType=" + mType
+ + ", mElapsedMillis=" + mElapsedMillis
+ + ", mUptimeMillis=" + mUptimeMillis
+ ", mDevices=" + Arrays.toString(mDevices)
+ '}';
}
diff --git a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java b/services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
similarity index 98%
rename from services/core/java/com/android/server/power/stats/IrqDeviceMap.java
rename to services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
index 091d18e..8644f72 100644
--- a/services/core/java/com/android/server/power/stats/IrqDeviceMap.java
+++ b/services/core/java/com/android/server/power/stats/wakeups/IrqDeviceMap.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
import android.annotation.XmlRes;
import android.content.Context;
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index c1552ab..64e1ae5 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -2865,11 +2865,9 @@
return;
}
- if (animate && mTransitionController.inCollectingTransition(startingWindow)
- && startingWindow.cancelAndRedraw()) {
+ if (animate && mTransitionController.inCollectingTransition(startingWindow)) {
// Defer remove starting window after transition start.
- // If splash screen window was in collecting, the client side is unable to draw because
- // of Session#cancelDraw, which will blocking the remove animation.
+ // The surface of app window could really show after the transition finish.
startingWindow.mSyncTransaction.addTransactionCommittedListener(Runnable::run, () -> {
synchronized (mAtmService.mGlobalLock) {
surface.remove(true);
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index c6db8a7..8660bec 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -263,16 +263,19 @@
* {@link WindowContainer#prepareSurfaces}. After calling this, the container should
* chain {@link WindowContainer#prepareSurfaces} down to it's children to give them
* a chance to request dims to continue.
- * @return Non-null dim bounds if the dimmer is showing.
*/
- Rect resetDimStates() {
+ void resetDimStates() {
if (mDimState == null) {
- return null;
+ return;
}
if (!mDimState.mDontReset) {
mDimState.mDimming = false;
}
- return mDimState.mDimBounds;
+ }
+
+ /** Returns non-null bounds if the dimmer is showing. */
+ Rect getDimBounds() {
+ return mDimState != null ? mDimState.mDimBounds : null;
}
void dontAnimateExit() {
diff --git a/services/core/java/com/android/server/wm/DisplayArea.java b/services/core/java/com/android/server/wm/DisplayArea.java
index 26f56a2..9f59f5a 100644
--- a/services/core/java/com/android/server/wm/DisplayArea.java
+++ b/services/core/java/com/android/server/wm/DisplayArea.java
@@ -779,8 +779,9 @@
@Override
void prepareSurfaces() {
- final Rect dimBounds = mDimmer.resetDimStates();
+ mDimmer.resetDimStates();
super.prepareSurfaces();
+ final Rect dimBounds = mDimmer.getDimBounds();
if (dimBounds != null) {
// Bounds need to be relative, as the dim layer is a child.
getBounds(dimBounds);
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 5f9ba3f..9363eb5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -3252,9 +3252,10 @@
@Override
void prepareSurfaces() {
- final Rect dimBounds = mDimmer.resetDimStates();
+ mDimmer.resetDimStates();
super.prepareSurfaces();
+ final Rect dimBounds = mDimmer.getDimBounds();
if (dimBounds != null) {
getDimBounds(dimBounds);
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 1d232fe..311b9a6 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -2923,9 +2923,10 @@
return;
}
- final Rect dimBounds = mDimmer.resetDimStates();
+ mDimmer.resetDimStates();
super.prepareSurfaces();
+ final Rect dimBounds = mDimmer.getDimBounds();
if (dimBounds != null) {
// Bounds need to be relative, as the dim layer is a child.
dimBounds.offsetTo(0 /* newLeft */, 0 /* newTop */);
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index 652c297..76b0e7b 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -2835,7 +2835,7 @@
final String name = isDisplayRotation ? "RotationLayer" : "transition snapshot: " + wc;
SurfaceControl snapshotSurface = wc.makeAnimationLeash()
.setName(name)
- .setOpaque(true)
+ .setOpaque(wc.fillsParent())
.setParent(wc.getSurfaceControl())
.setSecure(screenshotBuffer.containsSecureLayers())
.setCallsite("Transition.ScreenshotSync")
diff --git a/services/core/java/com/android/server/wm/WindowToken.java b/services/core/java/com/android/server/wm/WindowToken.java
index 327483e..da54b15 100644
--- a/services/core/java/com/android/server/wm/WindowToken.java
+++ b/services/core/java/com/android/server/wm/WindowToken.java
@@ -119,24 +119,20 @@
final DisplayInfo mDisplayInfo;
final DisplayFrames mDisplayFrames;
final Configuration mRotatedOverrideConfiguration;
- final SeamlessRotator mRotator;
+
/**
* The tokens that share the same transform. Their end time of transform are the same. The
* list should at least contain the token who creates this state.
*/
final ArrayList<WindowToken> mAssociatedTokens = new ArrayList<>(3);
- final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
+
boolean mIsTransforming = true;
FixedRotationTransformState(DisplayInfo rotatedDisplayInfo,
- DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig,
- int currentRotation) {
+ DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig) {
mDisplayInfo = rotatedDisplayInfo;
mDisplayFrames = rotatedDisplayFrames;
mRotatedOverrideConfiguration = rotatedConfig;
- // This will use unrotate as rotate, so the new and old rotation are inverted.
- mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation,
- rotatedDisplayInfo, true /* applyFixedTransformationHint */);
}
/**
@@ -144,10 +140,8 @@
* showing the window in a display with different rotation.
*/
void transform(WindowContainer<?> container) {
- mRotator.unrotate(container.getPendingTransaction(), container);
- if (!mRotatedContainers.contains(container)) {
- mRotatedContainers.add(container);
- }
+ // The default implementation assumes shell transition is enabled, so the transform
+ // is done by getOrCreateFixedRotationLeash().
}
/**
@@ -155,6 +149,40 @@
* be called when the window has the same rotation as display.
*/
void resetTransform() {
+ for (int i = mAssociatedTokens.size() - 1; i >= 0; --i) {
+ mAssociatedTokens.get(i).removeFixedRotationLeash();
+ }
+ }
+
+ /** The state may not only be used by self. Make sure to leave the influence by others. */
+ void disassociate(WindowToken token) {
+ mAssociatedTokens.remove(token);
+ }
+ }
+
+ private static class FixedRotationTransformStateLegacy extends FixedRotationTransformState {
+ final SeamlessRotator mRotator;
+ final ArrayList<WindowContainer<?>> mRotatedContainers = new ArrayList<>(3);
+
+ FixedRotationTransformStateLegacy(DisplayInfo rotatedDisplayInfo,
+ DisplayFrames rotatedDisplayFrames, Configuration rotatedConfig,
+ int currentRotation) {
+ super(rotatedDisplayInfo, rotatedDisplayFrames, rotatedConfig);
+ // This will use unrotate as rotate, so the new and old rotation are inverted.
+ mRotator = new SeamlessRotator(rotatedDisplayInfo.rotation, currentRotation,
+ rotatedDisplayInfo, true /* applyFixedTransformationHint */);
+ }
+
+ @Override
+ void transform(WindowContainer<?> container) {
+ mRotator.unrotate(container.getPendingTransaction(), container);
+ if (!mRotatedContainers.contains(container)) {
+ mRotatedContainers.add(container);
+ }
+ }
+
+ @Override
+ void resetTransform() {
for (int i = mRotatedContainers.size() - 1; i >= 0; i--) {
final WindowContainer<?> c = mRotatedContainers.get(i);
// If the window is detached (no parent), its surface may have been released.
@@ -164,9 +192,9 @@
}
}
- /** The state may not only be used by self. Make sure to leave the influence by others. */
+ @Override
void disassociate(WindowToken token) {
- mAssociatedTokens.remove(token);
+ super.disassociate(token);
mRotatedContainers.remove(token);
}
}
@@ -437,8 +465,11 @@
if (mFixedRotationTransformState != null) {
mFixedRotationTransformState.disassociate(this);
}
- mFixedRotationTransformState = new FixedRotationTransformState(info, displayFrames,
- new Configuration(config), mDisplayContent.getRotation());
+ config = new Configuration(config);
+ mFixedRotationTransformState = mTransitionController.isShellTransitionsEnabled()
+ ? new FixedRotationTransformState(info, displayFrames, config)
+ : new FixedRotationTransformStateLegacy(info, displayFrames, config,
+ mDisplayContent.getRotation());
mFixedRotationTransformState.mAssociatedTokens.add(this);
mDisplayContent.getDisplayPolicy().simulateLayoutDisplay(displayFrames);
onFixedRotationStatePrepared();
@@ -508,14 +539,7 @@
if (state == null) {
return;
}
- if (!mTransitionController.isShellTransitionsEnabled()) {
- state.resetTransform();
- } else {
- // Remove all the leashes
- for (int i = state.mAssociatedTokens.size() - 1; i >= 0; --i) {
- state.mAssociatedTokens.get(i).removeFixedRotationLeash();
- }
- }
+ state.resetTransform();
// Clear the flag so if the display will be updated to the same orientation, the transform
// won't take effect.
state.mIsTransforming = false;
@@ -589,7 +613,9 @@
void removeFixedRotationLeash() {
if (mFixedRotationTransformLeash == null) return;
final SurfaceControl.Transaction t = getSyncTransaction();
- t.reparent(getSurfaceControl(), getParentSurfaceControl());
+ if (mSurfaceControl != null) {
+ t.reparent(mSurfaceControl, getParentSurfaceControl());
+ }
t.remove(mFixedRotationTransformLeash);
mFixedRotationTransformLeash = null;
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 3d5686d..702602a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -792,7 +792,7 @@
admin.getUserId());
if (receivers.isEmpty()) {
Log.i(TAG, "Couldn't find any receivers that handle ACTION_DEVICE_POLICY_SET_RESULT"
- + "in package " + admin.getPackageName());
+ + " in package " + admin.getPackageName());
return;
}
@@ -845,7 +845,7 @@
admin.getUserId());
if (receivers.isEmpty()) {
Log.i(TAG, "Couldn't find any receivers that handle ACTION_DEVICE_POLICY_CHANGED"
- + "in package " + admin.getPackageName());
+ + " in package " + admin.getPackageName());
return;
}
@@ -868,7 +868,7 @@
for (ResolveInfo resolveInfo : receivers) {
if (!Manifest.permission.BIND_DEVICE_ADMIN.equals(
resolveInfo.activityInfo.permission)) {
- Log.w(TAG, "Receiver " + resolveInfo.activityInfo + " is not protected by"
+ Log.w(TAG, "Receiver " + resolveInfo.activityInfo + " is not protected by "
+ "BIND_DEVICE_ADMIN permission!");
continue;
}
@@ -1066,6 +1066,11 @@
* Removes all local policies for the provided {@code userId}.
*/
private void removeLocalPoliciesForUser(int userId) {
+ if (!mLocalPolicies.contains(userId)) {
+ // No policies on user
+ return;
+ }
+
Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet());
for (PolicyKey policy : localPolicies) {
PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index ed0bcc3..5cad4e2 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -856,7 +856,7 @@
private static final String ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG =
"enable_device_policy_engine";
- private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = false;
+ private static final boolean DEFAULT_ENABLE_DEVICE_POLICY_ENGINE_FOR_FINANCE_FLAG = true;
// TODO(b/265683382) remove the flag after rollout.
private static final String KEEP_PROFILES_RUNNING_FLAG = "enable_keep_profiles_running";
@@ -13287,6 +13287,9 @@
? getProfileParentId(caller.getUserId()) : caller.getUserId();
setLocalUserRestrictionInternal(admin, key, enabled, affectedUserId);
}
+ } else {
+ throw new IllegalStateException("Non-DO/Non-PO cannot set restriction " + key
+ + " while targetSdkVersion is less than UPSIDE_DOWN_CAKE");
}
}
}
@@ -13315,14 +13318,13 @@
/* who= */ null,
key,
caller.getPackageName(),
- caller.getUserId()
+ UserHandle.USER_ALL
);
setGlobalUserRestrictionInternal(admin, key, /* enabled= */ true);
logUserRestrictionCall(key, /* enabled= */ true, /* parent= */ false, caller);
}
-
private void setLocalUserRestrictionInternal(
EnforcingAdmin admin, String key, boolean enabled, int userId) {
PolicyDefinition<Boolean> policyDefinition =
@@ -13340,7 +13342,6 @@
userId);
}
}
-
private void setGlobalUserRestrictionInternal(
EnforcingAdmin admin, String key, boolean enabled) {
PolicyDefinition<Boolean> policyDefinition =
@@ -14724,7 +14725,7 @@
}
final int userId = mInjector.userHandleGetCallingUserId();
- if (isPermissionCheckFlagEnabled()) {
+ if (isPolicyEngineForFinanceFlagEnabled()) {
LockTaskPolicy policy = mDevicePolicyEngine.getResolvedPolicy(
PolicyDefinition.LOCK_TASK, userId);
if (policy == null) {
@@ -16111,6 +16112,7 @@
} else {
long ident = mInjector.binderClearCallingIdentity();
try {
+ // TODO(b/277908283): check in the policy engine instead of calling user manager.
List<UserManager.EnforcingUser> sources = mUserManager
.getUserRestrictionSources(restriction, UserHandle.of(userId));
if (sources == null) {
@@ -16142,27 +16144,14 @@
}
final UserManager.EnforcingUser enforcingUser = sources.get(0);
final int sourceType = enforcingUser.getUserRestrictionSource();
- final int enforcingUserId = enforcingUser.getUserHandle().getIdentifier();
- if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER) {
- // Restriction was enforced by PO
- final ComponentName profileOwner = mOwners.getProfileOwnerComponent(
- enforcingUserId);
- if (profileOwner != null) {
+ if (sourceType == UserManager.RESTRICTION_SOURCE_PROFILE_OWNER
+ || sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
+ ActiveAdmin admin = getMostProbableDPCAdminForLocalPolicy(userId);
+ if (admin != null) {
result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, enforcingUserId);
+ result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier());
result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- profileOwner);
- return result;
- }
- } else if (sourceType == UserManager.RESTRICTION_SOURCE_DEVICE_OWNER) {
- // Restriction was enforced by DO
- final Pair<Integer, ComponentName> deviceOwner =
- mOwners.getDeviceOwnerUserIdAndComponent();
- if (deviceOwner != null) {
- result = new Bundle();
- result.putInt(Intent.EXTRA_USER_ID, deviceOwner.first);
- result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN,
- deviceOwner.second);
+ admin.info.getComponent());
return result;
}
} else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) {
@@ -17376,6 +17365,7 @@
caller.getUserId());
admin = enforcingAdmin.getActiveAdmin();
} else {
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
}
@@ -17407,6 +17397,7 @@
caller.getUserId());
admin = enforcingAdmin.getActiveAdmin();
} else {
+ Objects.requireNonNull(who, "ComponentName is null");
Preconditions.checkCallingUser(isManagedProfile(caller.getUserId()));
Preconditions.checkCallAuthorization(isDeviceOwner(caller) || isProfileOwner(caller));
@@ -22572,53 +22563,53 @@
);
/**
- * All the permisisons granted to a profile owner.
+ * All the permissions granted to a profile owner.
*/
private static final List<String> PROFILE_OWNER_PERMISSIONS =
List.of(
- MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
- MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
- MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
- MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
- MANAGE_DEVICE_POLICY_AUTOFILL,
- MANAGE_DEVICE_POLICY_CALLS,
- MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
- MANAGE_DEVICE_POLICY_DISPLAY,
- MANAGE_DEVICE_POLICY_FACTORY_RESET,
- MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
- MANAGE_DEVICE_POLICY_KEYGUARD,
- MANAGE_DEVICE_POLICY_LOCALE,
- MANAGE_DEVICE_POLICY_LOCATION,
- MANAGE_DEVICE_POLICY_LOCK,
- MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
- MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
- MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
- MANAGE_DEVICE_POLICY_PACKAGE_STATE,
- MANAGE_DEVICE_POLICY_PRINTING,
- MANAGE_DEVICE_POLICY_PROFILES,
- MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
- MANAGE_DEVICE_POLICY_RESET_PASSWORD,
- MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
- MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
- MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
- MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
- MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
- MANAGE_DEVICE_POLICY_TIME,
- MANAGE_DEVICE_POLICY_VPN,
- MANAGE_DEVICE_POLICY_WIPE_DATA
+ MANAGE_DEVICE_POLICY_ACCOUNT_MANAGEMENT,
+ MANAGE_DEVICE_POLICY_ACROSS_USERS_SECURITY_CRITICAL,
+ MANAGE_DEVICE_POLICY_APPS_CONTROL,
+ MANAGE_DEVICE_POLICY_APP_RESTRICTIONS,
+ MANAGE_DEVICE_POLICY_AUDIO_OUTPUT,
+ MANAGE_DEVICE_POLICY_AUTOFILL,
+ MANAGE_DEVICE_POLICY_BLUETOOTH,
+ MANAGE_DEVICE_POLICY_CALLS,
+ MANAGE_DEVICE_POLICY_DEBUGGING_FEATURES,
+ MANAGE_DEVICE_POLICY_DISPLAY,
+ MANAGE_DEVICE_POLICY_FACTORY_RESET,
+ MANAGE_DEVICE_POLICY_INSTALL_UNKNOWN_SOURCES,
+ MANAGE_DEVICE_POLICY_KEYGUARD,
+ MANAGE_DEVICE_POLICY_LOCALE,
+ MANAGE_DEVICE_POLICY_LOCATION,
+ MANAGE_DEVICE_POLICY_LOCK,
+ MANAGE_DEVICE_POLICY_LOCK_CREDENTIALS,
+ MANAGE_DEVICE_POLICY_NEARBY_COMMUNICATION,
+ MANAGE_DEVICE_POLICY_ORGANIZATION_IDENTITY,
+ MANAGE_DEVICE_POLICY_PACKAGE_STATE,
+ MANAGE_DEVICE_POLICY_PRINTING,
+ MANAGE_DEVICE_POLICY_PROFILES,
+ MANAGE_DEVICE_POLICY_PROFILE_INTERACTION,
+ MANAGE_DEVICE_POLICY_RESET_PASSWORD,
+ MANAGE_DEVICE_POLICY_RUNTIME_PERMISSIONS,
+ MANAGE_DEVICE_POLICY_SCREEN_CAPTURE,
+ MANAGE_DEVICE_POLICY_SCREEN_CONTENT,
+ MANAGE_DEVICE_POLICY_SUPPORT_MESSAGE,
+ MANAGE_DEVICE_POLICY_SYSTEM_DIALOGS,
+ MANAGE_DEVICE_POLICY_TIME,
+ MANAGE_DEVICE_POLICY_VPN,
+ MANAGE_DEVICE_POLICY_WIPE_DATA
);
/**
- * All the additional permissions granted to an organisation owned profile owner.
- */
+ * All the additional permissions granted to an organisation owned profile owner.
+ */
private static final List<String>
ADDITIONAL_PROFILE_OWNER_OF_ORGANIZATION_OWNED_DEVICE_PERMISSIONS =
- List.of(
+ List.of(
MANAGE_DEVICE_POLICY_ACROSS_USERS,
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
MANAGE_DEVICE_POLICY_APPS_CONTROL,
- MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_CERTIFICATES,
MANAGE_DEVICE_POLICY_COMMON_CRITERIA_MODE,
@@ -22639,13 +22630,12 @@
MANAGE_DEVICE_POLICY_WIFI,
SET_TIME,
SET_TIME_ZONE
- );
+ );
private static final List<String> ADDITIONAL_PROFILE_OWNER_ON_USER_0_PERMISSIONS =
List.of(
MANAGE_DEVICE_POLICY_AIRPLANE_MODE,
- MANAGE_DEVICE_POLICY_BLUETOOTH,
MANAGE_DEVICE_POLICY_CAMERA,
MANAGE_DEVICE_POLICY_DISPLAY,
MANAGE_DEVICE_POLICY_FUN,
diff --git a/services/tests/PackageManagerServiceTests/TEST_MAPPING b/services/tests/PackageManagerServiceTests/TEST_MAPPING
index e98acb2..5d96af9 100644
--- a/services/tests/PackageManagerServiceTests/TEST_MAPPING
+++ b/services/tests/PackageManagerServiceTests/TEST_MAPPING
@@ -55,23 +55,10 @@
// TODO(b/204133664)
"exclude-filter": "com.android.server.pm.test.SdCardEjectionTests"
},
- {
- // TODO(b/272575212)
- "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataBinaryXml"
- },
- {
- "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptDataTextXml"
- },
- {
- "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderBinaryXml"
- },
- {
- "exclude-filter": "com.android.server.pm.test.SettingsTest#testWriteCorruptHeaderTextXml"
- },
- {
+ {
// TODO(b/272714903)
"exclude-filter": "com.android.server.pm.test.OverlayPathsUninstallSystemUpdatesTest#verify"
- }
+ }
]
}
],
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
index bc3f5ab..3fb7fb4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java
@@ -74,16 +74,17 @@
import android.os.DropBoxManager;
import android.os.HandlerThread;
import android.os.SystemClock;
+import android.os.TestLooperManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.IndentingPrintWriter;
import android.util.Pair;
import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.internal.util.FrameworkStatsLog;
import com.android.server.ExtendedMockitoRule;
-import com.android.server.am.BroadcastQueueTest.SyncBarrier;
import org.junit.After;
import org.junit.Before;
@@ -96,6 +97,7 @@
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
@SmallTest
public final class BroadcastQueueModernImplTest {
@@ -111,6 +113,7 @@
@Mock BroadcastProcessQueue mQueue4;
HandlerThread mHandlerThread;
+ TestLooperManager mLooper;
BroadcastConstants mConstants;
BroadcastQueueModernImpl mImpl;
@@ -127,6 +130,10 @@
mHandlerThread = new HandlerThread(getClass().getSimpleName());
mHandlerThread.start();
+ // Pause all event processing until a test chooses to resume
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+
mConstants = new BroadcastConstants(Settings.Global.BROADCAST_FG_CONSTANTS);
mConstants.DELAY_URGENT_MILLIS = -120_000;
mConstants.DELAY_NORMAL_MILLIS = 10_000;
@@ -167,6 +174,17 @@
mHandlerThread.quit();
}
+ /**
+ * Un-pause our handler to process pending events, wait for our queue to go
+ * idle, and then re-pause the handler.
+ */
+ private void waitForIdle() throws Exception {
+ mLooper.release();
+ mImpl.waitForIdle(LOG_WRITER_INFO);
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+ }
+
private static void assertOrphan(BroadcastProcessQueue queue) {
assertNull(queue.runnableAtNext);
assertNull(queue.runnableAtPrev);
@@ -836,9 +854,6 @@
optionsAlarmVolumeChanged.setDeliveryGroupMatchingKey("audio",
String.valueOf(AudioManager.STREAM_ALARM));
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
optionsMusicVolumeChanged));
@@ -905,9 +920,6 @@
String.valueOf(TEST_UID2));
optionsPackageChangedForUid.setDeliveryGroupExtrasMerger(extrasMerger);
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(packageChangedForUid,
optionsPackageChangedForUid));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(packageChangedForUid2,
@@ -955,9 +967,6 @@
BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
optionsAlarmVolumeChanged.setDeliveryGroupMatchingFilter(filterAlarmVolumeChanged);
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(musicVolumeChanged,
optionsMusicVolumeChanged));
@@ -1004,9 +1013,6 @@
final Pair<Intent, BroadcastOptions> dropboxEntryBroadcast3 = createDropboxBroadcast(
"TAG_A", now + 2000, 7);
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast1.first,
dropboxEntryBroadcast1.second));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(dropboxEntryBroadcast2.first,
@@ -1036,9 +1042,6 @@
.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT)
.setDeliveryGroupMatchingKey(Intent.ACTION_CLOSE_SYSTEM_DIALOGS, "testing");
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(
closeSystemDialogs1, optionsCloseSystemDialog1));
mImpl.enqueueBroadcastLocked(makeBroadcastRecord(
@@ -1088,9 +1091,6 @@
final Intent userPresent = new Intent(Intent.ACTION_USER_PRESENT);
userPresent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
-
final BroadcastRecord userPresentRecord1 = makeBroadcastRecord(userPresent);
final BroadcastRecord userPresentRecord2 = makeBroadcastRecord(userPresent);
@@ -1129,8 +1129,6 @@
makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW)
));
- // Halt all processing so that we get a consistent view
- mHandlerThread.getLooper().getQueue().postSyncBarrier();
mImpl.enqueueBroadcastLocked(record1);
mImpl.enqueueBroadcastLocked(record2);
@@ -1152,12 +1150,9 @@
final BroadcastOptions optionsTimeTick = BroadcastOptions.makeBasic();
optionsTimeTick.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT);
- // Halt all processing so that we get a consistent view
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
- mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
- }
- mImpl.waitForIdle(LOG_WRITER_INFO);
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+ mImpl.enqueueBroadcastLocked(makeBroadcastRecord(timeTick, optionsTimeTick));
+ waitForIdle();
// Verify that there is only one delivery event reported since one of the broadcasts
// should have been skipped.
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
index 90e6a2d..7be1d7c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueTest.java
@@ -77,6 +77,7 @@
import android.os.IBinder;
import android.os.PowerExemptionManager;
import android.os.SystemClock;
+import android.os.TestLooperManager;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Log;
@@ -146,6 +147,7 @@
private Context mContext;
private HandlerThread mHandlerThread;
+ private TestLooperManager mLooper;
private AtomicInteger mNextPid;
@Mock
@@ -206,6 +208,11 @@
mHandlerThread = new HandlerThread(TAG);
mHandlerThread.start();
+
+ // Pause all event processing until a test chooses to resume
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
+
mNextPid = new AtomicInteger(100);
LocalServices.removeServiceForTest(DropBoxManagerInternal.class);
@@ -360,25 +367,6 @@
}
}
- /**
- * Helper that leverages try-with-resources to pause dispatch of
- * {@link #mHandlerThread} until released.
- */
- static class SyncBarrier implements AutoCloseable {
- private final int mToken;
- private HandlerThread mThread;
-
- SyncBarrier(HandlerThread thread) {
- mThread = thread;
- mToken = mThread.getLooper().getQueue().postSyncBarrier();
- }
-
- @Override
- public void close() throws Exception {
- mThread.getLooper().getQueue().removeSyncBarrier(mToken);
- }
- }
-
private enum ProcessStartBehavior {
/** Process starts successfully */
SUCCESS,
@@ -671,8 +659,15 @@
}
}
+ /**
+ * Un-pause our handler to process pending events, wait for our queue to go
+ * idle, and then re-pause the handler.
+ */
private void waitForIdle() throws Exception {
+ mLooper.release();
mQueue.waitForIdle(LOG_WRITER_INFO);
+ mLooper = Objects.requireNonNull(InstrumentationRegistry.getInstrumentation()
+ .acquireLooperManager(mHandlerThread.getLooper()));
}
private void verifyScheduleReceiver(ProcessRecord app, Intent intent) throws Exception {
@@ -1156,23 +1151,21 @@
final ProcessRecord receiverApp = makeActiveProcessRecord(PACKAGE_GREEN);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
- List.of(makeRegisteredReceiver(receiverApp),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE)))));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+ List.of(makeRegisteredReceiver(receiverApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_RED),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_BLUE)))));
- synchronized (mAms) {
- mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_GREEN, Set.of(CLASS_GREEN),
- UserHandle.USER_SYSTEM);
+ synchronized (mAms) {
+ mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_GREEN, Set.of(CLASS_GREEN),
+ UserHandle.USER_SYSTEM);
- // Also try clearing out other unrelated things that should leave
- // the final receiver intact
- mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_RED, null,
- UserHandle.USER_SYSTEM);
- mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
- }
+ // Also try clearing out other unrelated things that should leave
+ // the final receiver intact
+ mQueue.cleanupDisabledPackageReceiversLocked(PACKAGE_RED, null,
+ UserHandle.USER_SYSTEM);
+ mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
// To maximize test coverage, dump current state; we're not worried
// about the actual output, just that we don't crash
@@ -1200,21 +1193,19 @@
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
final Intent timeZone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, USER_GUEST, new ArrayList<>(
- List.of(makeRegisteredReceiver(callerApp),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
- makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
- makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
- enqueueBroadcast(makeBroadcastRecord(timeZone, callerApp, USER_GUEST, new ArrayList<>(
- List.of(makeRegisteredReceiver(callerApp),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
- makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
- makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, USER_GUEST, new ArrayList<>(
+ List.of(makeRegisteredReceiver(callerApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
+ enqueueBroadcast(makeBroadcastRecord(timeZone, callerApp, USER_GUEST, new ArrayList<>(
+ List.of(makeRegisteredReceiver(callerApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_RED, USER_GUEST),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN, USER_GUEST),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_BLUE, USER_GUEST)))));
- synchronized (mAms) {
- mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
- }
+ synchronized (mAms) {
+ mQueue.cleanupDisabledPackageReceiversLocked(null, null, USER_GUEST);
}
waitForIdle();
@@ -1240,15 +1231,12 @@
final ProcessRecord oldApp = makeActiveProcessRecord(PACKAGE_GREEN);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
- List.of(makeRegisteredReceiver(oldApp),
- makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
-
- synchronized (mAms) {
- oldApp.killLocked(TAG, 42, false);
- mQueue.onApplicationCleanupLocked(oldApp);
- }
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp, new ArrayList<>(
+ List.of(makeRegisteredReceiver(oldApp),
+ makeManifestReceiver(PACKAGE_GREEN, CLASS_GREEN)))));
+ synchronized (mAms) {
+ oldApp.killLocked(TAG, 42, false);
+ mQueue.onApplicationCleanupLocked(oldApp);
}
waitForIdle();
@@ -1621,17 +1609,14 @@
final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
airplane.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
- List.of(makeRegisteredReceiver(receiverBlueApp, 10),
- makeRegisteredReceiver(receiverGreenApp, 10),
- makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
- makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
- makeRegisteredReceiver(receiverYellowApp, -10))));
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
- List.of(makeRegisteredReceiver(receiverBlueApp))));
- }
-
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp, 10),
+ makeRegisteredReceiver(receiverGreenApp, 10),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW),
+ makeRegisteredReceiver(receiverYellowApp, -10))));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverBlueApp))));
waitForIdle();
// Ignore the final foreground broadcast
@@ -1745,17 +1730,15 @@
final IIntentReceiver resultToFirst = mock(IIntentReceiver.class);
final IIntentReceiver resultToSecond = mock(IIntentReceiver.class);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeOrderedBroadcastRecord(timezoneFirst, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
- makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
- resultToFirst, null));
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_RED))));
- enqueueBroadcast(makeOrderedBroadcastRecord(timezoneSecond, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
- resultToSecond, null));
- }
+ enqueueBroadcast(makeOrderedBroadcastRecord(timezoneFirst, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE),
+ makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
+ resultToFirst, null));
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_RED))));
+ enqueueBroadcast(makeOrderedBroadcastRecord(timezoneSecond, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_GREEN)),
+ resultToSecond, null));
waitForIdle();
final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
@@ -1836,14 +1819,12 @@
timeTickFirst.putExtra(Intent.EXTRA_INDEX, "third");
timeTickThird.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
- enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
- enqueueBroadcast(makeBroadcastRecord(timeTickThird, callerApp,
- List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
- }
+ enqueueBroadcast(makeBroadcastRecord(timeTickFirst, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+ enqueueBroadcast(makeBroadcastRecord(timeTickSecond, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
+ enqueueBroadcast(makeBroadcastRecord(timeTickThird, callerApp,
+ List.of(makeManifestReceiver(PACKAGE_BLUE, CLASS_BLUE))));
waitForIdle();
final IApplicationThread blueThread = mAms.getProcessRecordLocked(PACKAGE_BLUE,
@@ -1878,26 +1859,26 @@
assertTrue(mQueue.isIdleLocked());
assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
- enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
- List.of(makeRegisteredReceiver(receiverApp))));
- afterFirst = SystemClock.uptimeMillis();
+ final Intent timezone = new Intent(Intent.ACTION_TIMEZONE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(timezone, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+ afterFirst = SystemClock.uptimeMillis();
- assertFalse(mQueue.isIdleLocked());
- assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
- assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
+ assertFalse(mQueue.isIdleLocked());
+ assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
+ assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
- final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
- List.of(makeRegisteredReceiver(receiverApp))));
- afterSecond = SystemClock.uptimeMillis() + 10;
+ final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(makeRegisteredReceiver(receiverApp))));
+ afterSecond = SystemClock.uptimeMillis() + 10;
- assertFalse(mQueue.isIdleLocked());
- assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
- assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
- assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
- }
+ assertFalse(mQueue.isIdleLocked());
+ assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst));
+ assertFalse(mQueue.isBeyondBarrierLocked(afterFirst));
+ assertFalse(mQueue.isBeyondBarrierLocked(afterSecond));
+
+ mLooper.release();
mQueue.waitForBarrier(LOG_WRITER_INFO);
assertTrue(mQueue.isBeyondBarrierLocked(afterFirst));
@@ -2048,25 +2029,23 @@
final ProcessRecord receiverBlueApp = makeActiveProcessRecord(PACKAGE_BLUE);
final Intent airplane = new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED);
- try (SyncBarrier b = new SyncBarrier(mHandlerThread)) {
- final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
- final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
- final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
- final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
- enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
- List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
+ final Object greenReceiver = makeRegisteredReceiver(receiverGreenApp);
+ final Object blueReceiver = makeRegisteredReceiver(receiverBlueApp);
+ final Object yellowReceiver = makeManifestReceiver(PACKAGE_YELLOW, CLASS_YELLOW);
+ final Object orangeReceiver = makeManifestReceiver(PACKAGE_ORANGE, CLASS_ORANGE);
+ enqueueBroadcast(makeBroadcastRecord(airplane, callerApp,
+ List.of(greenReceiver, blueReceiver, yellowReceiver, orangeReceiver)));
- doAnswer(invocation -> {
- final BroadcastRecord r = invocation.getArgument(0);
- final Object o = invocation.getArgument(1);
- if (airplane.getAction().equals(r.intent.getAction())
- && (isReceiverEquals(o, greenReceiver)
- || isReceiverEquals(o, orangeReceiver))) {
- return "test skipped receiver";
- }
- return null;
- }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
- }
+ doAnswer(invocation -> {
+ final BroadcastRecord r = invocation.getArgument(0);
+ final Object o = invocation.getArgument(1);
+ if (airplane.getAction().equals(r.intent.getAction())
+ && (isReceiverEquals(o, greenReceiver)
+ || isReceiverEquals(o, orangeReceiver))) {
+ return "test skipped receiver";
+ }
+ return null;
+ }).when(mSkipPolicy).shouldSkipMessage(any(BroadcastRecord.class), any());
waitForIdle();
// Verify that only blue and yellow receiver apps received the broadcast.
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
index df2f59a..e24354f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobNotificationCoordinatorTest.java
@@ -149,7 +149,8 @@
.enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
- coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED,
+ jsc.getRunningJobLocked());
verify(mNotificationManagerInternal, never())
.cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
anyInt(), anyInt());
@@ -170,7 +171,8 @@
.enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
- coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_UNDEFINED,
+ jsc.getRunningJobLocked());
verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -292,7 +294,8 @@
eq(notificationId2), eq(notification2), eq(UserHandle.getUserId(uid)));
// Remove the first job. Only the first notification should be removed.
- coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+ jsc1.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId1), eq(UserHandle.getUserId(uid)));
@@ -300,7 +303,8 @@
.cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
eq(notificationId2), anyInt());
- coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+ jsc2.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId2), eq(UserHandle.getUserId(uid)));
@@ -335,12 +339,14 @@
eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
// Remove the first job. The notification shouldn't be touched because of the 2nd job.
- coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+ jsc1.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal, never())
.cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
anyInt(), anyInt());
- coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+ jsc2.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -376,7 +382,8 @@
eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid2)));
// Remove the first job. Only the first notification should be removed.
- coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+ jsc1.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid1), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid1)));
@@ -384,7 +391,8 @@
.cancelNotification(anyString(), anyString(), eq(uid2), anyInt(), any(),
anyInt(), anyInt());
- coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+ jsc2.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid2), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid2)));
@@ -421,7 +429,8 @@
eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
// Remove the first job. Only the first notification should be removed.
- coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_UNDEFINED,
+ jsc1.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(pkg1), eq(pkg1), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -429,7 +438,8 @@
.cancelNotification(anyString(), anyString(), eq(uid), anyInt(), any(),
anyInt(), anyInt());
- coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED);
+ coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_UNDEFINED,
+ jsc2.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(pkg2), eq(pkg2), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -450,7 +460,8 @@
.enqueueNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(notification), eq(UserHandle.getUserId(uid)));
- coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_USER);
+ coordinator.removeNotificationAssociation(jsc, JobParameters.STOP_REASON_USER,
+ jsc.getRunningJobLocked());
verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
@@ -485,12 +496,14 @@
eq(notificationId), eq(notification2), eq(UserHandle.getUserId(uid)));
// Remove the first job. The notification shouldn't be touched because of the 2nd job.
- coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_USER);
+ coordinator.removeNotificationAssociation(jsc1, JobParameters.STOP_REASON_USER,
+ jsc1.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal, never())
.cancelNotification(anyString(), anyString(), anyInt(), anyInt(), any(),
anyInt(), anyInt());
- coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_USER);
+ coordinator.removeNotificationAssociation(jsc2, JobParameters.STOP_REASON_USER,
+ jsc2.getRunningJobLocked());
inOrder.verify(mNotificationManagerInternal)
.cancelNotification(eq(TEST_PACKAGE), eq(TEST_PACKAGE), eq(uid), eq(pid), any(),
eq(notificationId), eq(UserHandle.getUserId(uid)));
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
index 02fdfad..754f409 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/restrictions/ThermalStatusRestrictionTest.java
@@ -276,9 +276,9 @@
assertFalse(mThermalStatusRestriction.isJobRestricted(ejRunning));
assertTrue(mThermalStatusRestriction.isJobRestricted(ejRunningLong));
assertFalse(mThermalStatusRestriction.isJobRestricted(ui));
- assertTrue(mThermalStatusRestriction.isJobRestricted(uiRetried));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(uiRetried));
assertFalse(mThermalStatusRestriction.isJobRestricted(uiRunning));
- assertTrue(mThermalStatusRestriction.isJobRestricted(uiRunningLong));
+ assertFalse(mThermalStatusRestriction.isJobRestricted(uiRunningLong));
mStatusChangedListener.onThermalStatusChanged(THERMAL_STATUS_SEVERE);
diff --git a/services/tests/servicestests/res/xml/irq_device_map_3.xml b/services/tests/servicestests/res/xml/irq_device_map_3.xml
index 1d2a7d3..7e2529a 100644
--- a/services/tests/servicestests/res/xml/irq_device_map_3.xml
+++ b/services/tests/servicestests/res/xml/irq_device_map_3.xml
@@ -23,4 +23,7 @@
<device name="test.wifi.device">
<subsystem>Wifi</subsystem>
</device>
+ <device name="test.sound_trigger.device">
+ <subsystem>Sound_trigger</subsystem>
+ </device>
</irq-device-map>
\ No newline at end of file
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
index 120ddf6..b539a76 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerServiceMigrationTest.java
@@ -86,6 +86,7 @@
// Test setting default restrictions for managed profile.
@Test
+ @Ignore("b/277916462")
public void testMigration_managedProfileOwner() throws Exception {
// Create a managed profile user.
final File user10dir = getServices().addUser(10, 0,
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 dd81abe..16aadac 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -1368,6 +1368,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testClearDeviceOwner() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -1955,6 +1956,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserRestriction_asDo() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -2123,6 +2125,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserRestriction_asPo() {
setAsProfileOwner(admin1);
@@ -2259,6 +2262,7 @@
);
@Test
+ @Ignore("b/277916462")
public void testSetUserRestriction_asPoOfOrgOwnedDevice() throws Exception {
final int MANAGED_PROFILE_ADMIN_UID =
UserHandle.getUid(CALLER_USER_HANDLE, DpmMockContext.SYSTEM_UID);
@@ -2334,6 +2338,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testNoDefaultEnabledUserRestrictions() throws Exception {
mContext.callerPermissions.add(permission.MANAGE_DEVICE_ADMINS);
mContext.callerPermissions.add(permission.MANAGE_USERS);
@@ -2925,6 +2930,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testCreateAdminSupportIntent() throws Exception {
// Setup device owner.
mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID;
@@ -4993,6 +4999,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception {
setupProfileOwner();
configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE);
@@ -7023,6 +7030,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserControlDisabledPackages_asDO() throws Exception {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
@@ -7038,6 +7046,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserControlDisabledPackages_asPO() {
final List<String> testPackages = new ArrayList<>();
testPackages.add("package_1");
@@ -7776,6 +7785,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction()
throws Exception {
setDeviceOwner();
@@ -7858,6 +7868,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUninstallBlocked_financeDo_success() throws Exception {
String packageName = "com.android.foo.package";
setDeviceOwner();
@@ -7871,6 +7882,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testSetUserControlDisabledPackages_financeDo_success() throws Exception {
List<String> packages = new ArrayList<>();
packages.add("com.android.foo.package");
@@ -7960,6 +7972,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testAddPersistentPreferredActivity_financeDo_success() throws Exception {
IntentFilter filter = new IntentFilter();
ComponentName target = new ComponentName(admin2.getPackageName(), "test.class");
@@ -7975,6 +7988,7 @@
}
@Test
+ @Ignore("b/277916462")
public void testClearPackagePersistentPreferredActvities_financeDo_success() throws Exception {
String packageName = admin2.getPackageName();
setDeviceOwner();
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index a0fb3de..21a11bc 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -763,7 +763,7 @@
ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
dreamManagerStateListener.capture());
- dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false);
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
when(mBatteryManagerInternalMock.getPlugType())
.thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -793,7 +793,7 @@
ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
dreamManagerStateListener.capture());
- dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true);
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(true);
when(mBatteryManagerInternalMock.getPlugType())
.thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -823,7 +823,7 @@
ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
dreamManagerStateListener.capture());
- dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(true);
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(true);
when(mBatteryManagerInternalMock.getPlugType())
.thenReturn(BatteryManager.BATTERY_PLUGGED_DOCK);
@@ -831,7 +831,7 @@
forceAwake(); // Needs to be awake first before it can dream.
forceDream();
- dreamManagerStateListener.getValue().onKeepDreamingWhenUndockedChanged(false);
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
setPluggedIn(false);
@@ -839,6 +839,34 @@
}
@Test
+ public void testWakefulnessDream_shouldStopDreamingWhenUnplugging_whenDreamPrevents() {
+ // Make sure "unplug turns on screen" is configured to true.
+ when(mResourcesSpy.getBoolean(com.android.internal.R.bool.config_unplugTurnsOnScreen))
+ .thenReturn(true);
+
+ createService();
+ startSystem();
+
+ ArgumentCaptor<DreamManagerInternal.DreamManagerStateListener> dreamManagerStateListener =
+ ArgumentCaptor.forClass(DreamManagerInternal.DreamManagerStateListener.class);
+ verify(mDreamManagerInternalMock).registerDreamManagerStateListener(
+ dreamManagerStateListener.capture());
+
+ when(mBatteryManagerInternalMock.getPlugType())
+ .thenReturn(BatteryManager.BATTERY_PLUGGED_AC);
+ setPluggedIn(true);
+
+ forceAwake(); // Needs to be awake first before it can dream.
+ forceDream();
+ dreamManagerStateListener.getValue().onKeepDreamingWhenUnpluggingChanged(false);
+ when(mBatteryManagerInternalMock.getPlugType()).thenReturn(0);
+ setPluggedIn(false);
+
+ assertThat(mService.getGlobalWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ }
+
+
+ @Test
public void testWakefulnessDoze_goToSleep() {
createService();
// Start with AWAKE state
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
similarity index 80%
rename from services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
index 7cf5bc8..dca67d6 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/CpuWakeupStatsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/CpuWakeupStatsTest.java
@@ -14,13 +14,14 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_ALARM;
+import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_UNKNOWN;
import static android.os.BatteryStatsInternal.CPU_WAKEUP_SUBSYSTEM_WIFI;
-import static com.android.server.power.stats.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
+import static com.android.server.power.stats.wakeups.CpuWakeupStats.WAKEUP_REASON_HALF_WINDOW_MS;
import static com.google.common.truth.Truth.assertThat;
@@ -34,6 +35,7 @@
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.frameworks.servicestests.R;
+import com.android.server.power.stats.wakeups.CpuWakeupStats.Wakeup;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -46,10 +48,11 @@
@RunWith(AndroidJUnit4.class)
public class CpuWakeupStatsTest {
private static final String KERNEL_REASON_ALARM_IRQ = "120 test.alarm.device";
- private static final String KERNEL_REASON_WIFI_IRQ = "120 test.wifi.device";
+ private static final String KERNEL_REASON_WIFI_IRQ = "130 test.wifi.device";
+ private static final String KERNEL_REASON_SOUND_TRIGGER_IRQ = "129 test.sound_trigger.device";
private static final String KERNEL_REASON_UNKNOWN_IRQ = "140 test.unknown.device";
private static final String KERNEL_REASON_UNKNOWN = "free-form-reason test.alarm.device";
- private static final String KERNEL_REASON_UNSUPPORTED = "-1 test.alarm.device";
+ private static final String KERNEL_REASON_ALARM_ABNORMAL = "-1 test.alarm.device";
private static final String KERNEL_REASON_ABORT = "Abort: due to test.alarm.device";
private static final int TEST_UID_1 = 13239823;
@@ -140,6 +143,40 @@
}
@Test
+ public void soundTriggerIrqAttributionSolo() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+ final long wakeupTime = 1247121;
+
+ populateDefaultProcStates(obj);
+
+ obj.noteWakeupTimeAndReason(wakeupTime, 1, KERNEL_REASON_SOUND_TRIGGER_IRQ);
+
+ // Outside the window, so should be ignored.
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ wakeupTime - WAKEUP_REASON_HALF_WINDOW_MS - 1, TEST_UID_1);
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER,
+ wakeupTime + WAKEUP_REASON_HALF_WINDOW_MS + 1, TEST_UID_2);
+ // Should be attributed
+ obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER, wakeupTime + 5, TEST_UID_3,
+ TEST_UID_5);
+
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution).isNotNull();
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+ TEST_UID_1)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+ TEST_UID_2)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_3)).isEqualTo(
+ TEST_PROC_STATE_3);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).indexOfKey(
+ TEST_UID_4)).isLessThan(0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_SOUND_TRIGGER).get(TEST_UID_5)).isEqualTo(
+ TEST_PROC_STATE_5);
+ }
+
+ @Test
public void wifiIrqAttributionSolo() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
final long wakeupTime = 12423121;
@@ -268,22 +305,39 @@
}
@Test
- public void unsupportedWakeupIgnored() {
+ public void abnormalAlarmAttribution() {
final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+ populateDefaultProcStates(obj);
long wakeupTime = 970934;
- obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_UNSUPPORTED);
+ obj.noteWakeupTimeAndReason(wakeupTime, 34, KERNEL_REASON_ALARM_ABNORMAL);
- // Should be ignored as this type of wakeup is unsupported.
- assertThat(obj.mWakeupEvents.size()).isEqualTo(0);
+ assertThat(obj.mWakeupEvents.size()).isEqualTo(1);
+ assertThat(obj.mWakeupEvents.valueAt(0).mType).isEqualTo(Wakeup.TYPE_ABNORMAL);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime + 5, TEST_UID_3);
obj.noteWakingActivity(CPU_WAKEUP_SUBSYSTEM_ALARM, wakeupTime - 3, TEST_UID_4);
- // Any nearby activity should not end up in the attribution map.
- assertThat(obj.mWakeupAttribution.size()).isEqualTo(0);
+ final SparseArray<SparseIntArray> attribution = obj.mWakeupAttribution.get(wakeupTime);
+ assertThat(attribution.size()).isEqualTo(1);
+ assertThat(attribution.contains(CPU_WAKEUP_SUBSYSTEM_ALARM)).isTrue();
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_1)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_2)).isLessThan(
+ 0);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_3)).isEqualTo(
+ TEST_PROC_STATE_3);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).get(TEST_UID_4)).isEqualTo(
+ TEST_PROC_STATE_4);
+ assertThat(attribution.get(CPU_WAKEUP_SUBSYSTEM_ALARM).indexOfKey(TEST_UID_5)).isLessThan(
+ 0);
+ }
- wakeupTime = 883124;
+ @Test
+ public void abortIgnored() {
+ final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler);
+
+ long wakeupTime = 883124;
obj.noteWakeupTimeAndReason(wakeupTime, 3, KERNEL_REASON_ABORT);
// Should be ignored as this type of wakeup is unsupported.
diff --git a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
similarity index 98%
rename from services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
rename to services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
index 43d9e60..47a8f49 100644
--- a/services/tests/servicestests/src/com/android/server/power/stats/IrqDeviceMapTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/stats/wakeups/IrqDeviceMapTest.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.server.power.stats;
+package com.android.server.power.stats.wakeups;
import static com.google.common.truth.Truth.assertThat;
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java
new file mode 100644
index 0000000..6b924f3
--- /dev/null
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/BoundsInfoDrawHelper.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.android.test.handwritingime;
+
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_EDITOR_BOUNDS;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_NONE;
+import static com.google.android.test.handwritingime.HandwritingIme.BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.RectF;
+import android.view.View;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorBoundsInfo;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.graphics.ColorUtils;
+
+import java.util.List;
+
+public class BoundsInfoDrawHelper {
+ private static final Paint sPaint = new Paint();
+ private static final int EDITOR_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.DKGRAY, 128);
+ private static final int HANDWRITING_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.BLUE, 128);
+ private static final int VISIBLE_LINE_BOUNDS_COLOR =
+ ColorUtils.setAlphaComponent(Color.MAGENTA, 128);
+
+ public static void draw(Canvas canvas, View inkView, int boundsInfoMode,
+ CursorAnchorInfo cursorAnchorInfo) {
+ if (boundsInfoMode == BOUNDS_INFO_NONE || cursorAnchorInfo == null) {
+ return;
+ }
+
+ // The matrix in CursorAnchorInfo transforms the editor coordinates to on-screen
+ // coordinates. We then transform the matrix from the on-screen coordinates to the
+ // inkView's coordinates. So the result matrix transforms the editor coordinates
+ // to the inkView coordinates.
+ final Matrix matrix = cursorAnchorInfo.getMatrix();
+ inkView.transformMatrixToLocal(matrix);
+
+ if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+ drawEditorBoundsInfo(canvas, matrix, cursorAnchorInfo.getEditorBoundsInfo());
+ }
+
+ if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+ drawVisibleLineBounds(canvas, matrix, cursorAnchorInfo.getVisibleLineBounds());
+ }
+ }
+
+ private static void setPaintForEditorBoundsInfo() {
+ sPaint.reset();
+ sPaint.setStyle(Paint.Style.STROKE);
+ sPaint.setStrokeWidth(5f);
+ }
+
+ private static void drawEditorBoundsInfo(Canvas canvas, Matrix matrix,
+ @Nullable EditorBoundsInfo editorBoundsInfo) {
+ if (editorBoundsInfo == null) {
+ return;
+ }
+ final RectF editorBounds = editorBoundsInfo.getEditorBounds();
+ setPaintForEditorBoundsInfo();
+ if (editorBounds != null) {
+ final RectF localEditorBounds = new RectF(editorBounds);
+ matrix.mapRect(localEditorBounds);
+ sPaint.setColor(EDITOR_BOUNDS_COLOR);
+ canvas.drawRect(localEditorBounds, sPaint);
+ }
+
+ final RectF handwritingBounds = editorBoundsInfo.getHandwritingBounds();
+ if (handwritingBounds != null) {
+ final RectF localHandwritingBounds = new RectF(handwritingBounds);
+ matrix.mapRect(localHandwritingBounds);
+ sPaint.setColor(HANDWRITING_BOUNDS_COLOR);
+ canvas.drawRect(localHandwritingBounds, sPaint);
+ }
+ }
+
+ private static void setPaintForVisibleLineBounds() {
+ sPaint.reset();
+ sPaint.setStyle(Paint.Style.STROKE);
+ sPaint.setStrokeWidth(2f);
+ sPaint.setColor(VISIBLE_LINE_BOUNDS_COLOR);
+ }
+
+ private static void drawVisibleLineBounds(Canvas canvas, Matrix matrix,
+ List<RectF> visibleLineBounds) {
+ if (visibleLineBounds.isEmpty()) {
+ return;
+ }
+ setPaintForVisibleLineBounds();
+ for (RectF lineBound : visibleLineBounds) {
+ matrix.mapRect(lineBound);
+ canvas.drawRect(lineBound, sPaint);
+ }
+ }
+}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
index 2fd2368..8380dcf 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/HandwritingIme.java
@@ -25,7 +25,9 @@
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
+import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.DeleteGesture;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.HandwritingGesture;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InsertGesture;
@@ -34,6 +36,7 @@
import android.view.inputmethod.SelectGesture;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
+import android.widget.CheckBox;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.Spinner;
@@ -43,9 +46,6 @@
import java.util.function.IntConsumer;
public class HandwritingIme extends InputMethodService {
-
- public static final int HEIGHT_DP = 100;
-
private static final int OP_NONE = 0;
private static final int OP_SELECT = 1;
private static final int OP_DELETE = 2;
@@ -62,6 +62,12 @@
private Spinner mRichGestureGranularitySpinner;
private PointF mRichGestureStartPoint;
+ static final int BOUNDS_INFO_NONE = 0;
+ static final int BOUNDS_INFO_VISIBLE_LINE_BOUNDS = 1;
+ static final int BOUNDS_INFO_EDITOR_BOUNDS = 2;
+ private int mBoundsInfoMode = BOUNDS_INFO_NONE;
+ private LinearLayout mBoundsInfoCheckBoxes;
+
private final IntConsumer mResultConsumer = value -> Log.d(TAG, "Gesture result: " + value);
interface HandwritingFinisher {
@@ -201,12 +207,7 @@
public View onCreateInputView() {
Log.d(TAG, "onCreateInputView");
final ViewGroup view = new FrameLayout(this);
- final View inner = new View(this);
- final float density = getResources().getDisplayMetrics().density;
- final int height = (int) (HEIGHT_DP * density);
view.setPadding(0, 0, 0, 0);
- view.addView(inner, new FrameLayout.LayoutParams(
- FrameLayout.LayoutParams.MATCH_PARENT, height));
LinearLayout layout = new LinearLayout(this);
layout.setLayoutParams(new LinearLayout.LayoutParams(
@@ -214,9 +215,9 @@
layout.setOrientation(LinearLayout.VERTICAL);
layout.addView(getRichGestureActionsSpinner());
layout.addView(getRichGestureGranularitySpinner());
-
+ layout.addView(getBoundsInfoCheckBoxes());
+ layout.setBackgroundColor(getColor(R.color.holo_green_light));
view.addView(layout);
- inner.setBackgroundColor(getColor(R.color.holo_green_light));
return view;
}
@@ -228,7 +229,7 @@
mRichGestureModeSpinner = new Spinner(this);
mRichGestureModeSpinner.setPadding(100, 0, 100, 0);
mRichGestureModeSpinner.setTooltipText("Handwriting IME mode");
- String[] items = new String[] {
+ String[] items = new String[]{
"Handwriting IME - Rich gesture disabled",
"Rich gesture SELECT",
"Rich gesture DELETE",
@@ -259,6 +260,69 @@
return mRichGestureModeSpinner;
}
+ private void updateCursorAnchorInfo(int boundsInfoMode) {
+ final InputConnection ic = getCurrentInputConnection();
+ if (ic == null) return;
+
+ if (boundsInfoMode == BOUNDS_INFO_NONE) {
+ ic.requestCursorUpdates(0);
+ return;
+ }
+
+ final int cursorUpdateMode = InputConnection.CURSOR_UPDATE_MONITOR;
+ int cursorUpdateFilter = 0;
+ if ((boundsInfoMode & BOUNDS_INFO_EDITOR_BOUNDS) != 0) {
+ cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_EDITOR_BOUNDS;
+ }
+
+ if ((boundsInfoMode & BOUNDS_INFO_VISIBLE_LINE_BOUNDS) != 0) {
+ cursorUpdateFilter |= InputConnection.CURSOR_UPDATE_FILTER_VISIBLE_LINE_BOUNDS;
+ }
+ ic.requestCursorUpdates(cursorUpdateMode | cursorUpdateFilter);
+ }
+
+ private void updateBoundsInfoMode() {
+ if (mInk != null) {
+ mInk.setBoundsInfoMode(mBoundsInfoMode);
+ }
+ updateCursorAnchorInfo(mBoundsInfoMode);
+ }
+
+ private View getBoundsInfoCheckBoxes() {
+ if (mBoundsInfoCheckBoxes != null) {
+ return mBoundsInfoCheckBoxes;
+ }
+ mBoundsInfoCheckBoxes = new LinearLayout(this);
+ mBoundsInfoCheckBoxes.setPadding(100, 0, 100, 0);
+ mBoundsInfoCheckBoxes.setOrientation(LinearLayout.HORIZONTAL);
+
+ final CheckBox editorBoundsInfoCheckBox = new CheckBox(this);
+ editorBoundsInfoCheckBox.setText("EditorBoundsInfo");
+ editorBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mBoundsInfoMode |= BOUNDS_INFO_EDITOR_BOUNDS;
+ } else {
+ mBoundsInfoMode &= ~BOUNDS_INFO_EDITOR_BOUNDS;
+ }
+ updateBoundsInfoMode();
+ });
+
+ final CheckBox visibleLineBoundsInfoCheckBox = new CheckBox(this);
+ visibleLineBoundsInfoCheckBox.setText("VisibleLineBounds");
+ visibleLineBoundsInfoCheckBox.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (isChecked) {
+ mBoundsInfoMode |= BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+ } else {
+ mBoundsInfoMode &= ~BOUNDS_INFO_VISIBLE_LINE_BOUNDS;
+ }
+ updateBoundsInfoMode();
+ });
+
+ mBoundsInfoCheckBoxes.addView(editorBoundsInfoCheckBox);
+ mBoundsInfoCheckBoxes.addView(visibleLineBoundsInfoCheckBox);
+ return mBoundsInfoCheckBoxes;
+ }
+
private View getRichGestureGranularitySpinner() {
if (mRichGestureGranularitySpinner != null) {
return mRichGestureGranularitySpinner;
@@ -294,6 +358,7 @@
Log.d(TAG, "onPrepareStylusHandwriting ");
if (mInk == null) {
mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer());
+ mInk.setBoundsInfoMode(mBoundsInfoMode);
}
}
@@ -323,4 +388,16 @@
private boolean areRichGesturesEnabled() {
return mRichGestureMode != OP_NONE;
}
+
+ @Override
+ public void onUpdateCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ if (mInk != null) {
+ mInk.setCursorAnchorInfo(cursorAnchorInfo);
+ }
+ }
+
+ @Override
+ public void onStartInput(EditorInfo attribute, boolean restarting) {
+ updateCursorAnchorInfo(mBoundsInfoMode);
+ }
}
diff --git a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
index e94c79e..86b324c 100644
--- a/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
+++ b/tests/HandwritingIme/src/com/google/android/test/handwritingime/InkView.java
@@ -26,6 +26,7 @@
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowMetrics;
+import android.view.inputmethod.CursorAnchorInfo;
class InkView extends View {
private static final long FINISH_TIMEOUT = 1500;
@@ -37,6 +38,9 @@
private static final float STYLUS_MOVE_TOLERANCE = 1;
private Runnable mFinishRunnable;
+ private CursorAnchorInfo mCursorAnchorInfo;
+ private int mBoundsInfoMode;
+
InkView(Context context, HandwritingIme.HandwritingFinisher hwController,
HandwritingIme.StylusConsumer consumer) {
super(context);
@@ -66,6 +70,7 @@
canvas.drawPath(mPath, mPaint);
canvas.drawARGB(20, 255, 50, 50);
+ BoundsInfoDrawHelper.draw(canvas, this, mBoundsInfoMode, mCursorAnchorInfo);
}
private void stylusStart(float x, float y) {
@@ -156,4 +161,15 @@
return mFinishRunnable;
}
+ void setCursorAnchorInfo(CursorAnchorInfo cursorAnchorInfo) {
+ mCursorAnchorInfo = cursorAnchorInfo;
+ invalidate();
+ }
+
+ void setBoundsInfoMode(int boundsInfoMode) {
+ if (boundsInfoMode != mBoundsInfoMode) {
+ invalidate();
+ }
+ mBoundsInfoMode = boundsInfoMode;
+ }
}
diff --git a/tests/WindowAnimationJank/Android.bp b/tests/WindowAnimationJank/Android.bp
index ed86aa5..8542f88 100644
--- a/tests/WindowAnimationJank/Android.bp
+++ b/tests/WindowAnimationJank/Android.bp
@@ -25,7 +25,7 @@
name: "WindowAnimationJank",
srcs: ["src/**/*.java"],
static_libs: [
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"androidx.test.janktesthelper",
"junit",
],
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
index 2531464..48a359c 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java
@@ -18,11 +18,12 @@
import android.content.ComponentName;
import android.content.Intent;
import android.os.SystemClock;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.BySelector;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject2;
-import android.support.test.uiautomator.Until;
+
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
/**
* Set of helpers to manipulate test activities.
diff --git a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
index a8ace162..cb7c511 100644
--- a/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
+++ b/tests/WindowAnimationJank/src/android/windowanimationjank/WindowAnimationJankTestBase.java
@@ -16,9 +16,8 @@
package android.windowanimationjank;
-import android.support.test.uiautomator.UiDevice;
-
import androidx.test.jank.JankTestBase;
+import androidx.test.uiautomator.UiDevice;
/**
* This adds additional system level jank monitor and its result is merged with primary monitor
diff --git a/tests/testables/src/android/testing/TestableResources.java b/tests/testables/src/android/testing/TestableResources.java
index c60f07d..27d5b66 100644
--- a/tests/testables/src/android/testing/TestableResources.java
+++ b/tests/testables/src/android/testing/TestableResources.java
@@ -59,7 +59,8 @@
* Since resource ids are unique there is a single addOverride that will override the value
* whenever it is gotten regardless of which method is used (i.e. getColor or getDrawable).
* </p>
- * @param id The resource id to be overridden
+ *
+ * @param id The resource id to be overridden
* @param value The value of the resource, null to cause a {@link Resources.NotFoundException}
* when gotten.
*/
@@ -74,28 +75,33 @@
* cause a {@link Resources.NotFoundException} whereas removing the override will actually
* switch back to returning the default/real value of the resource.
* </p>
- * @param id
*/
public void removeOverride(int id) {
mOverrides.remove(id);
}
private Object answer(InvocationOnMock invocationOnMock) throws Throwable {
- try {
- int id = invocationOnMock.getArgument(0);
- int index = mOverrides.indexOfKey(id);
- if (index >= 0) {
- Object value = mOverrides.valueAt(index);
- if (value == null) throw new Resources.NotFoundException();
- return value;
+ // Only try to override methods with an integer first argument
+ if (invocationOnMock.getArguments().length > 0) {
+ Object argument = invocationOnMock.getArgument(0);
+ if (argument instanceof Integer) {
+ try {
+ int id = (Integer)argument;
+ int index = mOverrides.indexOfKey(id);
+ if (index >= 0) {
+ Object value = mOverrides.valueAt(index);
+ if (value == null) throw new Resources.NotFoundException();
+ return value;
+ }
+ } catch (Resources.NotFoundException e) {
+ // Let through NotFoundException.
+ throw e;
+ } catch (Throwable t) {
+ // Generic catching for the many things that can go wrong, fall back to
+ // the real implementation.
+ Log.i(TAG, "Falling back to default resources call " + t);
+ }
}
- } catch (Resources.NotFoundException e) {
- // Let through NotFoundException.
- throw e;
- } catch (Throwable t) {
- // Generic catching for the many things that can go wrong, fall back to
- // the real implementation.
- Log.i(TAG, "Falling back to default resources call " + t);
}
return invocationOnMock.callRealMethod();
}