Merge "Fix BP did not dismiss after swiping up the notification bar" into udc-dev
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp
index 9064b44..2590fe3 100644
--- a/apct-tests/perftests/blobstore/Android.bp
+++ b/apct-tests/perftests/blobstore/Android.bp
@@ -29,7 +29,7 @@
"androidx.test.rules",
"androidx.annotation_annotation",
"apct-perftests-utils",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"collector-device-lib-platform",
"androidx.benchmark_benchmark-macro",
],
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
index 0208dab..4e4780f 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/AtraceUtils.java
@@ -21,10 +21,10 @@
import android.os.ParcelFileDescriptor;
import android.perftests.utils.TraceMarkParser;
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
-import android.support.test.uiautomator.UiDevice;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import java.io.BufferedReader;
import java.io.IOException;
diff --git a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
index 03e5468..3cd9f50 100644
--- a/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
+++ b/apct-tests/perftests/blobstore/src/com/android/perftests/blob/BlobStorePerfTests.java
@@ -25,12 +25,12 @@
import android.perftests.utils.PerfManualStatusReporter;
import android.perftests.utils.TraceMarkParser;
import android.perftests.utils.TraceMarkParser.TraceMarkSlice;
-import android.support.test.uiautomator.UiDevice;
import android.util.DataUnit;
import androidx.benchmark.macro.MacrobenchmarkScope;
import androidx.test.filters.LargeTest;
import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.uiautomator.UiDevice;
import com.android.utils.blob.FakeBlobData;
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/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
index 6998081..b6dc32a 100644
--- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
+++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java
@@ -21,6 +21,7 @@
import android.app.backup.BackupManager;
import android.app.backup.BackupManagerMonitor;
import android.app.backup.BackupProgress;
+import android.app.backup.BackupRestoreEventLogger;
import android.app.backup.BackupTransport;
import android.app.backup.IBackupManager;
import android.app.backup.IBackupManagerMonitor;
@@ -821,14 +822,22 @@
doRestorePackage(arg);
} else {
try {
+ @Monitor int monitor = Monitor.OFF;
+
long token = Long.parseLong(arg, 16);
HashSet<String> filter = null;
while ((arg = nextArg()) != null) {
- if (filter == null) filter = new HashSet<String>();
- filter.add(arg);
+ if (arg.equals("--monitor")) {
+ monitor = Monitor.NORMAL;
+ } else if (arg.equals("--monitor-verbose")) {
+ monitor = Monitor.VERBOSE;
+ } else {
+ if (filter == null) filter = new HashSet<String>();
+ filter.add(arg);
+ }
}
- doRestoreAll(userId, token, filter);
+ doRestoreAll(userId, token, filter, monitor);
} catch (NumberFormatException e) {
showUsage();
return;
@@ -841,7 +850,8 @@
System.err.println("'restore <token> <package>'.");
}
- private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter) {
+ private void doRestoreAll(@UserIdInt int userId, long token, HashSet<String> filter,
+ @Monitor int monitorState) {
RestoreObserver observer = new RestoreObserver();
try {
@@ -852,8 +862,11 @@
return;
}
RestoreSet[] sets = null;
- // TODO implement monitor here
- int err = mRestore.getAvailableRestoreSets(observer, null);
+ BackupMonitor monitor =
+ (monitorState != Monitor.OFF)
+ ? new BackupMonitor(monitorState == Monitor.VERBOSE)
+ : null;
+ int err = mRestore.getAvailableRestoreSets(observer, monitor);
if (err == 0) {
observer.waitForCompletion();
sets = observer.sets;
@@ -862,12 +875,12 @@
if (s.token == token) {
System.out.println("Scheduling restore: " + s.name);
if (filter == null) {
- didRestore = (mRestore.restoreAll(token, observer, null) == 0);
+ didRestore = (mRestore.restoreAll(token, observer, monitor) == 0);
} else {
String[] names = new String[filter.size()];
filter.toArray(names);
didRestore = (mRestore.restorePackages(token, observer, names,
- null) == 0);
+ monitor) == 0);
}
break;
}
@@ -958,8 +971,8 @@
System.err.println(" bmgr list transports [-c]");
System.err.println(" bmgr list sets");
System.err.println(" bmgr transport WHICH|-c WHICH_COMPONENT");
- System.err.println(" bmgr restore TOKEN");
- System.err.println(" bmgr restore TOKEN PACKAGE...");
+ System.err.println(" bmgr restore TOKEN [--monitor|--monitor-verbose]");
+ System.err.println(" bmgr restore TOKEN PACKAGE... [--monitor|--monitor-verbose]");
System.err.println(" bmgr run");
System.err.println(" bmgr wipe TRANSPORT PACKAGE");
System.err.println(" bmgr fullbackup PACKAGE...");
@@ -1005,12 +1018,18 @@
System.err.println("restore operation from the currently active transport. It will deliver");
System.err.println("the restore set designated by the TOKEN argument to each application");
System.err.println("that had contributed data to that restore set.");
+ System.err.println(" --monitor flag prints monitor events (important events and errors");
+ System.err.println(" encountered during restore).");
+ System.err.println(" --monitor-verbose flag prints monitor events with all keys.");
System.err.println("");
System.err.println("The 'restore' command when given a token and one or more package names");
System.err.println("initiates a restore operation of just those given packages from the restore");
System.err.println("set designated by the TOKEN argument. It is effectively the same as the");
System.err.println("'restore' operation supplying only a token, but applies a filter to the");
System.err.println("set of applications to be restored.");
+ System.err.println(" --monitor flag prints monitor events (important events and errors");
+ System.err.println(" encountered during restore).");
+ System.err.println(" --monitor-verbose flag prints monitor events with all keys.");
System.err.println("");
System.err.println("The 'run' command causes any scheduled backup operation to be initiated");
System.err.println("immediately, without the usual waiting period for batching together");
@@ -1026,7 +1045,8 @@
System.err.println("");
System.err.println("The 'backupnow' command runs an immediate backup for one or more packages.");
System.err.println(" --all flag runs backup for all eligible packages.");
- System.err.println(" --monitor flag prints monitor events.");
+ System.err.println(" --monitor flag prints monitor events (important events and errors");
+ System.err.println(" encountered during backup).");
System.err.println(" --monitor-verbose flag prints monitor events with all keys.");
System.err.println("For each package it will run key/value or full data backup ");
System.err.println("depending on the package's manifest declarations.");
@@ -1076,6 +1096,37 @@
out.append("(v").append(version).append(")");
}
}
+ if (event.containsKey(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS)) {
+ ArrayList<BackupRestoreEventLogger.DataTypeResult> results =
+ event.getParcelableArrayList(
+ BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS,
+ BackupRestoreEventLogger.DataTypeResult.class);
+ out.append(", results = [");
+ for (BackupRestoreEventLogger.DataTypeResult result : results) {
+ out.append("\n{\n\tdataType: ");
+ out.append(result.getDataType());
+ out.append("\n\tsuccessCount: ");
+ out.append(result.getSuccessCount());
+ out.append("\n\tfailCount: ");
+ out.append(result.getFailCount());
+ out.append("\n\tmetadataHash: ");
+ out.append(Arrays.toString(result.getMetadataHash()));
+
+ if (!result.getErrors().isEmpty()) {
+ out.append("\n\terrors: [");
+ for (String error : result.getErrors().keySet()) {
+ out.append(error);
+ out.append(": ");
+ out.append(result.getErrors().get(error));
+ out.append(";");
+ }
+ out.append("]");
+ }
+ out.append("\n}");
+
+ }
+ out.append("]");
+ }
if (mVerbose) {
Set<String> remainingKeys = new ArraySet<>(event.keySet());
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_ID);
@@ -1083,6 +1134,7 @@
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_NAME);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_LONG_VERSION);
remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_EVENT_PACKAGE_VERSION);
+ remainingKeys.remove(BackupManagerMonitor.EXTRA_LOG_AGENT_LOGGING_RESULTS);
if (!remainingKeys.isEmpty()) {
out.append(", other keys =");
for (String key : remainingKeys) {
@@ -1192,6 +1244,8 @@
return "NO_PACKAGES";
case BackupManagerMonitor.LOG_EVENT_ID_TRANSPORT_IS_NULL:
return "TRANSPORT_IS_NULL";
+ case BackupManagerMonitor.LOG_EVENT_ID_AGENT_LOGGING_RESULTS:
+ return "AGENT_LOGGING_RESULTS";
default:
return "UNKNOWN_ID";
}
diff --git a/core/java/android/inputmethodservice/InkWindow.java b/core/java/android/inputmethodservice/InkWindow.java
index 15ed450..24d1c95 100644
--- a/core/java/android/inputmethodservice/InkWindow.java
+++ b/core/java/android/inputmethodservice/InkWindow.java
@@ -195,6 +195,7 @@
Objects.requireNonNull(decor);
final ViewRootImpl viewRoot = decor.getViewRootImpl();
Objects.requireNonNull(viewRoot);
- viewRoot.enqueueInputEvent(event);
+ // The view root will own the event that we enqueue, so provide a copy of the event.
+ viewRoot.enqueueInputEvent(MotionEvent.obtain(event));
}
}
diff --git a/core/java/android/nfc/tech/NdefFormatable.java b/core/java/android/nfc/tech/NdefFormatable.java
index f19d302..2240fe7 100644
--- a/core/java/android/nfc/tech/NdefFormatable.java
+++ b/core/java/android/nfc/tech/NdefFormatable.java
@@ -124,6 +124,9 @@
try {
int serviceHandle = mTag.getServiceHandle();
INfcTag tagService = mTag.getTagService();
+ if (tagService == null) {
+ throw new IOException();
+ }
int errorCode = tagService.formatNdef(serviceHandle, MifareClassic.KEY_DEFAULT);
switch (errorCode) {
case ErrorCodes.SUCCESS:
diff --git a/core/java/android/os/image/DynamicSystemClient.java b/core/java/android/os/image/DynamicSystemClient.java
index 63259ed..218ecc8 100644
--- a/core/java/android/os/image/DynamicSystemClient.java
+++ b/core/java/android/os/image/DynamicSystemClient.java
@@ -202,6 +202,13 @@
public static final String ACTION_NOTIFY_IF_IN_USE =
"android.os.image.action.NOTIFY_IF_IN_USE";
+ /**
+ * Intent action: hide notifications about the status of {@code DynamicSystem}.
+ * @hide
+ */
+ public static final String ACTION_HIDE_NOTIFICATION =
+ "android.os.image.action.HIDE_NOTIFICATION";
+
/*
* Intent Keys
*/
@@ -217,6 +224,28 @@
*/
public static final String KEY_USERDATA_SIZE = "KEY_USERDATA_SIZE";
+ /**
+ * Intent key: Whether to enable DynamicSystem immediately after installation is done.
+ * Note this will reboot the device automatically.
+ * @hide
+ */
+ public static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
+
+ /**
+ * Intent key: Whether to leave DynamicSystem on device reboot.
+ * False indicates a sticky mode where device stays in DynamicSystem across reboots.
+ * @hide
+ */
+ 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/credentials/CredentialProviderInfoFactory.java b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
index d2a4a66..fb2f4ad 100644
--- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java
+++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java
@@ -42,7 +42,6 @@
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.util.Log;
import android.util.Slog;
import android.util.Xml;
@@ -54,7 +53,6 @@
import java.io.IOException;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -181,7 +179,8 @@
if (disableSystemAppVerificationForTests) {
Bundle metadata = serviceInfo.metaData;
if (metadata == null) {
- Slog.e(TAG, "isValidSystemProvider - metadata is null: " + serviceInfo);
+ Slog.w(TAG, "metadata is null while reading "
+ + "TEST_SYSTEM_PROVIDER_META_DATA_KEY: " + serviceInfo);
return false;
}
return metadata.getBoolean(
@@ -200,7 +199,7 @@
// 1. Get the metadata for the service.
final Bundle metadata = serviceInfo.metaData;
if (metadata == null) {
- Log.i(TAG, "populateMetadata - metadata is null");
+ Slog.w(TAG, "Metadata is null for provider: " + serviceInfo.getComponentName());
return builder;
}
@@ -209,12 +208,13 @@
try {
resources = pm.getResourcesForApplication(serviceInfo.applicationInfo);
} catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Failed to get app resources", e);
+ Slog.e(TAG, "Failed to get app resources", e);
}
// 3. Stop if we are missing data.
- if (metadata == null || resources == null) {
- Log.i(TAG, "populateMetadata - resources is null");
+ if (resources == null) {
+ Slog.w(TAG, "Resources are null for the serviceInfo being processed: "
+ + serviceInfo.getComponentName());
return builder;
}
@@ -222,7 +222,7 @@
try {
builder = extractXmlMetadata(context, builder, serviceInfo, pm, resources);
} catch (Exception e) {
- Log.e(TAG, "Failed to get XML metadata", e);
+ Slog.e(TAG, "Failed to get XML metadata", e);
}
return builder;
@@ -259,7 +259,7 @@
afsAttributes.getString(
R.styleable.CredentialProvider_settingsSubtitle));
} catch (Exception e) {
- Log.e(TAG, "Failed to get XML attr", e);
+ Slog.e(TAG, "Failed to get XML attr", e);
} finally {
if (afsAttributes != null) {
afsAttributes.recycle();
@@ -267,10 +267,10 @@
}
builder.addCapabilities(parseXmlProviderOuterCapabilities(parser, resources));
} else {
- Log.e(TAG, "Meta-data does not start with credential-provider-service tag");
+ Slog.w(TAG, "Meta-data does not start with credential-provider-service tag");
}
} catch (IOException | XmlPullParserException e) {
- Log.e(TAG, "Error parsing credential provider service meta-data", e);
+ Slog.e(TAG, "Error parsing credential provider service meta-data", e);
}
return builder;
@@ -329,7 +329,7 @@
return si;
}
} catch (RemoteException e) {
- Slog.v(TAG, e.getMessage());
+ Slog.e(TAG, "Unable to get serviceInfo", e);
}
throw new PackageManager.NameNotFoundException(serviceComponent.toString());
}
@@ -377,10 +377,8 @@
}
services.add(serviceInfo);
- } catch (SecurityException e) {
- Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
- } catch (PackageManager.NameNotFoundException e) {
- Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
+ } catch (SecurityException | PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Error getting info for " + serviceInfo, e);
}
}
return services;
@@ -432,7 +430,7 @@
return pp;
} catch (SecurityException e) {
// If the current user is not enrolled in DPM then this can throw a security error.
- Log.e(TAG, "Failed to get device policy: " + e);
+ Slog.e(TAG, "Failed to get device policy: " + e);
}
return null;
@@ -593,7 +591,7 @@
for (ResolveInfo resolveInfo : resolveInfos) {
final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
if (serviceInfo == null) {
- Log.i(TAG, "No serviceInfo found for resolveInfo so skipping this provider");
+ Slog.d(TAG, "No serviceInfo found for resolveInfo, so skipping provider");
continue;
}
@@ -608,10 +606,8 @@
if (!cpi.isSystemProvider()) {
services.add(cpi);
}
- } catch (SecurityException e) {
- Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
} catch (Exception e) {
- Slog.e(TAG, "Error getting info for " + serviceInfo + ": " + e);
+ Slog.e(TAG, "Error getting info for " + serviceInfo, e);
}
}
return services;
diff --git a/core/java/android/service/credentials/CredentialProviderService.java b/core/java/android/service/credentials/CredentialProviderService.java
index e87333f..53a5fd5 100644
--- a/core/java/android/service/credentials/CredentialProviderService.java
+++ b/core/java/android/service/credentials/CredentialProviderService.java
@@ -226,7 +226,7 @@
if (SERVICE_INTERFACE.equals(intent.getAction())) {
return mInterface.asBinder();
}
- Log.i(TAG, "Failed to bind with intent: " + intent);
+ Log.d(TAG, "Failed to bind with intent: " + intent);
return null;
}
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 e9984da..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 */);
}
/**
@@ -400,8 +403,7 @@
public @Nullable SurfacePackage getSurfacePackage() {
if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) {
return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"),
- mAccessibilityEmbeddedConnection,
- mWm.getFocusGrantToken(), mRemoteInterface);
+ mAccessibilityEmbeddedConnection, getFocusGrantToken(), mRemoteInterface);
} else {
return null;
}
@@ -497,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();
@@ -507,7 +518,7 @@
* @hide
*/
public IBinder getFocusGrantToken() {
- return mWm.getFocusGrantToken();
+ return mWm.getFocusGrantToken(getWindowToken().asBinder());
}
private void addWindowToken(WindowManager.LayoutParams attrs) {
diff --git a/core/java/android/view/WindowlessWindowManager.java b/core/java/android/view/WindowlessWindowManager.java
index 9868144..96bfb2d 100644
--- a/core/java/android/view/WindowlessWindowManager.java
+++ b/core/java/android/view/WindowlessWindowManager.java
@@ -106,8 +106,22 @@
mConfiguration.setTo(configuration);
}
- IBinder getFocusGrantToken() {
- return mFocusGrantToken;
+ IBinder getFocusGrantToken(IBinder window) {
+ synchronized (this) {
+ // This can only happen if someone requested the focusGrantToken before setView was
+ // called for the SCVH. In that case, use the root focusGrantToken since this will be
+ // the same token sent to WMS for the root window once setView is called.
+ if (mStateForWindow.isEmpty()) {
+ return mFocusGrantToken;
+ }
+ State state = mStateForWindow.get(window);
+ if (state != null) {
+ return state.mFocusGrantToken;
+ }
+ }
+
+ Log.w(TAG, "Failed to get focusGrantToken. Returning null token");
+ return null;
}
/**
diff --git a/core/java/android/view/translation/Translator.java b/core/java/android/view/translation/Translator.java
index 70db6e5..50249da 100644
--- a/core/java/android/view/translation/Translator.java
+++ b/core/java/android/view/translation/Translator.java
@@ -18,7 +18,6 @@
import static android.view.translation.TranslationManager.STATUS_SYNC_CALL_FAIL;
import static android.view.translation.TranslationManager.SYNC_CALLS_TIMEOUT_MS;
-import static android.view.translation.UiTranslationController.DEBUG;
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
@@ -402,7 +401,7 @@
@Override
public void onTranslationResponse(TranslationResponse response) throws RemoteException {
- if (DEBUG) {
+ if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
Log.i(TAG, "onTranslationResponse called.");
}
final Runnable runnable =
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 514df59..140e3f1 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -122,8 +122,9 @@
Log.i(TAG, "Cannot update " + stateToString(state) + " for destroyed " + mActivity);
return;
}
+ boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
Log.i(TAG, "updateUiTranslationState state: " + stateToString(state)
- + (DEBUG ? (", views: " + views + ", spec: " + uiTranslationSpec) : ""));
+ + (isLoggable ? (", views: " + views + ", spec: " + uiTranslationSpec) : ""));
synchronized (mLock) {
mCurrentState = state;
if (views != null) {
@@ -237,7 +238,7 @@
}
pw.print(outerPrefix); pw.print("padded views: "); pw.println(mViewsToPadContent);
}
- if (DEBUG) {
+ if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
dumpViewByTraversal(outerPrefix, pw);
}
}
@@ -345,6 +346,7 @@
*/
private void onVirtualViewTranslationCompleted(
SparseArray<LongSparseArray<ViewTranslationResponse>> translatedResult) {
+ boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
if (mActivity.isDestroyed()) {
Log.v(TAG, "onTranslationCompleted:" + mActivity + "is destroyed.");
return;
@@ -369,7 +371,7 @@
}
final LongSparseArray<ViewTranslationResponse> virtualChildResponse =
translatedResult.valueAt(i);
- if (DEBUG) {
+ if (isLoggable) {
Log.v(TAG, "onVirtualViewTranslationCompleted: received response for "
+ "AutofillId " + autofillId);
}
@@ -379,7 +381,7 @@
}
mActivity.runOnUiThread(() -> {
if (view.getViewTranslationCallback() == null) {
- if (DEBUG) {
+ if (isLoggable) {
Log.d(TAG, view + " doesn't support showing translation because of "
+ "null ViewTranslationCallback.");
}
@@ -397,12 +399,13 @@
* The method is used to handle the translation result for non-vertual views.
*/
private void onTranslationCompleted(SparseArray<ViewTranslationResponse> translatedResult) {
+ boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
if (mActivity.isDestroyed()) {
Log.v(TAG, "onTranslationCompleted:" + mActivity + "is destroyed.");
return;
}
final int resultCount = translatedResult.size();
- if (DEBUG) {
+ if (isLoggable) {
Log.v(TAG, "onTranslationCompleted: receive " + resultCount + " responses.");
}
synchronized (mLock) {
@@ -413,7 +416,7 @@
}
for (int i = 0; i < resultCount; i++) {
final ViewTranslationResponse response = translatedResult.valueAt(i);
- if (DEBUG) {
+ if (isLoggable) {
Log.v(TAG, "onTranslationCompleted: "
+ sanitizedViewTranslationResponse(response));
}
@@ -443,7 +446,7 @@
(TextViewTranslationCallback) callback;
if (textViewCallback.isShowingTranslation()
|| textViewCallback.isAnimationRunning()) {
- if (DEBUG) {
+ if (isLoggable) {
Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId
+ ". Ignoring.");
}
@@ -458,7 +461,7 @@
callback = new TextViewTranslationCallback();
view.setViewTranslationCallback(callback);
} else {
- if (DEBUG) {
+ if (isLoggable) {
Log.d(TAG, view + " doesn't support showing translation because of "
+ "null ViewTranslationCallback.");
}
@@ -506,7 +509,7 @@
final TranslationRequest request = new TranslationRequest.Builder()
.setViewTranslationRequests(requests)
.build();
- if (DEBUG) {
+ if (Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG)) {
StringBuilder msg = new StringBuilder("sendTranslationRequest:{requests=[");
for (ViewTranslationRequest viewRequest: requests) {
msg.append("{request=")
@@ -636,6 +639,7 @@
private void runForEachView(BiConsumer<View, ViewTranslationCallback> action) {
synchronized (mLock) {
+ boolean isLoggable = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
final ArrayMap<AutofillId, WeakReference<View>> views = new ArrayMap<>(mViews);
if (views.size() == 0) {
Log.w(TAG, "No views can be excuted for runForEachView.");
@@ -644,12 +648,12 @@
final int viewCounts = views.size();
for (int i = 0; i < viewCounts; i++) {
final View view = views.valueAt(i).get();
- if (DEBUG) {
+ if (isLoggable) {
Log.d(TAG, "runForEachView for autofillId = " + (view != null
? view.getAutofillId() : " null"));
}
if (view == null || view.getViewTranslationCallback() == null) {
- if (DEBUG) {
+ if (isLoggable) {
Log.d(TAG, "View was gone or ViewTranslationCallback for autofillId "
+ "= " + views.keyAt(i));
}
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/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index e59b259..7068453 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -1665,47 +1665,47 @@
<item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
<item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
<item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorOnBackground">@color/system_on_background_light</item>
<item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorOnError">@color/system_on_error_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorTertiary">@color/system_tertiary_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOutline">@color/system_outline_light</item>
+ <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
</style>
<!-- DeviceDefault style for input methods, which is used by the
@@ -1758,47 +1758,47 @@
<item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
<item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
<item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorOnBackground">@color/system_on_background_light</item>
<item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorOnError">@color/system_on_error_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorTertiary">@color/system_tertiary_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOutline">@color/system_outline_light</item>
+ <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
</style>
<style name="Theme.DeviceDefault.Dialog.Alert" parent="Theme.Material.Dialog.Alert">
@@ -4043,16 +4043,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4123,16 +4123,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4195,16 +4195,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4366,16 +4366,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
<item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
@@ -4475,47 +4475,47 @@
<item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
<item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
<item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorOnBackground">@color/system_on_background_light</item>
<item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorOnError">@color/system_on_error_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorTertiary">@color/system_tertiary_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOutline">@color/system_outline_light</item>
+ <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.Alert" parent="Theme.Material.Settings.Dialog.Alert">
@@ -4570,47 +4570,47 @@
<item name="materialColorOnSecondaryFixedVariant">@color/system_on_secondary_fixed_variant</item>
<item name="materialColorOnTertiaryFixedVariant">@color/system_on_tertiary_fixed_variant</item>
- <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_dark</item>
+ <item name="materialColorSurfaceContainerLowest">@color/system_surface_container_lowest_light</item>
<item name="materialColorOnPrimaryFixedVariant">@color/system_on_primary_fixed_variant</item>
- <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_dark</item>
- <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_dark</item>
- <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_dark</item>
- <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_dark</item>
+ <item name="materialColorOnSecondaryContainer">@color/system_on_secondary_container_light</item>
+ <item name="materialColorOnTertiaryContainer">@color/system_on_tertiary_container_light</item>
+ <item name="materialColorSurfaceContainerLow">@color/system_surface_container_low_light</item>
+ <item name="materialColorOnPrimaryContainer">@color/system_on_primary_container_light</item>
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
- <item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
+ <item name="materialColorOnErrorContainer">@color/system_on_error_container_light</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
- <item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
- <item name="materialColorErrorContainer">@color/system_error_container_dark</item>
+ <item name="materialColorSecondaryContainer">@color/system_secondary_container_light</item>
+ <item name="materialColorErrorContainer">@color/system_error_container_light</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
- <item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
- <item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceVariant">@color/system_surface_variant_light</item>
+ <item name="materialColorTertiaryContainer">@color/system_tertiary_container_light</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
- <item name="materialColorPrimaryContainer">@color/system_primary_container_dark</item>
- <item name="materialColorOnBackground">@color/system_on_background_dark</item>
+ <item name="materialColorPrimaryContainer">@color/system_primary_container_light</item>
+ <item name="materialColorOnBackground">@color/system_on_background_light</item>
<item name="materialColorPrimaryFixed">@color/system_primary_fixed</item>
- <item name="materialColorOnSecondary">@color/system_on_secondary_dark</item>
- <item name="materialColorOnTertiary">@color/system_on_tertiary_dark</item>
- <item name="materialColorSurfaceDim">@color/system_surface_dim_dark</item>
- <item name="materialColorSurfaceBright">@color/system_surface_bright_dark</item>
- <item name="materialColorSecondary">@color/system_secondary_dark</item>
- <item name="materialColorOnError">@color/system_on_error_dark</item>
- <item name="materialColorSurface">@color/system_surface_dark</item>
- <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_dark</item>
- <item name="materialColorTertiary">@color/system_tertiary_dark</item>
- <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_dark</item>
- <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_dark</item>
- <item name="materialColorOutline">@color/system_outline_dark</item>
- <item name="materialColorOutlineVariant">@color/system_outline_variant_dark</item>
- <item name="materialColorOnPrimary">@color/system_on_primary_dark</item>
- <item name="materialColorOnSurface">@color/system_on_surface_dark</item>
- <item name="materialColorSurfaceContainer">@color/system_surface_container_dark</item>
+ <item name="materialColorOnSecondary">@color/system_on_secondary_light</item>
+ <item name="materialColorOnTertiary">@color/system_on_tertiary_light</item>
+ <item name="materialColorSurfaceDim">@color/system_surface_dim_light</item>
+ <item name="materialColorSurfaceBright">@color/system_surface_bright_light</item>
+ <item name="materialColorSecondary">@color/system_secondary_light</item>
+ <item name="materialColorOnError">@color/system_on_error_light</item>
+ <item name="materialColorSurface">@color/system_surface_light</item>
+ <item name="materialColorSurfaceContainerHigh">@color/system_surface_container_high_light</item>
+ <item name="materialColorTertiary">@color/system_tertiary_light</item>
+ <item name="materialColorSurfaceContainerHighest">@color/system_surface_container_highest_light</item>
+ <item name="materialColorOnSurfaceVariant">@color/system_on_surface_variant_light</item>
+ <item name="materialColorOutline">@color/system_outline_light</item>
+ <item name="materialColorOutlineVariant">@color/system_outline_variant_light</item>
+ <item name="materialColorOnPrimary">@color/system_on_primary_light</item>
+ <item name="materialColorOnSurface">@color/system_on_surface_light</item>
+ <item name="materialColorSurfaceContainer">@color/system_surface_container_light</item>
</style>
<style name="Theme.DeviceDefault.Settings.Dialog.NoActionBar" parent="Theme.DeviceDefault.Light.Dialog.NoActionBar" />
@@ -4700,16 +4700,16 @@
<item name="materialColorSecondaryFixedDim">@color/system_secondary_fixed_dim</item>
<item name="materialColorOnErrorContainer">@color/system_on_error_container_dark</item>
<item name="materialColorOnSecondaryFixed">@color/system_on_secondary_fixed</item>
- <item name="materialColorOnSurfaceInverse">@color/system_on_surface_dark</item>
+ <item name="materialColorOnSurfaceInverse">@color/system_on_surface_light</item>
<item name="materialColorTertiaryFixedDim">@color/system_tertiary_fixed_dim</item>
<item name="materialColorOnTertiaryFixed">@color/system_on_tertiary_fixed</item>
<item name="materialColorPrimaryFixedDim">@color/system_primary_fixed_dim</item>
<item name="materialColorSecondaryContainer">@color/system_secondary_container_dark</item>
<item name="materialColorErrorContainer">@color/system_error_container_dark</item>
<item name="materialColorOnPrimaryFixed">@color/system_on_primary_fixed</item>
- <item name="materialColorPrimaryInverse">@color/system_primary_dark</item>
+ <item name="materialColorPrimaryInverse">@color/system_primary_light</item>
<item name="materialColorSecondaryFixed">@color/system_secondary_fixed</item>
- <item name="materialColorSurfaceInverse">@color/system_surface_dark</item>
+ <item name="materialColorSurfaceInverse">@color/system_surface_light</item>
<item name="materialColorSurfaceVariant">@color/system_surface_variant_dark</item>
<item name="materialColorTertiaryContainer">@color/system_tertiary_container_dark</item>
<item name="materialColorTertiaryFixed">@color/system_tertiary_fixed</item>
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/core/tests/coretests/src/android/app/OWNERS b/core/tests/coretests/src/android/app/OWNERS
index b3f3993..8d9461d 100644
--- a/core/tests/coretests/src/android/app/OWNERS
+++ b/core/tests/coretests/src/android/app/OWNERS
@@ -4,3 +4,6 @@
per-file *Notification* = file:/packages/SystemUI/OWNERS
per-file *Zen* = file:/packages/SystemUI/OWNERS
per-file *StatusBar* = file:/packages/SystemUI/OWNERS
+
+# A11Y and related
+per-file *UiAutomation* = file:/services/accessibility/OWNERS
diff --git a/data/etc/preinstalled-packages-platform-overlays.xml b/data/etc/preinstalled-packages-platform-overlays.xml
index 2fd65dc..cea535e 100644
--- a/data/etc/preinstalled-packages-platform-overlays.xml
+++ b/data/etc/preinstalled-packages-platform-overlays.xml
@@ -58,6 +58,7 @@
</install-in-user-type>
<install-in-user-type package="com.android.role.notes.enabled">
<install-in user-type="FULL" />
+ <install-in user-type="PROFILE" />
</install-in-user-type>
<install-in-user-type package="com.android.theme.color.amethyst">
<install-in user-type="FULL" />
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 0eb4caa..7434cb0 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -175,12 +175,6 @@
"group": "WM_ERROR",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1941440781": {
- "message": "Creating Pending Move-to-back: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/Task.java"
- },
"-1939861963": {
"message": "Create root task displayId=%d winMode=%d",
"level": "VERBOSE",
@@ -631,12 +625,6 @@
"group": "WM_DEBUG_CONFIGURATION",
"at": "com\/android\/server\/wm\/ActivityStarter.java"
},
- "-1484988952": {
- "message": "Creating Pending Multiwindow Fullscreen Request: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityClientController.java"
- },
"-1483435730": {
"message": "InsetsSource setWin %s for type %s",
"level": "DEBUG",
@@ -2605,12 +2593,6 @@
"group": "WM_DEBUG_ANIM",
"at": "com\/android\/server\/wm\/WindowState.java"
},
- "286170861": {
- "message": "Creating Pending Transition for TaskFragment: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
- },
"288485303": {
"message": "Attempted to set remove mode to a display that does not exist: %d",
"level": "WARN",
@@ -3193,12 +3175,6 @@
"group": "WM_DEBUG_APP_TRANSITIONS",
"at": "com\/android\/server\/wm\/AppTransitionController.java"
},
- "800698875": {
- "message": "SyncGroup %d: Started when there is other active SyncGroup",
- "level": "WARN",
- "group": "WM_DEBUG_SYNC_ENGINE",
- "at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
- },
"801521566": {
"message": "Content Recording: Attempting to mirror %d from %d but no DisplayContent associated. Changing to mirror default display.",
"level": "WARN",
@@ -3259,12 +3235,6 @@
"group": "WM_DEBUG_STATES",
"at": "com\/android\/server\/wm\/RootWindowContainer.java"
},
- "898260097": {
- "message": "Creating Pending Pip-Enter: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/ActivityTaskManagerService.java"
- },
"898863925": {
"message": "Attempted to add QS dialog window with unknown token %s. Aborting.",
"level": "WARN",
@@ -4009,12 +3979,6 @@
"group": "WM_DEBUG_CONTENT_RECORDING",
"at": "com\/android\/server\/wm\/ContentRecorder.java"
},
- "1667162379": {
- "message": "Creating Pending Transition: %s",
- "level": "VERBOSE",
- "group": "WM_DEBUG_WINDOW_TRANSITIONS",
- "at": "com\/android\/server\/wm\/WindowOrganizerController.java"
- },
"1670933628": {
"message": " Setting allReady override",
"level": "VERBOSE",
@@ -4075,6 +4039,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "1735199721": {
+ "message": "Queueing transition: %s",
+ "level": "VERBOSE",
+ "group": "WM_DEBUG_WINDOW_TRANSITIONS_MIN",
+ "at": "com\/android\/server\/wm\/TransitionController.java"
+ },
"1739298851": {
"message": "removeWindowToken: Attempted to remove token: %s for non-exiting displayId=%d",
"level": "WARN",
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/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..23706a5 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();
@@ -1385,7 +1389,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 +1555,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..c5e9229 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
@@ -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/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/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 2cd16be..498f95c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -541,6 +541,34 @@
instanceId);
}
+ void startShortcutAndTask(ShortcutInfo shortcutInfo, @Nullable Bundle options1,
+ int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
+ float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
+ if (options1 == null) options1 = new Bundle();
+ final ActivityOptions activityOptions = ActivityOptions.fromBundle(options1);
+ final String packageName1 = shortcutInfo.getPackage();
+ // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
+ // recents that hasn't launched and is not being organized
+ final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
+ if (samePackage(packageName1, packageName2)) {
+ if (supportMultiInstancesSplit(packageName1)) {
+ activityOptions.setApplyMultipleTaskFlagForShortcut(true);
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
+ } else {
+ if (mRecentTasksOptional.isPresent()) {
+ mRecentTasksOptional.get().removeSplitPair(taskId);
+ }
+ taskId = INVALID_TASK_ID;
+ ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
+ "Cancel entering split as not supporting multi-instances");
+ Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
+ Toast.LENGTH_SHORT).show();
+ }
+ }
+ mStageCoordinator.startShortcutAndTask(shortcutInfo, options1, taskId, options2,
+ splitPosition, splitRatio, remoteTransition, instanceId);
+ }
+
/**
* See {@link #startIntent(PendingIntent, Intent, int, Bundle)}
* @param instanceId to be used by {@link SplitscreenEventLogger}
@@ -580,6 +608,8 @@
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
Intent fillInIntent = null;
final String packageName1 = SplitScreenUtils.getPackageName(pendingIntent);
+ // NOTE: This doesn't correctly pull out packageName2 if taskId is referring to a task in
+ // recents that hasn't launched and is not being organized
final String packageName2 = SplitScreenUtils.getPackageName(taskId, mTaskOrganizer);
if (samePackage(packageName1, packageName2)) {
if (supportMultiInstancesSplit(packageName1)) {
@@ -587,6 +617,10 @@
fillInIntent.addFlags(FLAG_ACTIVITY_MULTIPLE_TASK);
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN, "Adding MULTIPLE_TASK");
} else {
+ if (mRecentTasksOptional.isPresent()) {
+ mRecentTasksOptional.get().removeSplitPair(taskId);
+ }
+ taskId = INVALID_TASK_ID;
ProtoLog.v(ShellProtoLogGroup.WM_SHELL_SPLIT_SCREEN,
"Cancel entering split as not supporting multi-instances");
Toast.makeText(mContext, R.string.dock_multi_instances_not_supported_text,
@@ -1075,9 +1109,8 @@
float splitRatio, @Nullable RemoteTransition remoteTransition,
InstanceId instanceId) {
executeRemoteCallWithTaskPermission(mController, "startShortcutAndTask",
- (controller) -> controller.mStageCoordinator.startShortcutAndTask(shortcutInfo,
- options1, taskId, options2, splitPosition, splitRatio, remoteTransition,
- instanceId));
+ (controller) -> controller.startShortcutAndTask(shortcutInfo, options1, taskId,
+ options2, splitPosition, splitRatio, remoteTransition, instanceId));
}
@Override
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 8b890bb..2c08cd4 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
@@ -289,6 +289,12 @@
return null;
}
+ void startFullscreenTransition(WindowContainerTransaction wct,
+ @Nullable RemoteTransition handler) {
+ mTransitions.startTransition(TRANSIT_OPEN, wct,
+ new OneShotRemoteHandler(mTransitions.getMainExecutor(), handler));
+ }
+
/** Starts a transition to enter split with a remote transition animator. */
IBinder startEnterTransition(
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 ce5a2af6..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
@@ -612,6 +612,19 @@
@Nullable Bundle options2, @SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (taskId2 == INVALID_TASK_ID) {
+ if (mMainStage.containsTask(taskId1) || mSideStage.containsTask(taskId1)) {
+ prepareExitSplitScreen(STAGE_TYPE_UNDEFINED, wct);
+ }
+ if (mRecentTasks.isPresent()) {
+ mRecentTasks.get().removeSplitPair(taskId1);
+ }
+ options1 = options1 != null ? options1 : new Bundle();
+ wct.startTask(taskId1, options1);
+ mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+ return;
+ }
+
prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
@@ -627,6 +640,13 @@
@SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (taskId == INVALID_TASK_ID) {
+ options1 = options1 != null ? options1 : new Bundle();
+ wct.sendPendingIntent(pendingIntent, fillInIntent, options1);
+ mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+ return;
+ }
+
prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
@@ -641,6 +661,13 @@
int taskId, @Nullable Bundle options2, @SplitPosition int splitPosition,
float splitRatio, @Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (taskId == INVALID_TASK_ID) {
+ options1 = options1 != null ? options1 : new Bundle();
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo, options1);
+ mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+ return;
+ }
+
prepareEvictChildTasksIfSplitActive(wct);
setSideStagePosition(splitPosition, wct);
options1 = options1 != null ? options1 : new Bundle();
@@ -689,6 +716,17 @@
@SplitPosition int splitPosition, float splitRatio,
@Nullable RemoteTransition remoteTransition, InstanceId instanceId) {
final WindowContainerTransaction wct = new WindowContainerTransaction();
+ if (pendingIntent2 == null) {
+ options1 = options1 != null ? options1 : new Bundle();
+ if (shortcutInfo1 != null) {
+ wct.startShortcut(mContext.getPackageName(), shortcutInfo1, options1);
+ } else {
+ wct.sendPendingIntent(pendingIntent1, fillInIntent1, options1);
+ }
+ mSplitTransitions.startFullscreenTransition(wct, remoteTransition);
+ return;
+ }
+
if (!mMainStage.isActive()) {
// Build a request WCT that will launch both apps such that task 0 is on the main stage
// while task 1 is on the side stage.
@@ -2376,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/libs/hwui/renderthread/VulkanManager.cpp b/libs/hwui/renderthread/VulkanManager.cpp
index 718d4a1..96bfc10 100644
--- a/libs/hwui/renderthread/VulkanManager.cpp
+++ b/libs/hwui/renderthread/VulkanManager.cpp
@@ -42,6 +42,36 @@
namespace uirenderer {
namespace renderthread {
+static std::array<std::string_view, 18> sEnableExtensions{
+ VK_KHR_BIND_MEMORY_2_EXTENSION_NAME,
+ VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME,
+ VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME,
+ VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME,
+ VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME,
+ VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME,
+ VK_KHR_MAINTENANCE1_EXTENSION_NAME,
+ VK_KHR_MAINTENANCE2_EXTENSION_NAME,
+ VK_KHR_MAINTENANCE3_EXTENSION_NAME,
+ VK_KHR_SAMPLER_YCBCR_CONVERSION_EXTENSION_NAME,
+ VK_KHR_SURFACE_EXTENSION_NAME,
+ VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ VK_EXT_BLEND_OPERATION_ADVANCED_EXTENSION_NAME,
+ VK_EXT_IMAGE_DRM_FORMAT_MODIFIER_EXTENSION_NAME,
+ VK_ANDROID_EXTERNAL_MEMORY_ANDROID_HARDWARE_BUFFER_EXTENSION_NAME,
+ VK_EXT_QUEUE_FAMILY_FOREIGN_EXTENSION_NAME,
+ VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
+ VK_KHR_ANDROID_SURFACE_EXTENSION_NAME,
+};
+
+static bool shouldEnableExtension(const std::string_view& extension) {
+ for (const auto& it : sEnableExtensions) {
+ if (it == extension) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void free_features_extensions_structs(const VkPhysicalDeviceFeatures2& features) {
// All Vulkan structs that could be part of the features chain will start with the
// structure type followed by the pNext pointer. We cast to the CommonVulkanHeader
@@ -139,6 +169,11 @@
bool hasKHRSurfaceExtension = false;
bool hasKHRAndroidSurfaceExtension = false;
for (const VkExtensionProperties& extension : mInstanceExtensionsOwner) {
+ if (!shouldEnableExtension(extension.extensionName)) {
+ ALOGV("Not enabling instance extension %s", extension.extensionName);
+ continue;
+ }
+ ALOGV("Enabling instance extension %s", extension.extensionName);
mInstanceExtensions.push_back(extension.extensionName);
if (!strcmp(extension.extensionName, VK_KHR_SURFACE_EXTENSION_NAME)) {
hasKHRSurfaceExtension = true;
@@ -219,6 +254,11 @@
LOG_ALWAYS_FATAL_IF(VK_SUCCESS != err);
bool hasKHRSwapchainExtension = false;
for (const VkExtensionProperties& extension : mDeviceExtensionsOwner) {
+ if (!shouldEnableExtension(extension.extensionName)) {
+ ALOGV("Not enabling device extension %s", extension.extensionName);
+ continue;
+ }
+ ALOGV("Enabling device extension %s", extension.extensionName);
mDeviceExtensions.push_back(extension.extensionName);
if (!strcmp(extension.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
hasKHRSwapchainExtension = true;
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 116237f..609c7a4 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -196,6 +196,15 @@
dstSize = (size_t) env->GetDirectBufferCapacity(byteBuf);
}
+ // unlikely, but GetByteArrayElements() can fail
+ if (dst == nullptr) {
+ ALOGE("no buffer into which to read the data");
+ if (byteArray != NULL) {
+ env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
+ }
+ return -ENOMEM;
+ }
+
if (dstSize < offset) {
if (byteArray != NULL) {
env->ReleaseByteArrayElements(byteArray, (jbyte *)dst, 0);
@@ -204,8 +213,10 @@
return -ERANGE;
}
+ // passes in the backing memory to use, so it doesn't fail
sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
+ buffer->setRange(0, 0); // mark it empty
status_t err = mImpl->readSampleData(buffer);
if (byteArray != NULL) {
diff --git a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
index 24f92c0..7581b5c 100644
--- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
+++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt
@@ -19,9 +19,9 @@
import android.content.Intent
import android.credentials.ui.BaseDialogResult
import android.credentials.ui.RequestInfo
+import android.net.Uri
import android.os.Bundle
import android.os.ResultReceiver
-import android.provider.Settings
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
@@ -192,7 +192,9 @@
this@CredentialSelectorActivity.finish()
} else if (dialogState == DialogState.CANCELED_FOR_SETTINGS) {
Log.d(Constants.LOG_TAG, "Received signal to finish the activity and launch settings.")
- this@CredentialSelectorActivity.startActivity(Intent(Settings.ACTION_SYNC_SETTINGS))
+ val settingsIntent = Intent(ACTION_CREDENTIAL_PROVIDER)
+ settingsIntent.data = Uri.parse("package:" + this.getPackageName())
+ this@CredentialSelectorActivity.startActivity(settingsIntent)
this@CredentialSelectorActivity.finish()
}
}
@@ -222,4 +224,8 @@
dismissOnTimeout = true,
)
}
+
+ companion object {
+ const val ACTION_CREDENTIAL_PROVIDER = "android.settings.CREDENTIAL_PROVIDER"
+ }
}
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/AndroidManifest.xml b/packages/DynamicSystemInstallationService/AndroidManifest.xml
index c2aaeac..776bf2b 100644
--- a/packages/DynamicSystemInstallationService/AndroidManifest.xml
+++ b/packages/DynamicSystemInstallationService/AndroidManifest.xml
@@ -36,6 +36,10 @@
<data android:scheme="http" />
<data android:scheme="https" />
</intent-filter>
+ <intent-filter>
+ <action android:name="android.os.image.action.START_INSTALL" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
</activity>
<receiver
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
index 2c4b478..b265a42 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java
@@ -19,6 +19,7 @@
import static android.os.AsyncTask.Status.FINISHED;
import static android.os.AsyncTask.Status.PENDING;
import static android.os.AsyncTask.Status.RUNNING;
+import static android.os.image.DynamicSystemClient.ACTION_HIDE_NOTIFICATION;
import static android.os.image.DynamicSystemClient.ACTION_NOTIFY_IF_IN_USE;
import static android.os.image.DynamicSystemClient.ACTION_START_INSTALL;
import static android.os.image.DynamicSystemClient.CAUSE_ERROR_EXCEPTION;
@@ -27,6 +28,8 @@
import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_CANCELLED;
import static android.os.image.DynamicSystemClient.CAUSE_INSTALL_COMPLETED;
import static android.os.image.DynamicSystemClient.CAUSE_NOT_SPECIFIED;
+import static android.os.image.DynamicSystemClient.KEY_ENABLE_WHEN_COMPLETED;
+import static android.os.image.DynamicSystemClient.KEY_ONE_SHOT;
import static android.os.image.DynamicSystemClient.STATUS_IN_PROGRESS;
import static android.os.image.DynamicSystemClient.STATUS_IN_USE;
import static android.os.image.DynamicSystemClient.STATUS_NOT_STARTED;
@@ -77,8 +80,6 @@
private static final String TAG = "DynamicSystemInstallationService";
- // TODO (b/131866826): This is currently for test only. Will move this to System API.
- static final String KEY_ENABLE_WHEN_COMPLETED = "KEY_ENABLE_WHEN_COMPLETED";
static final String KEY_DSU_SLOT = "KEY_DSU_SLOT";
static final String DEFAULT_DSU_SLOT = "dsu";
static final String KEY_PUBKEY = "KEY_PUBKEY";
@@ -172,6 +173,8 @@
// This is for testing only now
private boolean mEnableWhenCompleted;
+ private boolean mOneShot;
+ private boolean mHideNotification;
private InstallationAsyncTask.Progress mInstallTaskProgress;
private InstallationAsyncTask mInstallTask;
@@ -229,6 +232,8 @@
executeRebootToNormalCommand();
} else if (ACTION_NOTIFY_IF_IN_USE.equals(action)) {
executeNotifyIfInUseCommand();
+ } else if (ACTION_HIDE_NOTIFICATION.equals(action)) {
+ executeHideNotificationCommand();
}
return Service.START_NOT_STICKY;
@@ -318,6 +323,7 @@
long systemSize = intent.getLongExtra(DynamicSystemClient.KEY_SYSTEM_SIZE, 0);
long userdataSize = intent.getLongExtra(DynamicSystemClient.KEY_USERDATA_SIZE, 0);
mEnableWhenCompleted = intent.getBooleanExtra(KEY_ENABLE_WHEN_COMPLETED, false);
+ mOneShot = intent.getBooleanExtra(KEY_ONE_SHOT, true);
String dsuSlot = intent.getStringExtra(KEY_DSU_SLOT);
String publicKey = intent.getStringExtra(KEY_PUBKEY);
@@ -384,9 +390,9 @@
boolean enabled = false;
if (mInstallTask != null && mInstallTask.isCompleted()) {
- enabled = mInstallTask.commit();
+ enabled = mInstallTask.commit(mOneShot);
} else if (isDynamicSystemInstalled()) {
- enabled = mDynSystem.setEnable(true, true);
+ enabled = mDynSystem.setEnable(true, mOneShot);
} else {
Log.e(TAG, "Trying to reboot to AOT while there is no complete installation");
return;
@@ -439,12 +445,16 @@
private void executeNotifyIfInUseCommand() {
switch (getStatus()) {
case STATUS_IN_USE:
- startForeground(NOTIFICATION_ID,
- buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+ if (!mHideNotification) {
+ startForeground(NOTIFICATION_ID,
+ buildNotification(STATUS_IN_USE, CAUSE_NOT_SPECIFIED));
+ }
break;
case STATUS_READY:
- startForeground(NOTIFICATION_ID,
- buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+ if (!mHideNotification) {
+ startForeground(NOTIFICATION_ID,
+ buildNotification(STATUS_READY, CAUSE_NOT_SPECIFIED));
+ }
break;
case STATUS_IN_PROGRESS:
break;
@@ -454,6 +464,16 @@
}
}
+ private void executeHideNotificationCommand() {
+ mHideNotification = true;
+ switch (getStatus()) {
+ case STATUS_IN_USE:
+ case STATUS_READY:
+ stopForeground(STOP_FOREGROUND_REMOVE);
+ break;
+ }
+ }
+
private void resetTaskAndStop() {
resetTaskAndStop(/* removeNotification= */ false);
}
diff --git a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
index a41399f..42b620a 100644
--- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
+++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java
@@ -803,7 +803,7 @@
return mIsCompleted;
}
- boolean commit() {
- return mDynSystem.setEnable(true, true);
+ boolean commit(boolean oneShot) {
+ return mDynSystem.setEnable(true, oneShot);
}
}
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/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp
index 69a1d7f..ef0d122 100644
--- a/packages/PrintSpooler/tests/outofprocess/Android.bp
+++ b/packages/PrintSpooler/tests/outofprocess/Android.bp
@@ -31,7 +31,7 @@
libs: ["android.test.runner.stubs"],
static_libs: [
"androidx.test.rules",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
"mockito-target-minus-junit4",
"print-test-util-lib",
],
diff --git a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
index 132545b..1509b70 100644
--- a/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
+++ b/packages/PrintSpooler/tests/outofprocess/src/com/android/printspooler/outofprocess/tests/WorkflowTest.java
@@ -35,15 +35,15 @@
import android.print.test.services.FirstPrintService;
import android.print.test.services.PrinterDiscoverySessionCallbacks;
import android.print.test.services.StubbablePrinterDiscoverySession;
-import android.support.test.uiautomator.By;
-import android.support.test.uiautomator.UiDevice;
-import android.support.test.uiautomator.UiObject;
-import android.support.test.uiautomator.UiObjectNotFoundException;
-import android.support.test.uiautomator.UiSelector;
-import android.support.test.uiautomator.Until;
import android.util.Log;
import androidx.test.filters.LargeTest;
+import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.UiDevice;
+import androidx.test.uiautomator.UiObject;
+import androidx.test.uiautomator.UiObjectNotFoundException;
+import androidx.test.uiautomator.UiSelector;
+import androidx.test.uiautomator.Until;
import org.junit.Test;
import org.junit.runner.RunWith;
diff --git a/packages/SettingsLib/OWNERS b/packages/SettingsLib/OWNERS
index 1b3020a..81340f5 100644
--- a/packages/SettingsLib/OWNERS
+++ b/packages/SettingsLib/OWNERS
@@ -3,9 +3,8 @@
edgarwang@google.com
evanlaird@google.com
juliacr@google.com
-lijun@google.com
-songchenxi@google.com
yantingyang@google.com
+ykhung@google.com
# Exempt resource files (because they are in a flat directory and too hard to manage via OWNERS)
per-file *.xml=*
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
index a7e95b5..eaf3229 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/ClockRegistry.kt
@@ -33,6 +33,7 @@
import com.android.systemui.plugins.PluginListener
import com.android.systemui.plugins.PluginManager
import com.android.systemui.util.Assert
+import java.io.PrintWriter
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.atomic.AtomicBoolean
import kotlinx.coroutines.CoroutineDispatcher
@@ -485,6 +486,14 @@
return availableClocks[targetClockId]?.provider?.createClock(settings)
}
+ fun dump(pw: PrintWriter, args: Array<out String>) {
+ pw.println("ClockRegistry:")
+ pw.println(" settings = $settings")
+ for ((id, info) in availableClocks) {
+ pw.println(" availableClocks[$id] = $info")
+ }
+ }
+
private data class ClockInfo(
val metadata: ClockMetadata,
var provider: ClockProvider?,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
index 0326b6d..5ba0ad6 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java
@@ -294,11 +294,11 @@
public void dump(PrintWriter pw, String[] args) {
pw.println("KeyguardClockSwitch:");
- pw.println(" mSmallClockFrame: " + mSmallClockFrame);
- pw.println(" mSmallClockFrame.alpha: " + mSmallClockFrame.getAlpha());
- pw.println(" mLargeClockFrame: " + mLargeClockFrame);
- pw.println(" mLargeClockFrame.alpha: " + mLargeClockFrame.getAlpha());
- pw.println(" mStatusArea: " + mStatusArea);
- pw.println(" mDisplayedClockSize: " + mDisplayedClockSize);
+ pw.println(" mSmallClockFrame = " + mSmallClockFrame);
+ pw.println(" mSmallClockFrame.alpha = " + mSmallClockFrame.getAlpha());
+ pw.println(" mLargeClockFrame = " + mLargeClockFrame);
+ pw.println(" mLargeClockFrame.alpha = " + mLargeClockFrame.getAlpha());
+ pw.println(" mStatusArea = " + mStatusArea);
+ pw.println(" mDisplayedClockSize = " + mDisplayedClockSize);
}
}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
index 25d1792..ad333b7 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java
@@ -493,7 +493,7 @@
if (mDateWeatherView != null) {
mUiExecutor.execute(() -> {
mDateWeatherView.setVisibility(
- clockHasCustomWeatherDataDisplay() ? View.GONE : View.VISIBLE);
+ clockHasCustomWeatherDataDisplay() ? View.INVISIBLE : View.VISIBLE);
});
}
}
@@ -519,9 +519,10 @@
@Override
public void dump(@NonNull PrintWriter pw, @NonNull String[] args) {
- pw.println("currentClockSizeLarge=" + (mCurrentClockSize == LARGE));
- pw.println("mCanShowDoubleLineClock=" + mCanShowDoubleLineClock);
+ pw.println("currentClockSizeLarge: " + (mCurrentClockSize == LARGE));
+ pw.println("mCanShowDoubleLineClock: " + mCanShowDoubleLineClock);
mView.dump(pw, args);
+ mClockRegistry.dump(pw, args);
ClockController clock = getClock();
if (clock != null) {
clock.dump(pw);
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 33a8224..cc64389 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1259,6 +1259,9 @@
* for bridging the gap while the migration is active.
*/
private void handleFaceHelp(int msgId, String helpString) {
+ if (mFaceAcquiredInfoIgnoreList.contains(msgId)) {
+ return;
+ }
Assert.isMainThread();
mLogger.logFaceAuthHelpMsg(msgId, helpString);
for (int i = 0; i < mCallbacks.size(); i++) {
@@ -4370,7 +4373,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/keyguard/dagger/ClockRegistryModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
index 6e98a18..cde8ff7 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/ClockRegistryModule.java
@@ -33,6 +33,7 @@
import dagger.Module;
import dagger.Provides;
+
import kotlinx.coroutines.CoroutineDispatcher;
import kotlinx.coroutines.CoroutineScope;
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index ffacf01..f01749a 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -229,7 +229,7 @@
/** Whether to inflate the bouncer view on a background thread. */
// TODO(b/273341787): Tracking Bug
@JvmField
- val PREVENT_BYPASS_KEYGUARD = unreleasedFlag(230, "prevent_bypass_keyguard", teamfood = true)
+ val PREVENT_BYPASS_KEYGUARD = releasedFlag(230, "prevent_bypass_keyguard")
/** Whether to use a new data source for intents to run on keyguard dismissal. */
@JvmField
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/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/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/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/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/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
index eaa1455..b3d2461 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt
@@ -35,6 +35,7 @@
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileUiAdapter
+import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl
import com.android.systemui.statusbar.pipeline.mobile.util.SubscriptionManagerProxy
@@ -53,6 +54,9 @@
import dagger.Provides
import dagger.multibindings.ClassKey
import dagger.multibindings.IntoMap
+import kotlinx.coroutines.flow.Flow
+import java.util.function.Supplier
+import javax.inject.Named
@Module
abstract class StatusBarPipelineModule {
@@ -115,6 +119,17 @@
@Provides
@SysUISingleton
+ @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
+ fun provideFirstMobileSubShowingNetworkTypeIconProvider(
+ mobileIconsViewModel: MobileIconsViewModel,
+ ): Supplier<Flow<Boolean>> {
+ return Supplier<Flow<Boolean>> {
+ mobileIconsViewModel.firstMobileSubShowingNetworkTypeIcon
+ }
+ }
+
+ @Provides
+ @SysUISingleton
@WifiInputLog
fun provideWifiInputLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("WifiInputLog", 50)
@@ -168,5 +183,8 @@
fun provideVerboseMobileViewLogBuffer(factory: LogBufferFactory): LogBuffer {
return factory.create("VerboseMobileViewLog", 100)
}
+
+ const val FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON =
+ "FirstMobileSubShowingNetworkTypeIcon"
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
index 9af5e83..40b8c90 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModel.kt
@@ -32,6 +32,9 @@
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
+import kotlinx.coroutines.flow.flatMapLatest
+import kotlinx.coroutines.flow.flowOf
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -63,22 +66,36 @@
}
.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
+ private val firstMobileSubViewModel: StateFlow<MobileIconViewModelCommon?> =
+ subscriptionIdsFlow
+ .map {
+ if (it.isEmpty()) {
+ null
+ } else {
+ // Mobile icons get reversed by [StatusBarIconController], so the last element
+ // in this list will show up visually first.
+ commonViewModelForSub(it.last())
+ }
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), null)
+
+ /**
+ * A flow that emits `true` if the mobile sub that's displayed first visually is showing its
+ * network type icon and `false` otherwise.
+ */
+ val firstMobileSubShowingNetworkTypeIcon: StateFlow<Boolean> =
+ firstMobileSubViewModel
+ .flatMapLatest { firstMobileSubViewModel ->
+ firstMobileSubViewModel?.networkTypeIcon?.map { it != null } ?: flowOf(false)
+ }
+ .stateIn(scope, SharingStarted.WhileSubscribed(), false)
+
init {
scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } }
}
fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
- val common =
- mobileIconSubIdCache[subId]
- ?: MobileIconViewModel(
- subId,
- interactor.createMobileConnectionInteractorForSubId(subId),
- airplaneModeInteractor,
- constants,
- scope,
- )
- .also { mobileIconSubIdCache[subId] = it }
-
+ val common = commonViewModelForSub(subId)
return LocationBasedMobileViewModel.viewModelForLocation(
common,
statusBarPipelineFlags,
@@ -87,6 +104,18 @@
)
}
+ private fun commonViewModelForSub(subId: Int): MobileIconViewModelCommon {
+ return mobileIconSubIdCache[subId]
+ ?: MobileIconViewModel(
+ subId,
+ interactor.createMobileConnectionInteractorForSubId(subId),
+ airplaneModeInteractor,
+ constants,
+ scope,
+ )
+ .also { mobileIconSubIdCache[subId] = it }
+ }
+
private fun removeInvalidModelsFromCache(subIds: List<Int>) {
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
index 9e8c814..e819c4f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt
@@ -36,7 +36,6 @@
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.MutableStateFlow
-import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
@@ -64,6 +63,7 @@
val activityOutView = view.requireViewById<ImageView>(R.id.wifi_out)
val activityContainerView = view.requireViewById<View>(R.id.inout_container)
val airplaneSpacer = view.requireViewById<View>(R.id.wifi_airplane_spacer)
+ val signalSpacer = view.requireViewById<View>(R.id.wifi_signal_spacer)
view.isVisible = true
iconView.isVisible = true
@@ -133,6 +133,12 @@
}
}
+ launch {
+ viewModel.isSignalSpacerVisible.distinctUntilChanged().collect { visible ->
+ signalSpacer.isVisible = visible
+ }
+ }
+
try {
awaitCancellation()
} finally {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
index c9a0786..d9c2144 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt
@@ -30,8 +30,8 @@
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
+import com.android.systemui.statusbar.pipeline.dagger.StatusBarPipelineModule.Companion.FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON
import com.android.systemui.statusbar.pipeline.dagger.WifiTableLog
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
@@ -39,7 +39,9 @@
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import com.android.systemui.statusbar.pipeline.wifi.shared.model.WifiNetworkModel
import com.android.systemui.statusbar.pipeline.wifi.ui.model.WifiIcon
+import java.util.function.Supplier
import javax.inject.Inject
+import javax.inject.Named
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
@@ -53,24 +55,24 @@
/**
* Models the UI state for the status bar wifi icon.
*
- * This class exposes three view models, one per status bar location: [home], [keyguard], and [qs].
- * In order to get the UI state for the wifi icon, you must use one of those view models (whichever
- * is correct for your location).
- *
- * Internally, this class maintains the current state of the wifi icon and notifies those three view
- * models of any changes.
+ * This is a singleton so that we don't have duplicate logs and should *not* be used directly to
+ * control views. Instead, use an instance of [LocationBasedWifiViewModel]. See
+ * [LocationBasedWifiViewModel.viewModelForLocation].
*/
@SysUISingleton
class WifiViewModel
@Inject
constructor(
airplaneModeViewModel: AirplaneModeViewModel,
+ // TODO(b/238425913): The wifi icon shouldn't need to consume mobile information. A
+ // container-level view model should do the work instead.
+ @Named(FIRST_MOBILE_SUB_SHOWING_NETWORK_TYPE_ICON)
+ shouldShowSignalSpacerProvider: Supplier<Flow<Boolean>>,
connectivityConstants: ConnectivityConstants,
private val context: Context,
@WifiTableLog wifiTableLogBuffer: TableLogBuffer,
interactor: WifiInteractor,
@Application private val scope: CoroutineScope,
- statusBarPipelineFlags: StatusBarPipelineFlags,
wifiConstants: WifiConstants,
) : WifiViewModelCommon {
/** Returns the icon to use based on the given network. */
@@ -183,6 +185,8 @@
override val isAirplaneSpacerVisible: Flow<Boolean> =
airplaneModeViewModel.isAirplaneModeIconVisible
+ override val isSignalSpacerVisible: Flow<Boolean> = shouldShowSignalSpacerProvider.get()
+
companion object {
@StringRes
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
index eccf023..617e192 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelCommon.kt
@@ -39,4 +39,7 @@
/** True if the airplane spacer view should be visible. */
val isAirplaneSpacerVisible: Flow<Boolean>
+
+ /** True if the spacer between the wifi icon and the RAT icon should be visible. */
+ val isSignalSpacerVisible: Flow<Boolean>
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
index b9f8dd94..fc906de 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardClockSwitchControllerTest.java
@@ -391,7 +391,7 @@
listenerArgumentCaptor.getValue().onCurrentClockChanged();
mExecutor.runAllReady();
- assertEquals(View.GONE, mFakeDateView.getVisibility());
+ assertEquals(View.INVISIBLE, mFakeDateView.getVisibility());
}
@Test
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/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/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/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
index 3ced7b2..b2bbcfd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt
@@ -49,6 +49,8 @@
FIVE_G_OVERRIDE_KEY to TelephonyIcons.NR_5G,
)
+ private val interactorCache: MutableMap<Int, FakeMobileIconInteractor> = mutableMapOf()
+
override val isDefaultConnectionFailed = MutableStateFlow(false)
override val filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
@@ -75,7 +77,15 @@
/** Always returns a new fake interactor */
override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
- return FakeMobileIconInteractor(tableLogBuffer)
+ return FakeMobileIconInteractor(tableLogBuffer).also { interactorCache[subId] = it }
+ }
+
+ /**
+ * Returns the most recently created interactor for the given subId, or null if an interactor
+ * has never been created for that sub.
+ */
+ fun getInteractorForSubId(subId: Int): FakeMobileIconInteractor? {
+ return interactorCache[subId]
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
index 01bec87..f8e1aa9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconsViewModelTest.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import androidx.test.filters.SmallTest
+import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.phone.StatusBarLocation
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
@@ -24,6 +25,7 @@
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
+import com.android.systemui.statusbar.pipeline.mobile.domain.model.NetworkTypeIconModel
import com.android.systemui.statusbar.pipeline.mobile.ui.MobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.ui.VerboseMobileViewLogger
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
@@ -135,8 +137,179 @@
assertThat(underTest.mobileIconSubIdCache).containsExactly(2, model2.commonImpl)
}
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_noSubs_false() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = emptyList()
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_oneSub_notShowingRat_false() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1)
+ // The unknown icon group doesn't show a RAT
+ interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_oneSub_showingRat_true() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1)
+ // The 3G icon group will show a RAT
+ interactor.getInteractorForSubId(1)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_updatesAsSubUpdates() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1)
+ val sub1Interactor = interactor.getInteractorForSubId(1)!!
+
+ sub1Interactor.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+ assertThat(latest).isTrue()
+
+ sub1Interactor.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ assertThat(latest).isFalse()
+
+ sub1Interactor.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.LTE)
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubNotShowingRat_false() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+ interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_multipleSubs_lastSubShowingRat_true() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+ assertThat(latest).isTrue()
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_subListUpdates_valAlsoUpdates() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ interactor.getInteractorForSubId(1)?.networkTypeIconGroup?.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ interactor.getInteractorForSubId(2)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+
+ assertThat(latest).isTrue()
+
+ // WHEN the sub list gets new subscriptions where the last subscription is not showing
+ // the network type icon
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2, SUB_3)
+ interactor.getInteractorForSubId(3)!!.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+
+ // THEN the flow updates
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun firstMobileSubShowingNetworkTypeIcon_subListReorders_valAlsoUpdates() =
+ testScope.runTest {
+ var latest: Boolean? = null
+ val job =
+ underTest.firstMobileSubShowingNetworkTypeIcon.onEach { latest = it }.launchIn(this)
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ // Immediately switch the order so that we've created both interactors
+ interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+ val sub1Interactor = interactor.getInteractorForSubId(1)!!
+ val sub2Interactor = interactor.getInteractorForSubId(2)!!
+
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+ sub1Interactor.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.UNKNOWN)
+ sub2Interactor.networkTypeIconGroup.value =
+ NetworkTypeIconModel.DefaultIcon(TelephonyIcons.THREE_G)
+ assertThat(latest).isTrue()
+
+ // WHEN sub1 becomes last and sub1 has no network type icon
+ interactor.filteredSubscriptions.value = listOf(SUB_2, SUB_1)
+
+ // THEN the flow updates
+ assertThat(latest).isFalse()
+
+ // WHEN sub2 becomes last and sub2 has a network type icon
+ interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
+
+ // THEN the flow updates
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
companion object {
private val SUB_1 = SubscriptionModel(subscriptionId = 1, isOpportunistic = false)
private val SUB_2 = SubscriptionModel(subscriptionId = 2, isOpportunistic = false)
+ private val SUB_3 = SubscriptionModel(subscriptionId = 3, isOpportunistic = false)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
index 5c19108..0d51af2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/view/ModernStatusBarWifiViewTest.kt
@@ -50,6 +50,7 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.flow.MutableStateFlow
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -98,12 +99,12 @@
val viewModelCommon =
WifiViewModel(
airplaneModeViewModel,
+ shouldShowSignalSpacerProvider = { MutableStateFlow(false) },
connectivityConstants,
context,
tableLogBuffer,
interactor,
scope,
- statusBarPipelineFlags,
wifiConstants,
)
viewModel =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
index ffe990b..e6724d8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelIconParameterizedTest.kt
@@ -27,7 +27,6 @@
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
-import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository
import com.android.systemui.statusbar.pipeline.airplane.domain.interactor.AirplaneModeInteractor
import com.android.systemui.statusbar.pipeline.airplane.ui.viewmodel.AirplaneModeViewModel
@@ -46,6 +45,7 @@
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.yield
@@ -66,7 +66,6 @@
private lateinit var underTest: WifiViewModel
- @Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
@Mock private lateinit var tableLogBuffer: TableLogBuffer
@Mock private lateinit var connectivityConstants: ConnectivityConstants
@Mock private lateinit var wifiConstants: WifiConstants
@@ -121,12 +120,12 @@
underTest =
WifiViewModel(
airplaneModeViewModel,
+ shouldShowSignalSpacerProvider = { MutableStateFlow(false) },
connectivityConstants,
context,
tableLogBuffer,
interactor,
scope,
- statusBarPipelineFlags,
wifiConstants,
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
index 802e360..0e303b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt
@@ -39,8 +39,8 @@
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
+import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
@@ -53,7 +53,6 @@
import org.mockito.MockitoAnnotations
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
class WifiViewModelTest : SysuiTestCase() {
@@ -68,6 +67,7 @@
private lateinit var wifiRepository: FakeWifiRepository
private lateinit var interactor: WifiInteractor
private lateinit var airplaneModeViewModel: AirplaneModeViewModel
+ private val shouldShowSignalSpacerProviderFlow = MutableStateFlow(false)
private lateinit var scope: CoroutineScope
@Before
@@ -473,6 +473,34 @@
job.cancel()
}
+ @Test
+ fun signalSpacer_firstSubNotShowingNetworkTypeIcon_outputsFalse() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isSignalSpacerVisible.onEach { latest = it }.launchIn(this)
+
+ shouldShowSignalSpacerProviderFlow.value = false
+ yield()
+
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun signalSpacer_firstSubIsShowingNetworkTypeIcon_outputsTrue() =
+ runBlocking(IMMEDIATE) {
+ var latest: Boolean? = null
+ val job = underTest.isSignalSpacerVisible.onEach { latest = it }.launchIn(this)
+
+ shouldShowSignalSpacerProviderFlow.value = true
+ yield()
+
+ assertThat(latest).isTrue()
+
+ job.cancel()
+ }
+
private fun createAndSetViewModel() {
// [WifiViewModel] creates its flows as soon as it's instantiated, and some of those flow
// creations rely on certain config values that we mock out in individual tests. This method
@@ -480,12 +508,12 @@
underTest =
WifiViewModel(
airplaneModeViewModel,
+ { shouldShowSignalSpacerProviderFlow },
connectivityConstants,
context,
tableLogBuffer,
interactor,
scope,
- statusBarPipelineFlags,
wifiConstants,
)
}
diff --git a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
index 0b019d1..3041888 100644
--- a/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
+++ b/packages/SystemUI/unfold/src/com/android/systemui/unfold/progress/UnfoldRemoteFilter.kt
@@ -35,7 +35,7 @@
private var inProgress = false
- private var processedProgress: Float = 0.0f
+ private var processedProgress: Float = 1.0f
set(newProgress) {
if (inProgress) {
logCounter({ "$TAG#filtered_progress" }, newProgress)
diff --git a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
index 1990fe2..98aebdd 100644
--- a/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
+++ b/services/backup/java/com/android/server/backup/BackupAndRestoreFeatureFlags.java
@@ -77,4 +77,19 @@
/* name= */ "full_backup_utils_route_buffer_size_bytes",
/* defaultValue= */ 32 * 1024); // 32 KB
}
+
+ /**
+ * Retrieves the value of the flag
+ * "unified_restore_continue_after_transport_failure_in_kv_restore".
+ * If true, Unified restore task will continue to next package if key-value restore of a
+ * package fails due to Transport-level failure. See b/128499560 for more context.
+ */
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public static boolean getUnifiedRestoreContinueAfterTransportFailureInKvRestore() {
+ return DeviceConfig.getBoolean(
+ NAMESPACE,
+ /* name= */
+ "unified_restore_continue_after_transport_failure_in_kv_restore",
+ /* defaultValue= */ true);
+ }
}
diff --git a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
index 18e28de..1656b6f 100644
--- a/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
+++ b/services/backup/java/com/android/server/backup/restore/PerformUnifiedRestoreTask.java
@@ -57,6 +57,7 @@
import com.android.server.EventLogTags;
import com.android.server.LocalServices;
import com.android.server.backup.BackupAgentTimeoutParameters;
+import com.android.server.backup.BackupAndRestoreFeatureFlags;
import com.android.server.backup.BackupRestoreTask;
import com.android.server.backup.BackupUtils;
import com.android.server.backup.OperationStorage;
@@ -168,11 +169,13 @@
private final BackupEligibilityRules mBackupEligibilityRules;
@VisibleForTesting
- PerformUnifiedRestoreTask(UserBackupManagerService backupManagerService) {
+ PerformUnifiedRestoreTask(
+ UserBackupManagerService backupManagerService,
+ TransportConnection transportConnection) {
mListener = null;
mAgentTimeoutParameters = null;
mOperationStorage = null;
- mTransportConnection = null;
+ mTransportConnection = transportConnection;
mTransportManager = null;
mEphemeralOpToken = 0;
mUserId = 0;
@@ -731,13 +734,18 @@
ParcelFileDescriptor.MODE_TRUNCATE);
if (transport.getRestoreData(stage) != BackupTransport.TRANSPORT_OK) {
- // Transport-level failure, so we wind everything up and
- // terminate the restore operation.
+ // Transport-level failure. This failure could be specific to package currently in
+ // restore.
Slog.e(TAG, "Error getting restore data for " + packageName);
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
stage.close();
downloadFile.delete();
- executeNextState(UnifiedRestoreState.FINAL);
+ UnifiedRestoreState nextState =
+ BackupAndRestoreFeatureFlags
+ .getUnifiedRestoreContinueAfterTransportFailureInKvRestore()
+ ? UnifiedRestoreState.RUNNING_QUEUE
+ : UnifiedRestoreState.FINAL;
+ executeNextState(nextState);
return;
}
@@ -1358,6 +1366,7 @@
executeNextState(UnifiedRestoreState.RUNNING_QUEUE);
}
+ @VisibleForTesting
void executeNextState(UnifiedRestoreState nextState) {
if (MORE_DEBUG) {
Slog.i(TAG, " => executing next step on "
@@ -1369,6 +1378,26 @@
backupManagerService.getBackupHandler().sendMessage(msg);
}
+ @VisibleForTesting
+ UnifiedRestoreState getCurrentUnifiedRestoreStateForTesting() {
+ return mState;
+ }
+
+ @VisibleForTesting
+ void setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState state) {
+ mState = state;
+ }
+
+ @VisibleForTesting
+ void setStateDirForTesting(File stateDir) {
+ mStateDir = stateDir;
+ }
+
+ @VisibleForTesting
+ void initiateOneRestoreForTesting(PackageInfo app, long appVersionCode) {
+ initiateOneRestore(app, appVersionCode);
+ }
+
// restore observer support
void sendStartRestore(int numPackages) {
if (mObserver != null) {
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
index 291c0587..9644642 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceManagerService.java
@@ -151,15 +151,14 @@
}
void onCameraAccessBlocked(int appUid) {
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- CharSequence deviceName = mVirtualDevices.valueAt(i).getDisplayName();
- mVirtualDevices.valueAt(i).showToastWhereUidIsRunning(appUid,
- getContext().getString(
- com.android.internal.R.string.vdm_camera_access_denied,
- deviceName),
- Toast.LENGTH_LONG, Looper.myLooper());
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
+ virtualDevice.showToastWhereUidIsRunning(appUid,
+ getContext().getString(
+ com.android.internal.R.string.vdm_camera_access_denied,
+ virtualDevice.getDisplayName()),
+ Toast.LENGTH_LONG, Looper.myLooper());
}
}
@@ -265,6 +264,16 @@
cdm.removeOnAssociationsChangedListener(mCdmAssociationListener);
}
+ private ArrayList<VirtualDeviceImpl> getVirtualDevicesSnapshot() {
+ synchronized (mVirtualDeviceManagerLock) {
+ ArrayList<VirtualDeviceImpl> virtualDevices = new ArrayList<>(mVirtualDevices.size());
+ for (int i = 0; i < mVirtualDevices.size(); i++) {
+ virtualDevices.add(mVirtualDevices.valueAt(i));
+ }
+ return virtualDevices;
+ }
+ }
+
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
private final VirtualDeviceImpl.PendingTrampolineCallback mPendingTrampolineCallback =
@@ -314,6 +323,17 @@
Objects.requireNonNull(activityListener);
Objects.requireNonNull(soundEffectListener);
+ final UserHandle userHandle = getCallingUserHandle();
+ final CameraAccessController cameraAccessController =
+ getCameraAccessController(userHandle);
+ final int deviceId = sNextUniqueIndex.getAndIncrement();
+ final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
+ runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
+ VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
+ associationInfo, VirtualDeviceManagerService.this, token, callingUid,
+ deviceId, cameraAccessController,
+ mPendingTrampolineCallback, activityListener,
+ soundEffectListener, runningAppsChangedCallback, params);
synchronized (mVirtualDeviceManagerLock) {
if (mVirtualDevices.size() == 0) {
final long callindId = Binder.clearCallingIdentity();
@@ -323,21 +343,9 @@
Binder.restoreCallingIdentity(callindId);
}
}
-
- final UserHandle userHandle = getCallingUserHandle();
- final CameraAccessController cameraAccessController =
- getCameraAccessController(userHandle);
- final int deviceId = sNextUniqueIndex.getAndIncrement();
- final Consumer<ArraySet<Integer>> runningAppsChangedCallback =
- runningUids -> notifyRunningAppsChanged(deviceId, runningUids);
- VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl(getContext(),
- associationInfo, VirtualDeviceManagerService.this, token, callingUid,
- deviceId, cameraAccessController,
- mPendingTrampolineCallback, activityListener,
- soundEffectListener, runningAppsChangedCallback, params);
mVirtualDevices.put(deviceId, virtualDevice);
- return virtualDevice;
}
+ return virtualDevice;
}
@Override // Binder call
@@ -399,12 +407,11 @@
if (displayId == Display.INVALID_DISPLAY || displayId == Display.DEFAULT_DISPLAY) {
return Context.DEVICE_ID_DEFAULT;
}
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
- if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) {
- return virtualDevice.getDeviceId();
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ VirtualDeviceImpl virtualDevice = virtualDevicesSnapshot.get(i);
+ if (virtualDevice.isDisplayOwnedByVirtualDevice(displayId)) {
+ return virtualDevice.getDeviceId();
}
}
return Context.DEVICE_ID_DEFAULT;
@@ -496,10 +503,9 @@
return;
}
fout.println("Created virtual devices: ");
- synchronized (mVirtualDeviceManagerLock) {
- for (int i = 0; i < mVirtualDevices.size(); i++) {
- mVirtualDevices.valueAt(i).dump(fd, fout, args);
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ virtualDevicesSnapshot.get(i).dump(fd, fout, args);
}
}
}
@@ -516,33 +522,30 @@
@Override
public int getDeviceOwnerUid(int deviceId) {
+ VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
- VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
- return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID;
+ virtualDevice = mVirtualDevices.get(deviceId);
}
+ return virtualDevice != null ? virtualDevice.getOwnerUid() : Process.INVALID_UID;
}
@Override
public @Nullable VirtualSensor getVirtualSensor(int deviceId, int handle) {
+ VirtualDeviceImpl virtualDevice;
synchronized (mVirtualDeviceManagerLock) {
- VirtualDeviceImpl virtualDevice = mVirtualDevices.get(deviceId);
- if (virtualDevice != null) {
- return virtualDevice.getVirtualSensorByHandle(handle);
- }
+ virtualDevice = mVirtualDevices.get(deviceId);
}
- return null;
+ return virtualDevice != null ? virtualDevice.getVirtualSensorByHandle(handle) : null;
}
@Override
public @NonNull ArraySet<Integer> getDeviceIdsForUid(int uid) {
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
ArraySet<Integer> result = new ArraySet<>();
- synchronized (mVirtualDeviceManagerLock) {
- int size = mVirtualDevices.size();
- for (int i = 0; i < size; i++) {
- VirtualDeviceImpl device = mVirtualDevices.valueAt(i);
- if (device.isAppRunningOnVirtualDevice(uid)) {
- result.add(device.getDeviceId());
- }
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ VirtualDeviceImpl device = virtualDevicesSnapshot.get(i);
+ if (device.isAppRunningOnVirtualDevice(uid)) {
+ result.add(device.getDeviceId());
}
}
return result;
@@ -630,12 +633,10 @@
@Override
public boolean isAppRunningOnAnyVirtualDevice(int uid) {
- synchronized (mVirtualDeviceManagerLock) {
- int size = mVirtualDevices.size();
- for (int i = 0; i < size; i++) {
- if (mVirtualDevices.valueAt(i).isAppRunningOnVirtualDevice(uid)) {
- return true;
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ if (virtualDevicesSnapshot.get(i).isAppRunningOnVirtualDevice(uid)) {
+ return true;
}
}
return false;
@@ -643,12 +644,10 @@
@Override
public boolean isDisplayOwnedByAnyVirtualDevice(int displayId) {
- synchronized (mVirtualDeviceManagerLock) {
- int size = mVirtualDevices.size();
- for (int i = 0; i < size; i++) {
- if (mVirtualDevices.valueAt(i).isDisplayOwnedByVirtualDevice(displayId)) {
- return true;
- }
+ ArrayList<VirtualDeviceImpl> virtualDevicesSnapshot = getVirtualDevicesSnapshot();
+ for (int i = 0; i < virtualDevicesSnapshot.size(); i++) {
+ if (virtualDevicesSnapshot.get(i).isDisplayOwnedByVirtualDevice(displayId)) {
+ return true;
}
}
return false;
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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index e248007..ee18ed5 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -1019,6 +1019,24 @@
r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
service, neededGrants, callingUid, callingProcessName, callingPackage));
+ // We want to allow scheduling user-initiated jobs when the app is running a
+ // foreground service that was started in the same conditions that allows for scheduling
+ // UI jobs. More explicitly, we want to allow scheduling UI jobs when the app is running
+ // an FGS that started when the app was in the TOP or a BAL-approved state.
+ final boolean isFgs = r.isForeground || r.fgRequired;
+ if (isFgs) {
+ // As of Android UDC, the conditions required for the while-in-use permissions
+ // are the same conditions that we want, so we piggyback on that logic.
+ // Use that as a shortcut if possible to avoid having to recheck all the conditions.
+ final boolean whileInUseAllowsUiJobScheduling =
+ ActivityManagerService.doesReasonCodeAllowSchedulingUserInitiatedJobs(
+ r.mAllowWhileInUsePermissionInFgsReason);
+ r.updateAllowUiJobScheduling(whileInUseAllowsUiJobScheduling
+ || mAm.canScheduleUserInitiatedJobs(callingUid, callingPid, callingPackage));
+ } else {
+ r.updateAllowUiJobScheduling(false);
+ }
+
if (fgRequired) {
// We are now effectively running a foreground service.
synchronized (mAm.mProcessStats.mLock) {
@@ -7362,26 +7380,12 @@
} else {
allowWhileInUse = REASON_UNKNOWN;
}
- // We want to allow scheduling user-initiated jobs when the app is running a
- // foreground service that was started in the same conditions that allows for scheduling
- // UI jobs. More explicitly, we want to allow scheduling UI jobs when the app is running
- // an FGS that started when the app was in the TOP or a BAL-approved state.
- // As of Android UDC, the conditions required for the while-in-use permissions
- // are the same conditions that we want, so we piggyback on that logic.
- // We use that as a shortcut if possible so we don't have to recheck all the conditions.
- final boolean isFgs = r.isForeground || r.fgRequired;
- if (isFgs) {
- r.updateAllowUiJobScheduling(ActivityManagerService
- .doesReasonCodeAllowSchedulingUserInitiatedJobs(allowWhileInUse)
- || mAm.canScheduleUserInitiatedJobs(
- callingUid, callingPid, callingPackage, true));
- } else {
- r.updateAllowUiJobScheduling(false);
- }
+ r.mAllowWhileInUsePermissionInFgsReason = allowWhileInUse;
}
void resetFgsRestrictionLocked(ServiceRecord r) {
r.mAllowWhileInUsePermissionInFgs = false;
+ r.mAllowWhileInUsePermissionInFgsReason = REASON_DENIED;
r.mAllowStartForeground = REASON_DENIED;
r.mInfoAllowStartForeground = null;
r.mInfoTempFgsAllowListReason = null;
@@ -7425,14 +7429,17 @@
final int uidState = mAm.getUidStateLocked(callingUid);
if (ret == REASON_DENIED) {
- // Is the calling UID at PROCESS_STATE_TOP or above?
+ // Allow FGS while-in-use if the caller's process state is PROCESS_STATE_PERSISTENT,
+ // PROCESS_STATE_PERSISTENT_UI or PROCESS_STATE_TOP.
if (uidState <= PROCESS_STATE_TOP) {
ret = getReasonCodeFromProcState(uidState);
}
}
if (ret == REASON_DENIED) {
- // Does the calling UID have any visible activity?
+ // Allow FGS while-in-use if the caller has visible activity.
+ // Here we directly check ActivityTaskManagerService, instead of checking
+ // PendingStartActivityUids in ActivityManagerService, which gives the same result.
final boolean isCallingUidVisible = mAm.mAtmInternal.isUidForeground(callingUid);
if (isCallingUidVisible) {
ret = REASON_UID_VISIBLE;
@@ -7440,7 +7447,8 @@
}
if (ret == REASON_DENIED) {
- // Is the allow activity background start flag on?
+ // Allow FGS while-in-use if the background activity start flag is on. Because
+ // activity start can lead to FGS start in TOP state and obtain while-in-use.
if (backgroundStartPrivileges.allowsBackgroundActivityStarts()) {
ret = REASON_START_ACTIVITY_FLAG;
}
@@ -7449,6 +7457,7 @@
if (ret == REASON_DENIED) {
boolean isCallerSystem = false;
final int callingAppId = UserHandle.getAppId(callingUid);
+ // Allow FGS while-in-use for a list of special UIDs.
switch (callingAppId) {
case ROOT_UID:
case SYSTEM_UID:
@@ -7467,6 +7476,10 @@
}
if (ret == REASON_DENIED) {
+ // Allow FGS while-in-use if the WindowManager allows background activity start.
+ // This is mainly to get the 10 seconds grace period if any activity in the caller has
+ // either started or finished very recently. The binding flag
+ // BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS is also allowed by the check here.
final Integer allowedType = mAm.mProcessList.searchEachLruProcessesLOSP(false, pr -> {
if (pr.uid == callingUid) {
if (pr.getWindowProcessController().areBackgroundFgsStartsAllowed()) {
@@ -7481,6 +7494,12 @@
}
if (ret == REASON_DENIED) {
+ // Allow FGS while-in-use if the caller UID is in ActivityManagerService's
+ // mFgsWhileInUseTempAllowList. This is a temp allowlist to allow FGS while-in-use. It
+ // is used when MediaSessionService's bluetooth button or play/resume/stop commands are
+ // issued. The typical temp allowlist duration is 10 seconds.
+ // This temp allowlist mechanism can also be called by other system_server internal
+ // components such as Telephone/VOIP if they want to start a FGS and get while-in-use.
if (mAm.mInternal.isTempAllowlistedForFgsWhileInUse(callingUid)) {
return REASON_TEMP_ALLOWED_WHILE_IN_USE;
}
@@ -7488,6 +7507,8 @@
if (ret == REASON_DENIED) {
if (targetProcess != null) {
+ // Allow FGS while-in-use if the caller of the instrumentation has
+ // START_ACTIVITIES_FROM_BACKGROUND permission.
ActiveInstrumentation instr = targetProcess.getActiveInstrumentation();
if (instr != null && instr.mHasBackgroundActivityStartsPermission) {
ret = REASON_INSTR_BACKGROUND_ACTIVITY_PERMISSION;
@@ -7496,6 +7517,9 @@
}
if (ret == REASON_DENIED) {
+ // Allow FGS while-in-use if the caller has START_ACTIVITIES_FROM_BACKGROUND
+ // permission, because starting an activity can lead to starting FGS from the TOP state
+ // and obtain while-in-use.
if (mAm.checkPermission(START_ACTIVITIES_FROM_BACKGROUND, callingPid, callingUid)
== PERMISSION_GRANTED) {
ret = REASON_BACKGROUND_ACTIVITY_PERMISSION;
@@ -7503,6 +7527,8 @@
}
if (ret == REASON_DENIED) {
+ // Allow FGS while-in-use if the caller is in the while-in-use allowlist. Right now
+ // AttentionService and SystemCaptionsService packageName are in this allowlist.
if (verifyPackage(callingPackage, callingUid)) {
final boolean isAllowedPackage =
mAllowListWhileInUsePermissionInFgs.contains(callingPackage);
@@ -7517,7 +7543,7 @@
}
if (ret == REASON_DENIED) {
- // Is the calling UID a device owner app?
+ // Allow FGS while-in-use if the caller is the device owner.
final boolean isDeviceOwner = mAm.mInternal.isDeviceOwner(callingUid);
if (isDeviceOwner) {
ret = REASON_DEVICE_OWNER;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index b32f8c9..97d34b8 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -6189,8 +6189,8 @@
final ProcessServiceRecord psr = pr.mServices;
if (psr != null && psr.hasForegroundServices()) {
- for (int s = psr.numberOfExecutingServices() - 1; s >= 0; --s) {
- final ServiceRecord sr = psr.getExecutingServiceAt(s);
+ for (int s = psr.numberOfRunningServices() - 1; s >= 0; --s) {
+ final ServiceRecord sr = psr.getRunningServiceAt(s);
if (sr.isForeground && sr.mAllowUiJobScheduling) {
return true;
}
@@ -6205,12 +6205,7 @@
* {@link android.app.job.JobInfo.Builder#setUserInitiated(boolean) user-initiated job}.
*/
// TODO(262260570): log allow reason to an atom
- private boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
- return canScheduleUserInitiatedJobs(uid, pid, pkgName, false);
- }
-
- boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName,
- boolean skipWhileInUseCheck) {
+ boolean canScheduleUserInitiatedJobs(int uid, int pid, String pkgName) {
synchronized (this) {
final ProcessRecord processRecord;
synchronized (mPidsSelfLocked) {
@@ -6240,7 +6235,7 @@
// As of Android UDC, the conditions required to grant a while-in-use permission
// covers the majority of those cases, and so we piggyback on that logic as the base.
// Missing cases are added after.
- if (!skipWhileInUseCheck && mServices.canAllowWhileInUsePermissionInFgsLocked(
+ if (mServices.canAllowWhileInUsePermissionInFgsLocked(
pid, uid, pkgName, processRecord, backgroundStartPrivileges)) {
return true;
}
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/am/ServiceRecord.java b/services/core/java/com/android/server/am/ServiceRecord.java
index edf0dbd..a875860 100644
--- a/services/core/java/com/android/server/am/ServiceRecord.java
+++ b/services/core/java/com/android/server/am/ServiceRecord.java
@@ -174,6 +174,8 @@
// allow while-in-use permissions in foreground service or not.
// while-in-use permissions in FGS started from background might be restricted.
boolean mAllowWhileInUsePermissionInFgs;
+ @PowerExemptionManager.ReasonCode
+ int mAllowWhileInUsePermissionInFgsReason;
// A copy of mAllowWhileInUsePermissionInFgs's value when the service is entering FGS state.
boolean mAllowWhileInUsePermissionInFgsAtEntering;
/** Allow scheduling user-initiated jobs from the background. */
@@ -609,6 +611,8 @@
}
pw.print(prefix); pw.print("allowWhileInUsePermissionInFgs=");
pw.println(mAllowWhileInUsePermissionInFgs);
+ pw.print(prefix); pw.print("mAllowWhileInUsePermissionInFgsReason=");
+ pw.println(mAllowWhileInUsePermissionInFgsReason);
pw.print(prefix); pw.print("allowUiJobScheduling="); pw.println(mAllowUiJobScheduling);
pw.print(prefix); pw.print("recentCallingPackage=");
pw.println(mRecentCallingPackage);
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index ac55f28..d1cbbfc 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -7053,13 +7053,16 @@
return deviceSet.iterator().next();
} else {
// Multiple device selection is either:
+ // - dock + one other device: give priority to dock in this case.
// - speaker + one other device: give priority to speaker in this case.
// - one A2DP device + another device: happens with duplicated output. In this case
// retain the device on the A2DP output as the other must not correspond to an active
// selection if not the speaker.
// - HDMI-CEC system audio mode only output: give priority to available item in order.
- if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
+ if (deviceSet.contains(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET)) {
+ return AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET;
+ } else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER)) {
return AudioSystem.DEVICE_OUT_SPEAKER;
} else if (deviceSet.contains(AudioSystem.DEVICE_OUT_SPEAKER_SAFE)) {
// Note: DEVICE_OUT_SPEAKER_SAFE not present in getDeviceSetForStreamDirect
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/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 1301cd47..ebcbfed 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -3335,7 +3335,7 @@
}
checkCallerIsSameApp(pkg);
- final boolean isSystemToast = isCallerSystemOrPhone()
+ final boolean isSystemToast = isCallerIsSystemOrSystemUi()
|| PackageManagerService.PLATFORM_PACKAGE_NAME.equals(pkg);
boolean isAppRenderedToast = (callback != null);
if (!checkCanEnqueueToast(pkg, callingUid, displayId, isAppRenderedToast,
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/ShutdownCheckPoints.java b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
index 32f1bcf..546dc81 100644
--- a/services/core/java/com/android/server/power/ShutdownCheckPoints.java
+++ b/services/core/java/com/android/server/power/ShutdownCheckPoints.java
@@ -295,11 +295,18 @@
@Nullable
String getProcessName() {
try {
- List<ActivityManager.RunningAppProcessInfo> runningProcesses =
- mActivityManager.getRunningAppProcesses();
- for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
- if (processInfo.pid == mCallerProcessId) {
- return processInfo.processName;
+ List<ActivityManager.RunningAppProcessInfo> runningProcesses = null;
+ if (mActivityManager != null) {
+ runningProcesses = mActivityManager.getRunningAppProcesses();
+ } else {
+ Slog.v(TAG, "No ActivityManager available to find process name with pid="
+ + mCallerProcessId);
+ }
+ if (runningProcesses != null) {
+ for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
+ if (processInfo.pid == mCallerProcessId) {
+ return processInfo.processName;
+ }
}
}
} catch (RemoteException e) {
diff --git a/services/core/java/com/android/server/power/ShutdownThread.java b/services/core/java/com/android/server/power/ShutdownThread.java
index c2d4ac6..b1430e7 100644
--- a/services/core/java/com/android/server/power/ShutdownThread.java
+++ b/services/core/java/com/android/server/power/ShutdownThread.java
@@ -703,17 +703,20 @@
// vibrate before shutting down
Vibrator vibrator = new SystemVibrator(context);
try {
- vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
+ if (vibrator.hasVibrator()) {
+ vibrator.vibrate(SHUTDOWN_VIBRATE_MS, VIBRATION_ATTRIBUTES);
+ // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
+ try {
+ Thread.sleep(SHUTDOWN_VIBRATE_MS);
+ } catch (InterruptedException unused) {
+ // this is not critical and does not require logging
+ }
+ }
} catch (Exception e) {
// Failure to vibrate shouldn't interrupt shutdown. Just log it.
Log.w(TAG, "Failed to vibrate during shutdown.", e);
}
- // vibrator is asynchronous so we need to wait to avoid shutting down too soon.
- try {
- Thread.sleep(SHUTDOWN_VIBRATE_MS);
- } catch (InterruptedException unused) {
- }
}
// Shutdown power
Log.i(TAG, "Performing low-level shutdown...");
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/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java
index 7718dd8..8bbcd27 100644
--- a/services/core/java/com/android/server/wm/ActivityClientController.java
+++ b/services/core/java/com/android/server/wm/ActivityClientController.java
@@ -97,7 +97,6 @@
import com.android.internal.app.AssistUtils;
import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.server.LocalServices;
import com.android.server.Watchdog;
@@ -1142,18 +1141,11 @@
// Initiate the transition.
final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */, controller,
mService.mWindowManager.mSyncEngine);
- if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Multiwindow Fullscreen Request: %s", transition);
- r.mTransitionController.queueCollecting(transition,
- () -> {
- executeFullscreenRequestTransition(fullscreenRequest, callback, r,
- transition, true /* queued */);
- });
- } else {
- executeFullscreenRequestTransition(fullscreenRequest, callback, r, transition,
- false /* queued */);
- }
+ r.mTransitionController.startCollectOrQueue(transition,
+ (deferred) -> {
+ executeFullscreenRequestTransition(fullscreenRequest, callback, r,
+ transition, deferred);
+ });
}
private void executeFullscreenRequestTransition(int fullscreenRequest, IRemoteCallback callback,
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 0b98495..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);
@@ -9806,7 +9804,7 @@
if (mTransitionController.isShellTransitionsEnabled()) {
final Transition transition = new Transition(TRANSIT_RELAUNCH, 0 /* flags */,
mTransitionController, mWmService.mSyncEngine);
- final Runnable executeRestart = () -> {
+ mTransitionController.startCollectOrQueue(transition, (deferred) -> {
if (mState != RESTARTING_PROCESS || !attachedToProcess()) {
transition.abort();
return;
@@ -9818,13 +9816,7 @@
mTransitionController.requestStartTransition(transition, task,
null /* remoteTransition */, null /* displayChange */);
scheduleStopForRestartProcess();
- };
- if (mWmService.mSyncEngine.hasActiveSync()) {
- mTransitionController.queueCollecting(transition, executeRestart);
- } else {
- mTransitionController.moveToCollecting(transition);
- executeRestart.run();
- }
+ });
} else {
startFreezingScreen();
scheduleStopForRestartProcess();
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index e780716..064af0f 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -250,7 +250,6 @@
import com.android.internal.os.TransferPipe;
import com.android.internal.policy.AttributeCache;
import com.android.internal.policy.KeyguardDismissCallback;
-import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.FastPrintWriter;
@@ -2873,28 +2872,19 @@
final Transition transition = new Transition(TRANSIT_CHANGE, 0 /* flags */,
getTransitionController(), mWindowManager.mSyncEngine);
- if (mWindowManager.mSyncEngine.hasActiveSync()) {
- getTransitionController().queueCollecting(transition,
- () -> {
- if (!task.getWindowConfiguration().canResizeTask()) {
- Slog.w(TAG, "resizeTask not allowed on task=" + task);
- transition.abort();
- return;
- }
- getTransitionController().requestStartTransition(transition, task,
- null /* remoteTransition */, null /* displayChange */);
- getTransitionController().collect(task);
- task.resize(bounds, resizeMode, preserveWindow);
- transition.setReady(task, true);
- });
- } else {
- getTransitionController().moveToCollecting(transition);
- getTransitionController().requestStartTransition(transition, task,
- null /* remoteTransition */, null /* displayChange */);
- getTransitionController().collect(task);
- task.resize(bounds, resizeMode, preserveWindow);
- transition.setReady(task, true);
- }
+ getTransitionController().startCollectOrQueue(transition,
+ (deferred) -> {
+ if (deferred && !task.getWindowConfiguration().canResizeTask()) {
+ Slog.w(TAG, "resizeTask not allowed on task=" + task);
+ transition.abort();
+ return;
+ }
+ getTransitionController().requestStartTransition(transition, task,
+ null /* remoteTransition */, null /* displayChange */);
+ getTransitionController().collect(task);
+ task.resize(bounds, resizeMode, preserveWindow);
+ transition.setReady(task, true);
+ });
}
} finally {
Binder.restoreCallingIdentity(ident);
@@ -3625,30 +3615,25 @@
mActivityClientController.dismissKeyguard(r.token, new KeyguardDismissCallback() {
@Override
public void onDismissSucceeded() {
- if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Pip-Enter: %s", transition);
- getTransitionController().queueCollecting(transition, enterPipRunnable);
- } else {
- // Move to collecting immediately to "claim" the sync-engine for this
- // transition.
- if (transition != null) {
- getTransitionController().moveToCollecting(transition);
- }
+ if (transition == null) {
mH.post(enterPipRunnable);
+ return;
}
+ getTransitionController().startCollectOrQueue(transition, (deferred) -> {
+ if (deferred) {
+ enterPipRunnable.run();
+ } else {
+ mH.post(enterPipRunnable);
+ }
+ });
}
}, null /* message */);
} else {
// Enter picture in picture immediately otherwise
- if (transition != null && mWindowManager.mSyncEngine.hasActiveSync()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Pip-Enter: %s", transition);
- getTransitionController().queueCollecting(transition, enterPipRunnable);
+ if (transition != null) {
+ getTransitionController().startCollectOrQueue(transition,
+ (deferred) -> enterPipRunnable.run());
} else {
- if (transition != null) {
- getTransitionController().moveToCollecting(transition);
- }
enterPipRunnable.run();
}
}
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/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index ad93454..cd4b3c5 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -2328,17 +2328,23 @@
// this as a signal to the transition-player.
final Transition transition = new Transition(TRANSIT_SLEEP, 0 /* flags */,
display.mTransitionController, mWmService.mSyncEngine);
- final Runnable sendSleepTransition = () -> {
+ final TransitionController.OnStartCollect sendSleepTransition = (deferred) -> {
display.mTransitionController.requestStartTransition(transition,
null /* trigger */, null /* remote */, null /* display */);
// Force playing immediately so that unrelated ops can't be collected.
transition.playNow();
};
- if (display.mTransitionController.isCollecting()) {
- display.mTransitionController.queueCollecting(transition, sendSleepTransition);
- } else {
+ if (!display.mTransitionController.isCollecting()) {
+ // Since this bypasses sync, submit directly ignoring whether sync-engine
+ // is active.
+ if (mWindowManager.mSyncEngine.hasActiveSync()) {
+ Slog.w(TAG, "Ongoing sync outside of a transition.");
+ }
display.mTransitionController.moveToCollecting(transition);
- sendSleepTransition.run();
+ sendSleepTransition.onCollectStarted(false /* deferred */);
+ } else {
+ display.mTransitionController.startCollectOrQueue(transition,
+ sendSleepTransition);
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 89f9753..9363eb5 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -188,7 +188,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.protolog.ProtoLogGroup;
import com.android.internal.protolog.common.ProtoLog;
import com.android.internal.util.XmlUtils;
import com.android.internal.util.function.pooled.PooledLambda;
@@ -3253,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);
@@ -5632,29 +5632,20 @@
final Transition transition = new Transition(TRANSIT_TO_BACK, 0 /* flags */,
mTransitionController, mWmService.mSyncEngine);
// Guarantee that this gets its own transition by queueing on SyncEngine
- if (mWmService.mSyncEngine.hasActiveSync()) {
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Move-to-back: %s", transition);
- mTransitionController.queueCollecting(transition,
- () -> {
- // Need to check again since this happens later and the system might
- // be in a different state.
- if (!canMoveTaskToBack(tr)) {
- Slog.e(TAG, "Failed to move task to back after saying we could: "
- + tr.mTaskId);
- transition.abort();
- return;
- }
- mTransitionController.requestStartTransition(transition, tr,
- null /* remoteTransition */, null /* displayChange */);
- moveTaskToBackInner(tr);
- });
- } else {
- mTransitionController.moveToCollecting(transition);
- mTransitionController.requestStartTransition(transition, tr,
- null /* remoteTransition */, null /* displayChange */);
- moveTaskToBackInner(tr);
- }
+ mTransitionController.startCollectOrQueue(transition,
+ (deferred) -> {
+ // Need to check again if deferred since the system might
+ // be in a different state.
+ if (deferred && !canMoveTaskToBack(tr)) {
+ Slog.e(TAG, "Failed to move task to back after saying we could: "
+ + tr.mTaskId);
+ transition.abort();
+ return;
+ }
+ mTransitionController.requestStartTransition(transition, tr,
+ null /* remoteTransition */, null /* displayChange */);
+ moveTaskToBackInner(tr);
+ });
} else {
// Skip the transition for pinned task.
if (!inPinnedWindowingMode()) {
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/TransitionController.java b/services/core/java/com/android/server/wm/TransitionController.java
index 4c1c2ff..cbb4fe2 100644
--- a/services/core/java/com/android/server/wm/TransitionController.java
+++ b/services/core/java/com/android/server/wm/TransitionController.java
@@ -884,13 +884,30 @@
proto.end(token);
}
- void queueCollecting(Transition transit, Runnable onCollectStart) {
- mAtm.mWindowManager.mSyncEngine.queueSyncSet(
- // Make sure to collect immediately to prevent another transition
- // from sneaking in before it. Note: moveToCollecting internally
- // calls startSyncSet.
- () -> moveToCollecting(transit),
- onCollectStart);
+ /** Returns {@code true} if it started collecting, {@code false} if it was queued. */
+ boolean startCollectOrQueue(Transition transit, OnStartCollect onStartCollect) {
+ if (mAtm.mWindowManager.mSyncEngine.hasActiveSync()) {
+ if (!isCollecting()) {
+ Slog.w(TAG, "Ongoing Sync outside of transition.");
+ }
+ ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS_MIN,
+ "Queueing transition: %s", transit);
+ mAtm.mWindowManager.mSyncEngine.queueSyncSet(
+ // Make sure to collect immediately to prevent another transition
+ // from sneaking in before it. Note: moveToCollecting internally
+ // calls startSyncSet.
+ () -> moveToCollecting(transit),
+ () -> onStartCollect.onCollectStarted(true /* deferred */));
+ return false;
+ } else {
+ moveToCollecting(transit);
+ onStartCollect.onCollectStarted(false /* deferred */);
+ return true;
+ }
+ }
+
+ interface OnStartCollect {
+ void onCollectStarted(boolean deferred);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index f63470f2..ee86b97 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -280,41 +280,31 @@
applyTransaction(t, -1 /* syncId */, null, caller);
return null;
}
- // In cases where transition is already provided, the "readiness lifecycle" of the
- // transition is determined outside of this transaction. However, if this is a
- // direct call from shell, the entire transition lifecycle is contained in the
- // provided transaction and thus we can setReady immediately after apply.
- final boolean needsSetReady = transition == null && t != null;
final WindowContainerTransaction wct =
t != null ? t : new WindowContainerTransaction();
if (transition == null) {
if (type < 0) {
throw new IllegalArgumentException("Can't create transition with no type");
}
- transition = new Transition(type, 0 /* flags */, mTransitionController,
- mService.mWindowManager.mSyncEngine);
- // If there is already a collecting transition, queue up a new transition and
- // return that. The actual start and apply will then be deferred until that
- // transition starts collecting. This should almost never happen except during
- // tests.
- if (mService.mWindowManager.mSyncEngine.hasActiveSync()) {
- Slog.w(TAG, "startTransition() while one is already collecting.");
- final Transition nextTransition = transition;
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Transition: %s", nextTransition);
- mTransitionController.queueCollecting(nextTransition,
- () -> {
- nextTransition.start();
- nextTransition.mLogger.mStartWCT = wct;
- applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
- if (needsSetReady) {
- nextTransition.setAllReady();
- }
- });
- return nextTransition.getToken();
- }
- mTransitionController.moveToCollecting(transition);
+ // This is a direct call from shell, so the entire transition lifecycle is
+ // contained in the provided transaction if provided. Thus, we can setReady
+ // immediately after apply.
+ final boolean needsSetReady = t != null;
+ final Transition nextTransition = new Transition(type, 0 /* flags */,
+ mTransitionController, mService.mWindowManager.mSyncEngine);
+ mTransitionController.startCollectOrQueue(nextTransition,
+ (deferred) -> {
+ nextTransition.start();
+ nextTransition.mLogger.mStartWCT = wct;
+ applyTransaction(wct, -1 /*syncId*/, nextTransition, caller);
+ if (needsSetReady) {
+ nextTransition.setAllReady();
+ }
+ });
+ return nextTransition.getToken();
}
+ // The transition already started collecting before sending a request to shell,
+ // so just start here.
if (!transition.isCollecting() && !transition.isForcePlaying()) {
Slog.e(TAG, "Trying to start a transition that isn't collecting. This probably"
+ " means Shell took too long to respond to a request. WM State may be"
@@ -325,9 +315,8 @@
transition.start();
transition.mLogger.mStartWCT = wct;
applyTransaction(wct, -1 /*syncId*/, transition, caller);
- if (needsSetReady) {
- transition.setAllReady();
- }
+ // Since the transition is already provided, it means WMCore is determining the
+ // "readiness lifecycle" outside the provided transaction, so don't set ready here.
return transition.getToken();
}
} finally {
@@ -432,23 +421,8 @@
return;
}
- if (!mService.mWindowManager.mSyncEngine.hasActiveSync()) {
- // Sync is for either transition or applySyncTransaction(). We don't support
- // multiple sync at the same time because it may cause conflict.
- // Create a new transition when there is no active sync to collect the changes.
- final Transition transition = mTransitionController.createTransition(type);
- if (applyTransaction(wct, -1 /* syncId */, transition, caller)
- == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
- transition.abort();
- return;
- }
- mTransitionController.requestStartTransition(transition, null /* startTask */,
- null /* remoteTransition */, null /* displayChange */);
- transition.setAllReady();
- return;
- }
-
- if (!shouldApplyIndependently) {
+ if (mService.mWindowManager.mSyncEngine.hasActiveSync()
+ && !shouldApplyIndependently) {
// Although there is an active sync, we want to apply the transaction now.
// TODO(b/232042367) Redesign the organizer update on activity callback so that we
// we will know about the transition explicitly.
@@ -467,25 +441,23 @@
return;
}
- // It is ok to queue the WCT until the sync engine is free.
- final Transition nextTransition = new Transition(type, 0 /* flags */,
+ final Transition transition = new Transition(type, 0 /* flags */,
mTransitionController, mService.mWindowManager.mSyncEngine);
- ProtoLog.v(ProtoLogGroup.WM_DEBUG_WINDOW_TRANSITIONS,
- "Creating Pending Transition for TaskFragment: %s", nextTransition);
- mTransitionController.queueCollecting(nextTransition,
- () -> {
- if (mTaskFragmentOrganizerController.isValidTransaction(wct)
- && (applyTransaction(wct, -1 /* syncId */, nextTransition, caller)
- != TRANSACT_EFFECTS_NONE
- || !nextTransition.mParticipants.isEmpty())) {
- mTransitionController.requestStartTransition(nextTransition,
- null /* startTask */, null /* remoteTransition */,
- null /* displayChange */);
- nextTransition.setAllReady();
- return;
- }
- nextTransition.abort();
- });
+ TransitionController.OnStartCollect doApply = (deferred) -> {
+ if (deferred && !mTaskFragmentOrganizerController.isValidTransaction(wct)) {
+ transition.abort();
+ return;
+ }
+ if (applyTransaction(wct, -1 /* syncId */, transition, caller)
+ == TRANSACT_EFFECTS_NONE && transition.mParticipants.isEmpty()) {
+ transition.abort();
+ return;
+ }
+ mTransitionController.requestStartTransition(transition, null /* startTask */,
+ null /* remoteTransition */, null /* displayChange */);
+ transition.setAllReady();
+ };
+ mTransitionController.startCollectOrQueue(transition, doApply);
} finally {
Binder.restoreCallingIdentity(ident);
}
diff --git a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
index a04143a..5a71808 100644
--- a/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/CreateRequestSession.java
@@ -89,14 +89,16 @@
protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
+ cancelExistingPendingIntent();
try {
- mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+ mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newCreateRequestInfo(
mRequestId, mClientRequest,
mClientAppInfo.getPackageName(),
PermissionUtils.hasPermission(mContext, mClientAppInfo.getPackageName(),
Manifest.permission.CREDENTIAL_MANAGER_SET_ALLOWED_PROVIDERS)),
- providerDataList));
+ providerDataList);
+ mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
index 8750906..0dee7a4 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerUi.java
@@ -33,7 +33,6 @@
import android.os.Looper;
import android.os.ResultReceiver;
import android.service.credentials.CredentialProviderInfoFactory;
-import android.util.Log;
import android.util.Slog;
import java.util.ArrayList;
@@ -124,7 +123,6 @@
public CredentialManagerUi(Context context, int userId,
CredentialManagerUiCallback callbacks, Set<ComponentName> enabledProviders) {
- Log.i(TAG, "In CredentialManagerUi constructor");
mContext = context;
mUserId = userId;
mCallbacks = callbacks;
@@ -151,8 +149,6 @@
*/
public PendingIntent createPendingIntent(
RequestInfo requestInfo, ArrayList<ProviderData> providerDataList) {
- Log.i(TAG, "In createPendingIntent");
-
List<CredentialProviderInfo> allProviders =
CredentialProviderInfoFactory.getCredentialProviderServices(
mContext,
diff --git a/services/credentials/java/com/android/server/credentials/GetRequestSession.java b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
index 208a4be..3dba4a9 100644
--- a/services/credentials/java/com/android/server/credentials/GetRequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/GetRequestSession.java
@@ -89,11 +89,13 @@
protected void launchUiWithProviderData(ArrayList<ProviderData> providerDataList) {
mRequestSessionMetric.collectUiCallStartTime(System.nanoTime());
mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.USER_INTERACTION);
+ cancelExistingPendingIntent();
try {
- mClientCallback.onPendingIntent(mCredentialManagerUi.createPendingIntent(
+ mPendingIntent = mCredentialManagerUi.createPendingIntent(
RequestInfo.newGetRequestInfo(
mRequestId, mClientRequest, mClientAppInfo.getPackageName()),
- providerDataList));
+ providerDataList);
+ mClientCallback.onPendingIntent(mPendingIntent);
} catch (RemoteException e) {
mRequestSessionMetric.collectUiReturnedFinalPhase(/*uiReturned=*/ false);
mCredentialManagerUi.setStatus(CredentialManagerUi.UiStatus.TERMINATED);
diff --git a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
index c1e9bc6..0ad73c9 100644
--- a/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
+++ b/services/credentials/java/com/android/server/credentials/RemoteCredentialService.java
@@ -40,7 +40,6 @@
import android.service.credentials.IClearCredentialStateCallback;
import android.service.credentials.ICredentialProviderService;
import android.text.format.DateUtils;
-import android.util.Log;
import android.util.Slog;
import com.android.internal.infra.ServiceConnector;
@@ -122,7 +121,6 @@
*/
public void onBeginGetCredential(@NonNull BeginGetCredentialRequest request,
ProviderCallbacks<BeginGetCredentialResponse> callback) {
- Log.i(TAG, "In onGetCredentials in RemoteCredentialService");
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
AtomicReference<CompletableFuture<BeginGetCredentialResponse>> futureRef =
new AtomicReference<>();
@@ -142,7 +140,6 @@
@Override
public void onFailure(String errorType, CharSequence message) {
- Log.i(TAG, "In onFailure in RemoteCredentialService");
String errorMsg = message == null ? "" : String.valueOf(
message);
getCredentials.completeExceptionally(
@@ -182,7 +179,6 @@
*/
public void onBeginCreateCredential(@NonNull BeginCreateCredentialRequest request,
ProviderCallbacks<BeginCreateCredentialResponse> callback) {
- Log.i(TAG, "In onCreateCredential in RemoteCredentialService");
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
AtomicReference<CompletableFuture<BeginCreateCredentialResponse>> futureRef =
new AtomicReference<>();
@@ -197,14 +193,11 @@
request, new IBeginCreateCredentialCallback.Stub() {
@Override
public void onSuccess(BeginCreateCredentialResponse response) {
- Log.i(TAG, "In onSuccess onBeginCreateCredential "
- + "in RemoteCredentialService");
createCredentialFuture.complete(response);
}
@Override
public void onFailure(String errorType, CharSequence message) {
- Log.i(TAG, "In onFailure in RemoteCredentialService");
String errorMsg = message == null ? "" : String.valueOf(
message);
createCredentialFuture.completeExceptionally(
@@ -244,7 +237,6 @@
*/
public void onClearCredentialState(@NonNull ClearCredentialStateRequest request,
ProviderCallbacks<Void> callback) {
- Log.i(TAG, "In onClearCredentialState in RemoteCredentialService");
AtomicReference<ICancellationSignal> cancellationSink = new AtomicReference<>();
AtomicReference<CompletableFuture<Void>> futureRef = new AtomicReference<>();
@@ -258,14 +250,11 @@
request, new IClearCredentialStateCallback.Stub() {
@Override
public void onSuccess() {
- Log.i(TAG, "In onSuccess onClearCredentialState "
- + "in RemoteCredentialService");
clearCredentialFuture.complete(null);
}
@Override
public void onFailure(String errorType, CharSequence message) {
- Log.i(TAG, "In onFailure in RemoteCredentialService");
String errorMsg = message == null ? "" :
String.valueOf(message);
clearCredentialFuture.completeExceptionally(
@@ -300,35 +289,29 @@
AtomicReference<ICancellationSignal> cancellationSink,
ProviderCallbacks<T> callback) {
if (error == null) {
- Log.i(TAG, "In RemoteCredentialService execute error is null");
callback.onProviderResponseSuccess(result);
} else {
if (error instanceof TimeoutException) {
- Log.i(TAG, "In RemoteCredentialService execute error is timeout");
+ Slog.d(TAG, "Remote provider response timed tuo for: " + mComponentName);
dispatchCancellationSignal(cancellationSink.get());
callback.onProviderResponseFailure(
CredentialProviderErrors.ERROR_TIMEOUT,
null);
} else if (error instanceof CancellationException) {
- Log.i(TAG, "In RemoteCredentialService execute error is cancellation");
+ Slog.d(TAG, "Cancellation exception for remote provider: " + mComponentName);
dispatchCancellationSignal(cancellationSink.get());
callback.onProviderResponseFailure(
CredentialProviderErrors.ERROR_TASK_CANCELED,
null);
} else if (error instanceof GetCredentialException) {
- Log.i(TAG, "In RemoteCredentialService execute error is provider get"
- + "error");
callback.onProviderResponseFailure(
CredentialProviderErrors.ERROR_PROVIDER_FAILURE,
(GetCredentialException) error);
} else if (error instanceof CreateCredentialException) {
- Log.i(TAG, "In RemoteCredentialService execute error is provider create "
- + "error");
callback.onProviderResponseFailure(
CredentialProviderErrors.ERROR_PROVIDER_FAILURE,
(CreateCredentialException) error);
} else {
- Log.i(TAG, "In RemoteCredentialService execute error is unknown");
callback.onProviderResponseFailure(
CredentialProviderErrors.ERROR_UNKNOWN,
(Exception) error);
diff --git a/services/credentials/java/com/android/server/credentials/RequestSession.java b/services/credentials/java/com/android/server/credentials/RequestSession.java
index 8fd0269..15a30e4 100644
--- a/services/credentials/java/com/android/server/credentials/RequestSession.java
+++ b/services/credentials/java/com/android/server/credentials/RequestSession.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -90,6 +91,8 @@
private final Set<ComponentName> mEnabledProviders;
+ protected PendingIntent mPendingIntent;
+
@NonNull
protected RequestSessionStatus mRequestSessionStatus =
RequestSessionStatus.IN_PROGRESS;
@@ -202,11 +205,23 @@
if (propagateCancellation) {
mProviders.values().forEach(ProviderSession::cancelProviderRemoteSession);
}
+ cancelExistingPendingIntent();
mRequestSessionStatus = RequestSessionStatus.COMPLETE;
mProviders.clear();
clearRequestSessionLocked();
}
+ void cancelExistingPendingIntent() {
+ if (mPendingIntent != null) {
+ try {
+ mPendingIntent.cancel();
+ mPendingIntent = null;
+ } catch (Exception e) {
+ Slog.e(TAG, "Unable to cancel existing pending intent", e);
+ }
+ }
+ }
+
private void clearRequestSessionLocked() {
synchronized (mLock) {
mSessionCallback.onFinishRequestSession(mUserId, mRequestId);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
index 926c7e4..3d5686d 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyEngine.java
@@ -910,7 +910,7 @@
mDeviceAdminServiceController.stopServicesForUser(
userId, actionForLog);
} else {
- for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
+ for (EnforcingAdmin admin : getEnforcingAdminsOnUser(userId)) {
// DPCs are handled separately in DPMS, no need to reestablish the connection here.
if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
continue;
@@ -921,6 +921,51 @@
}
}
+ /**
+ * Handles internal state related to a user getting started.
+ */
+ void handleStartUser(int userId) {
+ updateDeviceAdminsServicesForUser(
+ userId, /* enable= */ true, /* actionForLog= */ "start-user");
+ }
+
+ /**
+ * Handles internal state related to a user getting started.
+ */
+ void handleUnlockUser(int userId) {
+ updateDeviceAdminsServicesForUser(
+ userId, /* enable= */ true, /* actionForLog= */ "unlock-user");
+ }
+
+ /**
+ * Handles internal state related to a user getting stopped.
+ */
+ void handleStopUser(int userId) {
+ updateDeviceAdminsServicesForUser(
+ userId, /* enable= */ false, /* actionForLog= */ "stop-user");
+ }
+
+ /**
+ * Handles internal state related to packages getting updated.
+ */
+ void handlePackageChanged(@Nullable String updatedPackage, int userId) {
+ if (updatedPackage == null) {
+ return;
+ }
+ updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
+ }
+
+ /**
+ * Handles internal state related to a user getting removed.
+ */
+ void handleUserRemoved(int userId) {
+ removeLocalPoliciesForUser(userId);
+ removePoliciesForAdminsOnUser(userId);
+ }
+
+ /**
+ * Handles internal state related to a user getting created.
+ */
void handleUserCreated(UserInfo user) {
enforcePoliciesOnInheritableProfilesIfApplicable(user);
}
@@ -963,40 +1008,6 @@
}
/**
- * Handles internal state related to a user getting started.
- */
- void handleStartUser(int userId) {
- updateDeviceAdminsServicesForUser(
- userId, /* enable= */ true, /* actionForLog= */ "start-user");
- }
-
- /**
- * Handles internal state related to a user getting started.
- */
- void handleUnlockUser(int userId) {
- updateDeviceAdminsServicesForUser(
- userId, /* enable= */ true, /* actionForLog= */ "unlock-user");
- }
-
- /**
- * Handles internal state related to a user getting stopped.
- */
- void handleStopUser(int userId) {
- updateDeviceAdminsServicesForUser(
- userId, /* enable= */ false, /* actionForLog= */ "stop-user");
- }
-
- /**
- * Handles internal state related to packages getting updated.
- */
- void handlePackageChanged(@Nullable String updatedPackage, int userId) {
- if (updatedPackage == null) {
- return;
- }
- updateDeviceAdminServiceOnPackageChanged(updatedPackage, userId);
- }
-
- /**
* Returns all current enforced policies set on the device, and the individual values set by
* each admin. Global policies are returned under {@link UserHandle#ALL}.
*/
@@ -1024,6 +1035,63 @@
return new DevicePolicyState(policies);
}
+
+ /**
+ * Removes all local and global policies set by that admin.
+ */
+ void removePoliciesForAdmin(EnforcingAdmin admin) {
+ Set<PolicyKey> globalPolicies = new HashSet<>(mGlobalPolicies.keySet());
+ for (PolicyKey policy : globalPolicies) {
+ PolicyState<?> policyState = mGlobalPolicies.get(policy);
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ removeGlobalPolicy(policyState.getPolicyDefinition(), admin);
+ }
+ }
+
+ for (int i = 0; i < mLocalPolicies.size(); i++) {
+ Set<PolicyKey> localPolicies = new HashSet<>(
+ mLocalPolicies.get(mLocalPolicies.keyAt(i)).keySet());
+ for (PolicyKey policy : localPolicies) {
+ PolicyState<?> policyState = mLocalPolicies.get(
+ mLocalPolicies.keyAt(i)).get(policy);
+ if (policyState.getPoliciesSetByAdmins().containsKey(admin)) {
+ removeLocalPolicy(
+ policyState.getPolicyDefinition(), admin, mLocalPolicies.keyAt(i));
+ }
+ }
+ }
+ }
+
+ /**
+ * Removes all local policies for the provided {@code userId}.
+ */
+ private void removeLocalPoliciesForUser(int userId) {
+ Set<PolicyKey> localPolicies = new HashSet<>(mLocalPolicies.get(userId).keySet());
+ for (PolicyKey policy : localPolicies) {
+ PolicyState<?> policyState = mLocalPolicies.get(userId).get(policy);
+ Set<EnforcingAdmin> admins = new HashSet<>(
+ policyState.getPoliciesSetByAdmins().keySet());
+ for (EnforcingAdmin admin : admins) {
+ removeLocalPolicy(
+ policyState.getPolicyDefinition(), admin, userId);
+ }
+ }
+
+ mLocalPolicies.remove(userId);
+ }
+
+ /**
+ * Removes all local and global policies for admins installed in the provided
+ * {@code userId}.
+ */
+ private void removePoliciesForAdminsOnUser(int userId) {
+ Set<EnforcingAdmin> admins = getEnforcingAdminsOnUser(userId);
+
+ for (EnforcingAdmin admin : admins) {
+ removePoliciesForAdmin(admin);
+ }
+ }
+
/**
* Reestablishes the service that handles
* {@link DevicePolicyManager#ACTION_DEVICE_ADMIN_SERVICE} in the enforcing admin if the package
@@ -1031,7 +1099,7 @@
*/
private void updateDeviceAdminServiceOnPackageChanged(
@NonNull String updatedPackage, int userId) {
- for (EnforcingAdmin admin : getEnforcingAdminsForUser(userId)) {
+ for (EnforcingAdmin admin : getEnforcingAdminsOnUser(userId)) {
// DPCs are handled separately in DPMS, no need to reestablish the connection here.
if (admin.hasAuthority(EnforcingAdmin.DPC_AUTHORITY)) {
continue;
@@ -1120,7 +1188,7 @@
}
@NonNull
- private Set<EnforcingAdmin> getEnforcingAdminsForUser(int userId) {
+ private Set<EnforcingAdmin> getEnforcingAdminsOnUser(int userId) {
return mEnforcingAdmins.contains(userId)
? mEnforcingAdmins.get(userId) : Collections.emptySet();
}
@@ -1159,12 +1227,6 @@
}
}
- // TODO: we need to listen for user removal and package removal and update out internal policy
- // map and enforcing admins for this is be accurate.
- boolean hasActivePolicies() {
- return mEnforcingAdmins.size() > 0;
- }
-
private <V> boolean checkFor2gFailure(@NonNull PolicyDefinition<V> policyDefinition,
@NonNull EnforcingAdmin enforcingAdmin) {
if (!policyDefinition.getPolicyKey().getIdentifier().equals(
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 29f9a30..c2766c2 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";
@@ -1178,6 +1178,9 @@
// Resume logging if all remaining users are affiliated.
maybeResumeDeviceWideLoggingLocked();
}
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.handleUserRemoved(userHandle);
+ }
}
} else if (Intent.ACTION_USER_STARTED.equals(action)) {
sendDeviceOwnerUserCommand(DeviceAdminReceiver.ACTION_USER_STARTED, userHandle);
@@ -3664,6 +3667,9 @@
}
for (Integer userId : deletedUsers) {
removeUserData(userId);
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.handleUserRemoved(userId);
+ }
}
}
@@ -4153,6 +4159,11 @@
mInjector.binderWithCleanCallingIdentity(() ->
removeActiveAdminLocked(adminReceiver, userHandle));
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ adminReceiver, userHandle, admin));
+ }
}
}
@@ -9992,6 +10003,12 @@
toggleBackupServiceActive(UserHandle.USER_SYSTEM, true);
pushUserControlDisabledPackagesLocked(userId);
setGlobalSettingDeviceOwnerType(DEVICE_OWNER_TYPE_DEFAULT);
+
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(), userId, admin));
+ }
}
private void clearApplicationRestrictions(int userId) {
@@ -10139,6 +10156,12 @@
toggleBackupServiceActive(userId, true);
applyProfileRestrictionsIfDeviceOwnerLocked();
setNetworkLoggingActiveInternal(false);
+
+ if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) {
+ mDevicePolicyEngine.removePoliciesForAdmin(
+ EnforcingAdmin.createEnterpriseEnforcingAdmin(
+ admin.info.getComponent(), userId, admin));
+ }
}
@Override
@@ -13264,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");
}
}
}
@@ -13292,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 =
@@ -13317,7 +13342,6 @@
userId);
}
}
-
private void setGlobalUserRestrictionInternal(
EnforcingAdmin admin, String key, boolean enabled) {
PolicyDefinition<Boolean> policyDefinition =
@@ -16088,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) {
@@ -16119,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) {
@@ -22549,53 +22561,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,
@@ -22616,13 +22628,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/backup/BackupAndRestoreFeatureFlagsTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
index 201da35..9f685b4 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/BackupAndRestoreFeatureFlagsTest.java
@@ -104,4 +104,24 @@
assertThat(BackupAndRestoreFeatureFlags.getFullBackupUtilsRouteBufferSizeBytes())
.isEqualTo(5678);
}
+
+ @Test
+ public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_notSet_returnsDefault() {
+ assertThat(
+ BackupAndRestoreFeatureFlags
+ .getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
+ .isEqualTo(true);
+ }
+
+ @Test
+ public void getUnifiedRestoreContinueAfterTransportFailureInKvRestore_set_returnsSetValue() {
+ DeviceConfig.setProperty(/*namespace=*/ "backup_and_restore",
+ /*name=*/ "unified_restore_continue_after_transport_failure_in_kv_restore",
+ /*value=*/ "false", /*makeDefault=*/ false);
+
+ assertThat(
+ BackupAndRestoreFeatureFlags
+ .getUnifiedRestoreContinueAfterTransportFailureInKvRestore())
+ .isEqualTo(false);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
index 017c939..c84797f 100644
--- a/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/backup/restore/PerformUnifiedRestoreTaskTest.java
@@ -25,20 +25,33 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
+import android.app.backup.BackupDataInput;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.BackupTransport;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.os.Message;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.provider.DeviceConfig;
+
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
-import android.app.backup.BackupDataInput;
-import android.app.backup.BackupDataOutput;
-import android.platform.test.annotations.Presubmit;
-
+import com.android.modules.utils.testing.TestableDeviceConfig;
import com.android.server.backup.UserBackupManagerService;
+import com.android.server.backup.internal.BackupHandler;
+import com.android.server.backup.transport.BackupTransportClient;
+import com.android.server.backup.transport.TransportConnection;
+import com.android.server.backup.transport.TransportNotAvailableException;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
+import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
@@ -62,9 +75,14 @@
private static final String SYSTEM_PACKAGE_NAME = "android";
private static final String NON_SYSTEM_PACKAGE_NAME = "package";
- @Mock private BackupDataInput mBackupDataInput;
- @Mock private BackupDataOutput mBackupDataOutput;
- @Mock private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private BackupDataInput mBackupDataInput;
+ @Mock
+ private BackupDataOutput mBackupDataOutput;
+ @Mock
+ private UserBackupManagerService mBackupManagerService;
+ @Mock
+ private TransportConnection mTransportConnection;
private Set<String> mExcludedkeys = new HashSet<>();
private Map<String, String> mBackupData = new HashMap<>();
@@ -74,12 +92,20 @@
private Set<String> mBackupDataDump;
private PerformUnifiedRestoreTask mRestoreTask;
+ @Rule
+ public TestableDeviceConfig.TestableDeviceConfigRule
+ mDeviceConfigRule = new TestableDeviceConfig.TestableDeviceConfigRule();
+
+ private Context mContext;
+
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
populateTestData();
+ mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
+
mBackupDataSource = new ArrayDeque<>(mBackupData.keySet());
when(mBackupDataInput.readNextHeader()).then(new Answer<Boolean>() {
@Override
@@ -106,7 +132,7 @@
}
});
- mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService);
+ mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection);
}
private void populateTestData() {
@@ -179,4 +205,64 @@
assertTrue(mRestoreTask.shouldStageBackupData(SYSTEM_PACKAGE_NAME));
}
+
+ @Test
+ public void testFailedKeyValueRestore_continueAfterFeatureEnabled_nextStateIsRunningQueue()
+ throws TransportNotAvailableException, RemoteException {
+ DeviceConfig.setProperty(
+ "backup_and_restore",
+ "unified_restore_continue_after_transport_failure_in_kv_restore",
+ "true",
+ false);
+
+ setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
+
+ mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
+ mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
+
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "test.package.name";
+ mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
+ assertTrue(
+ mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
+ == UnifiedRestoreState.RUNNING_QUEUE);
+ }
+
+ @Test
+ public void testFailedKeyValueRestore_continueAfterFeatureDisabled_nextStateIsFinal()
+ throws RemoteException, TransportNotAvailableException {
+ DeviceConfig.setProperty(
+ "backup_and_restore",
+ "unified_restore_continue_after_transport_failure_in_kv_restore",
+ "false",
+ false);
+
+ setupForRestoreKeyValueState(BackupTransport.TRANSPORT_ERROR);
+
+ mRestoreTask.setCurrentUnifiedRestoreStateForTesting(UnifiedRestoreState.RESTORE_KEYVALUE);
+ mRestoreTask.setStateDirForTesting(mContext.getCacheDir());
+
+ PackageInfo testPackageInfo = new PackageInfo();
+ testPackageInfo.packageName = "test.package.name";
+ mRestoreTask.initiateOneRestoreForTesting(testPackageInfo, 0L);
+ assertTrue(
+ mRestoreTask.getCurrentUnifiedRestoreStateForTesting()
+ == UnifiedRestoreState.FINAL);
+ }
+
+ private void setupForRestoreKeyValueState(int transportStatus)
+ throws RemoteException, TransportNotAvailableException {
+ // Mock BackupHandler to do nothing when executeNextState() is called
+ BackupHandler backupHandler = Mockito.mock(BackupHandler.class);
+ when(backupHandler.obtainMessage(anyInt(), any())).thenReturn(new Message());
+ when(backupHandler.sendMessage(any())).thenReturn(true);
+
+ // Return cache directory for any bookkeeping or maintaining persistent state.
+ when(mBackupManagerService.getDataDir()).thenReturn(mContext.getCacheDir());
+ when(mBackupManagerService.getBackupHandler()).thenReturn(backupHandler);
+
+ BackupTransportClient transport = Mockito.mock(BackupTransportClient.class);
+ when(transport.getRestoreData(any())).thenReturn(transportStatus);
+ when(mTransportConnection.connectOrThrow(any())).thenReturn(transport);
+ }
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
index 45fefe4..ca857f1 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerController2Test.java
@@ -38,9 +38,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -51,18 +49,18 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
+import android.testing.TestableContext;
import android.util.FloatProperty;
import android.view.Display;
import android.view.DisplayInfo;
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -74,12 +72,12 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -97,11 +95,9 @@
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
- private MockitoSession mSession;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
- private Context mContextSpy;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
@@ -118,40 +114,44 @@
@Mock
private PowerManager mPowerManagerMock;
@Mock
- private Resources mResourcesMock;
- @Mock
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(BatteryStatsService.class)
+ .build();
+
@Before
public void setUp() throws Exception {
- mSession = ExtendedMockito.mockitoSession()
- .initMocks(this)
- .strictness(Strictness.LENIENT)
- .spyStatic(SystemProperties.class)
- .spyStatic(LocalServices.class)
- .spyStatic(BatteryStatsService.class)
- .spyStatic(Settings.System.class)
- .startMocking();
- mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mHandler = new Handler(mTestLooper.getLooper());
- addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
- when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
- when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+ // Put the system into manual brightness by default, just to minimize unexpected events and
+ // have a consistent starting state
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+ addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
+ mCdsiMock);
+
+ mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
SystemProperties.set(anyString(), any()));
- doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
- mCdsiMock).when(() -> LocalServices.getService(
- ColorDisplayService.ColorDisplayServiceInternal.class));
doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
- doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
- Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
setUpSensors();
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -159,8 +159,8 @@
@After
public void tearDown() {
- mSession.finishMocking();
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
@Test
@@ -421,11 +421,9 @@
@Test
public void testDisplayBrightnessFollowers_AutomaticBrightness() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
final float brightness = 0.4f;
final float nits = 300;
final float ambientLux = 3000;
@@ -436,7 +434,7 @@
when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
when(mHolder.automaticBrightnessController.getAmbientLux()).thenReturn(ambientLux);
when(mHolder.displayPowerState.getScreenState()).thenReturn(Display.STATE_ON);
- DisplayPowerController followerDpc = mock(DisplayPowerController.class);
+ DisplayPowerController2 followerDpc = mock(DisplayPowerController2.class);
mHolder.dpc.addDisplayBrightnessFollower(followerDpc);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -542,11 +540,9 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -577,17 +573,14 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- when(mResourcesMock.getBoolean(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
- .thenReturn(true);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -615,12 +608,7 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
-
+ // Tests are set up with manual brightness by default, so no need to set it here.
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -632,11 +620,9 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -690,9 +676,9 @@
public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
float brightness = 0.3f;
float nits = 500;
- when(mResourcesMock.getBoolean(
- com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
- .thenReturn(true);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
+ true);
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
@@ -753,7 +739,7 @@
any(HysteresisLevels.class),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
- eq(mContextSpy),
+ eq(mContext),
any(HighBrightnessModeController.class),
any(BrightnessThrottler.class),
isNull(),
@@ -862,7 +848,7 @@
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
final DisplayPowerController2 dpc = new DisplayPowerController2(
- mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
+ mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
diff --git a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
index d9133a4..0b97c5c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/display/DisplayPowerControllerTest.java
@@ -38,9 +38,7 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.when;
-import android.content.ContentResolver;
import android.content.Context;
-import android.content.ContextWrapper;
import android.content.res.Resources;
import android.hardware.Sensor;
import android.hardware.SensorEventListener;
@@ -51,18 +49,18 @@
import android.os.Looper;
import android.os.PowerManager;
import android.os.SystemProperties;
-import android.os.UserHandle;
import android.os.test.TestLooper;
import android.provider.Settings;
+import android.testing.TestableContext;
import android.util.FloatProperty;
import android.view.Display;
import android.view.DisplayInfo;
-import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
-import androidx.test.runner.AndroidJUnit4;
+import androidx.test.platform.app.InstrumentationRegistry;
-import com.android.dx.mockito.inline.extended.ExtendedMockito;
+import com.android.server.ExtendedMockitoRule;
import com.android.server.LocalServices;
import com.android.server.am.BatteryStatsService;
import com.android.server.display.RampAnimator.DualRampAnimator;
@@ -75,12 +73,12 @@
import org.junit.After;
import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
-import org.mockito.MockitoSession;
import org.mockito.quality.Strictness;
import org.mockito.stubbing.Answer;
@@ -98,11 +96,9 @@
private static final String SECOND_FOLLOWER_UNIQUE_DISPLAY_ID = "unique_id_789";
private static final float PROX_SENSOR_MAX_RANGE = 5;
- private MockitoSession mSession;
private OffsettableClock mClock;
private TestLooper mTestLooper;
private Handler mHandler;
- private Context mContextSpy;
private DisplayPowerControllerHolder mHolder;
private Sensor mProxSensor;
@@ -119,41 +115,45 @@
@Mock
private PowerManager mPowerManagerMock;
@Mock
- private Resources mResourcesMock;
- @Mock
private ColorDisplayService.ColorDisplayServiceInternal mCdsiMock;
@Captor
private ArgumentCaptor<SensorEventListener> mSensorEventListenerCaptor;
+ @Rule
+ public final TestableContext mContext = new TestableContext(
+ InstrumentationRegistry.getInstrumentation().getContext());
+
+ @Rule
+ public final ExtendedMockitoRule mExtendedMockitoRule =
+ new ExtendedMockitoRule.Builder(this)
+ .setStrictness(Strictness.LENIENT)
+ .spyStatic(SystemProperties.class)
+ .spyStatic(BatteryStatsService.class)
+ .build();
+
@Before
public void setUp() throws Exception {
- mSession = ExtendedMockito.mockitoSession()
- .initMocks(this)
- .strictness(Strictness.LENIENT)
- .spyStatic(SystemProperties.class)
- .spyStatic(LocalServices.class)
- .spyStatic(BatteryStatsService.class)
- .spyStatic(Settings.System.class)
- .startMocking();
- mContextSpy = spy(new ContextWrapper(ApplicationProvider.getApplicationContext()));
mClock = new OffsettableClock.Stopped();
mTestLooper = new TestLooper(mClock::now);
mHandler = new Handler(mTestLooper.getLooper());
- addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+ // Put the system into manual brightness by default, just to minimize unexpected events and
+ // have a consistent starting state
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL);
- when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock);
- when(mContextSpy.getResources()).thenReturn(mResourcesMock);
+
+ addLocalServiceMock(WindowManagerPolicy.class, mWindowManagerPolicyMock);
+ addLocalServiceMock(ColorDisplayService.ColorDisplayServiceInternal.class,
+ mCdsiMock);
+
+ mContext.addMockSystemService(PowerManager.class, mPowerManagerMock);
doAnswer((Answer<Void>) invocationOnMock -> null).when(() ->
SystemProperties.set(anyString(), any()));
- doAnswer((Answer<ColorDisplayService.ColorDisplayServiceInternal>) invocationOnMock ->
- mCdsiMock).when(() -> LocalServices.getService(
- ColorDisplayService.ColorDisplayServiceInternal.class));
doAnswer((Answer<Void>) invocationOnMock -> null).when(BatteryStatsService::getService);
- doAnswer((Answer<Boolean>) invocationOnMock -> true).when(() ->
- Settings.System.putFloatForUser(any(), any(), anyFloat(), anyInt()));
setUpSensors();
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
@@ -161,8 +161,8 @@
@After
public void tearDown() {
- mSession.finishMocking();
LocalServices.removeServiceForTest(WindowManagerPolicy.class);
+ LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class);
}
@Test
@@ -425,11 +425,9 @@
@Test
public void testDisplayBrightnessFollowers_AutomaticBrightness() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
final float brightness = 0.4f;
final float nits = 300;
final float ambientLux = 3000;
@@ -547,11 +545,9 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsOff() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
@@ -582,17 +578,14 @@
@Test
public void testSetScreenOffBrightnessSensorEnabled_DisplayIsInDoze() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_DOZE;
- when(mResourcesMock.getBoolean(
- com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing))
- .thenReturn(true);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_allowAutoBrightnessWhileDozing, true);
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
advanceTime(1); // Run updatePowerState
@@ -620,12 +613,7 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_AutoBrightnessIsDisabled() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_MANUAL)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
-
+ // Tests are set up with manual brightness by default, so no need to set it here.
DisplayPowerRequest dpr = new DisplayPowerRequest();
dpr.policy = DisplayPowerRequest.POLICY_OFF;
mHolder.dpc.requestPowerState(dpr, /* waitForNegativeProximity= */ false);
@@ -637,11 +625,10 @@
@Test
public void testSetScreenOffBrightnessSensorDisabled_DisplayIsDisabled() {
- doAnswer((Answer<Integer>) invocationOnMock ->
- Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC)
- .when(() -> Settings.System.getIntForUser(any(ContentResolver.class),
- eq(Settings.System.SCREEN_BRIGHTNESS_MODE), anyInt(),
- eq(UserHandle.USER_CURRENT)));
+ Settings.System.putInt(mContext.getContentResolver(),
+ Settings.System.SCREEN_BRIGHTNESS_MODE,
+ Settings.System.SCREEN_BRIGHTNESS_MODE_AUTOMATIC);
+
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID, /* isEnabled= */ false);
DisplayPowerRequest dpr = new DisplayPowerRequest();
@@ -695,9 +682,10 @@
public void testBrightnessNitsPersistWhenDisplayDeviceChanges() {
float brightness = 0.3f;
float nits = 500;
- when(mResourcesMock.getBoolean(
- com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay))
- .thenReturn(true);
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_persistBrightnessNitsForDefaultDisplay,
+ true);
+
mHolder = createDisplayPowerController(DISPLAY_ID, UNIQUE_ID);
when(mHolder.automaticBrightnessController.convertToNits(brightness)).thenReturn(nits);
@@ -758,7 +746,7 @@
any(HysteresisLevels.class),
any(HysteresisLevels.class),
any(HysteresisLevels.class),
- eq(mContextSpy),
+ eq(mContext),
any(HighBrightnessModeController.class),
any(BrightnessThrottler.class),
isNull(),
@@ -866,7 +854,7 @@
setUpDisplay(displayId, uniqueId, display, device, config, isEnabled);
final DisplayPowerController dpc = new DisplayPowerController(
- mContextSpy, injector, mDisplayPowerCallbacksMock, mHandler,
+ mContext, injector, mDisplayPowerCallbacksMock, mHandler,
mSensorManagerMock, mDisplayBlankerMock, display,
mBrightnessTrackerMock, brightnessSetting, () -> {},
hbmMetadata, /* bootCompleted= */ false);
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/ShutdownCheckPointsTest.java b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
index 2230ddd..2bde51b 100644
--- a/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/ShutdownCheckPointsTest.java
@@ -103,21 +103,45 @@
}
@Test
- public void testCallerProcessBinderEntry() throws RemoteException {
+ public void testCallerProcessBinderEntries() throws RemoteException {
List<ActivityManager.RunningAppProcessInfo> runningAppProcessInfos = new ArrayList<>();
runningAppProcessInfos.add(
new ActivityManager.RunningAppProcessInfo("process_name", 1, new String[0]));
when(mActivityManager.getRunningAppProcesses()).thenReturn(runningAppProcessInfos);
mTestInjector.setCurrentTime(1000);
+ // Matching pid in getRunningAppProcesses
+ mInstance.recordCheckPointInternal(1, "reason1");
+ // Mising pid in getRunningAppProcesses
+ mInstance.recordCheckPointInternal(2, "reason2");
+
+ assertEquals(
+ "Shutdown request from BINDER for reason reason1 "
+ + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testCallerProcessBinderEntries\n"
+ + "From process process_name (pid=1)\n\n"
+ + "Shutdown request from BINDER for reason reason2 "
+ + "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ + "com.android.server.power.ShutdownCheckPointsTest"
+ + ".testCallerProcessBinderEntries\n"
+ + "From process ? (pid=2)\n\n",
+ dumpToString());
+ }
+
+ @Test
+ public void testNullCallerProcessBinderEntries() throws RemoteException {
+ when(mActivityManager.getRunningAppProcesses()).thenReturn(null);
+
+ mTestInjector.setCurrentTime(1000);
mInstance.recordCheckPointInternal(1, "reason1");
assertEquals(
"Shutdown request from BINDER for reason reason1 "
+ "at 1970-01-01 00:00:01.000 UTC (epoch=1000)\n"
+ "com.android.server.power.ShutdownCheckPointsTest"
- + ".testCallerProcessBinderEntry\n"
- + "From process process_name (pid=1)\n\n",
+ + ".testNullCallerProcessBinderEntries\n"
+ + "From process ? (pid=1)\n\n",
dumpToString());
}
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/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java
new file mode 100644
index 0000000..bc94dac
--- /dev/null
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandlerTest.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.anyInt;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.Phrase;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.RecognitionMode;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.SoundModelType;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.InOrder;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(JUnit4.class)
+public final class SoundTriggerDuplicateModelHandlerTest {
+ // Component under test
+ private SoundTriggerDuplicateModelHandler mComponent;
+
+ private static final String DUPLICATE_UUID = "abcddead-beef-0123-3210-0123456789ab";
+ private static final String DIFFERENT_UUID = "0000dead-beef-0123-3210-0123456789ab";
+
+ @Mock private ISoundTriggerHal mUnderlying;
+ @Mock private ISoundTriggerHal.GlobalCallback mGlobalCallback;
+ @Mock private ISoundTriggerHal.ModelCallback mModelCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mComponent = new SoundTriggerDuplicateModelHandler(mUnderlying);
+ doNothing().when(mUnderlying).registerCallback(any());
+ mComponent.registerCallback(mGlobalCallback);
+ verify(mUnderlying).registerCallback(eq(mGlobalCallback));
+ }
+
+ @Test
+ public void loadSoundModel_throwsResourceContention_whenDuplicateUuid() {
+ final var soundModel = createSoundModelOne();
+ final var soundModelSameUuid = createSoundModelTwo();
+ // First sound model load should complete successfully
+ mComponent.loadSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+ assertEquals(
+ assertThrows(
+ RecoverableException.class,
+ () -> mComponent.loadSoundModel(soundModelSameUuid, mModelCallback))
+ .errorCode,
+ Status.RESOURCE_CONTENTION);
+ // Model has not been unloaded, so we don't get a callback
+ verify(mGlobalCallback, never()).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void loadSoundModel_doesNotThrowResourceContention_whenDifferentUuid() {
+ final var soundModel = createSoundModelOne();
+ // Make all other fields the same
+ final var soundModelDifferentUuid = createSoundModelOne();
+ soundModelDifferentUuid.uuid = DIFFERENT_UUID;
+ InOrder inOrder = Mockito.inOrder(mUnderlying);
+ // First sound model load should complete successfully
+ mComponent.loadSoundModel(soundModel, mModelCallback);
+ inOrder.verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+ mComponent.loadSoundModel(soundModelDifferentUuid, mModelCallback);
+ inOrder.verify(mUnderlying).loadSoundModel(eq(soundModelDifferentUuid), eq(mModelCallback));
+ // No contention, so we don't get a callback
+ verify(mGlobalCallback, never()).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void loadSoundModel_doesNotThrow_afterDuplicateUuidHasBeenUnloaded() {
+ final var soundModel = createSoundModelOne();
+ // First sound model load should complete successfully
+ int handle = mComponent.loadSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+ // Unload model should complete successfully
+ mComponent.unloadSoundModel(handle);
+ verify(mUnderlying).unloadSoundModel(eq(handle));
+ // Since the model with the same UUID was unloaded, the subsequent load model
+ // should succeed.
+ mComponent.loadSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying, times(2)).loadSoundModel(eq(soundModel), eq(mModelCallback));
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void unloadSoundModel_triggersResourceCallback_afterDuplicateUuidRejected() {
+ final var soundModel = createSoundModelOne();
+ final var soundModelSameUuid = createSoundModelTwo();
+ // First sound model load should complete successfully
+ int handle = mComponent.loadSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadSoundModel(eq(soundModel), eq(mModelCallback));
+ assertEquals(
+ assertThrows(
+ RecoverableException.class,
+ () -> mComponent.loadSoundModel(soundModelSameUuid, mModelCallback))
+ .errorCode,
+ Status.RESOURCE_CONTENTION);
+ mComponent.unloadSoundModel(handle);
+ verify(mUnderlying).unloadSoundModel(eq(handle));
+ verify(mGlobalCallback).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ // Next tests are same as above, but for phrase sound model.
+ @Test
+ public void loadPhraseSoundModel_throwsResourceContention_whenDuplicateUuid() {
+ final var soundModel = createPhraseSoundModelOne();
+ final var soundModelSameUuid = createPhraseSoundModelTwo();
+ // First sound model load should complete successfully
+ mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+ assertEquals(
+ assertThrows(
+ RecoverableException.class,
+ () ->
+ mComponent.loadPhraseSoundModel(
+ soundModelSameUuid, mModelCallback))
+ .errorCode,
+ Status.RESOURCE_CONTENTION);
+ // Model has not been unloaded, so we don't get a callback
+ verify(mGlobalCallback, never()).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void loadPhraseSoundModel_doesNotThrowResourceContention_whenDifferentUuid() {
+ final var soundModel = createPhraseSoundModelOne();
+ // Make all other fields the same
+ final var soundModelDifferentUuid = createPhraseSoundModelOne();
+ soundModelDifferentUuid.common.uuid = DIFFERENT_UUID;
+ InOrder inOrder = Mockito.inOrder(mUnderlying);
+ // First sound model load should complete successfully
+ mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+ inOrder.verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+ mComponent.loadPhraseSoundModel(soundModelDifferentUuid, mModelCallback);
+ inOrder.verify(mUnderlying).loadPhraseSoundModel(eq(soundModelDifferentUuid),
+ eq(mModelCallback));
+ // No contention, so we don't get a callback
+ verify(mGlobalCallback, never()).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void loadPhraseSoundModel_doesNotThrow_afterDuplicateUuidHasBeenUnloaded() {
+ final var soundModel = createPhraseSoundModelOne();
+ // First sound model load should complete successfully
+ int handle = mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+ // Unload model should complete successfully
+ mComponent.unloadSoundModel(handle);
+ verify(mUnderlying).unloadSoundModel(eq(handle));
+ // Since the model with the same UUID was unloaded, the subsequent load model
+ // should succeed.
+ mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying, times(2)).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void unloadSoundModel_triggersResourceCallback_afterDuplicateUuidRejectedPhrase() {
+ final var soundModel = createPhraseSoundModelOne();
+ final var soundModelSameUuid = createPhraseSoundModelTwo();
+ // First sound model load should complete successfully
+ int handle = mComponent.loadPhraseSoundModel(soundModel, mModelCallback);
+ verify(mUnderlying).loadPhraseSoundModel(eq(soundModel), eq(mModelCallback));
+ assertEquals(
+ assertThrows(
+ RecoverableException.class,
+ () ->
+ mComponent.loadPhraseSoundModel(
+ soundModelSameUuid, mModelCallback))
+ .errorCode,
+ Status.RESOURCE_CONTENTION);
+ mComponent.unloadSoundModel(handle);
+ verify(mUnderlying).unloadSoundModel(eq(handle));
+ verify(mGlobalCallback).onResourcesAvailable();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ @Test
+ public void testDelegation() {
+ // Test that the rest of the interface delegates its calls to the underlying object
+ // appropriately.
+ // This test method does not test load/unloadSoundModel
+ var properties = new Properties();
+ InOrder inOrder = Mockito.inOrder(mUnderlying);
+ doReturn(properties).when(mUnderlying).getProperties();
+ assertEquals(mComponent.getProperties(), properties);
+ inOrder.verify(mUnderlying).getProperties();
+ var mockGlobalCallback = mock(ISoundTriggerHal.GlobalCallback.class);
+ mComponent.registerCallback(mockGlobalCallback);
+ inOrder.verify(mUnderlying).registerCallback(eq(mockGlobalCallback));
+ int modelId = 5;
+ int deviceHandle = 2;
+ int ioHandle = 3;
+ var config = mock(RecognitionConfig.class);
+ mComponent.startRecognition(modelId, deviceHandle, ioHandle, config);
+ inOrder.verify(mUnderlying)
+ .startRecognition(eq(modelId), eq(deviceHandle), eq(ioHandle), eq(config));
+
+ mComponent.stopRecognition(modelId);
+ inOrder.verify(mUnderlying).stopRecognition(eq(modelId));
+ mComponent.forceRecognitionEvent(modelId);
+ inOrder.verify(mUnderlying).forceRecognitionEvent(eq(modelId));
+ int param = 10;
+ int value = 50;
+ var modelParamRange = new ModelParameterRange();
+ doReturn(modelParamRange).when(mUnderlying).queryParameter(anyInt(), anyInt());
+ assertEquals(mComponent.queryParameter(param, value), modelParamRange);
+ inOrder.verify(mUnderlying).queryParameter(param, value);
+ doReturn(value).when(mUnderlying).getModelParameter(anyInt(), anyInt());
+ assertEquals(mComponent.getModelParameter(modelId, param), value);
+ inOrder.verify(mUnderlying).getModelParameter(eq(modelId), eq(param));
+ mComponent.setModelParameter(modelId, param, value);
+ inOrder.verify(mUnderlying).setModelParameter(eq(modelId), eq(param), eq(value));
+ var recipient = mock(IBinder.DeathRecipient.class);
+ mComponent.linkToDeath(recipient);
+ inOrder.verify(mUnderlying).linkToDeath(eq(recipient));
+ mComponent.unlinkToDeath(recipient);
+ inOrder.verify(mUnderlying).unlinkToDeath(eq(recipient));
+ mComponent.flushCallbacks();
+ inOrder.verify(mUnderlying).flushCallbacks();
+ var token = mock(IBinder.class);
+ mComponent.clientAttached(token);
+ inOrder.verify(mUnderlying).clientAttached(eq(token));
+ mComponent.clientDetached(token);
+ inOrder.verify(mUnderlying).clientDetached(eq(token));
+ mComponent.reboot();
+ inOrder.verify(mUnderlying).reboot();
+ mComponent.detach();
+ inOrder.verify(mUnderlying).detach();
+ verifyNoMoreInteractions(mUnderlying);
+ verifyNoMoreInteractions(mGlobalCallback);
+ }
+
+ private static SoundModel createSoundModelOne() {
+ SoundModel model = new SoundModel();
+ model.type = SoundModelType.GENERIC;
+ model.uuid = DUPLICATE_UUID;
+ model.vendorUuid = "87654321-5432-6543-7654-456789fedcba";
+ byte[] data = new byte[] {91, 92, 93, 94, 95};
+ model.data = TestUtil.byteArrayToParcelFileDescriptor(data);
+ model.dataSize = data.length;
+ return model;
+ }
+
+ // Different except for the same UUID
+ private static SoundModel createSoundModelTwo() {
+ SoundModel model = new SoundModel();
+ model.type = SoundModelType.GENERIC;
+ model.uuid = DUPLICATE_UUID;
+ model.vendorUuid = "12345678-9876-5432-1012-345678901234";
+ byte[] data = new byte[] {19, 18, 17, 16};
+ model.data = TestUtil.byteArrayToParcelFileDescriptor(data);
+ model.dataSize = data.length;
+ return model;
+ }
+
+ private static PhraseSoundModel createPhraseSoundModelOne() {
+ PhraseSoundModel model = new PhraseSoundModel();
+ model.common = createSoundModelOne();
+ model.common.type = SoundModelType.KEYPHRASE;
+ model.phrases = new Phrase[1];
+ model.phrases[0] = new Phrase();
+ model.phrases[0].id = 123;
+ model.phrases[0].users = new int[] {5, 6, 7};
+ model.phrases[0].locale = "locale";
+ model.phrases[0].text = "text";
+ model.phrases[0].recognitionModes =
+ RecognitionMode.USER_AUTHENTICATION | RecognitionMode.USER_IDENTIFICATION;
+ return model;
+ }
+
+ private static PhraseSoundModel createPhraseSoundModelTwo() {
+ PhraseSoundModel model = new PhraseSoundModel();
+ model.common = createSoundModelTwo();
+ model.common.type = SoundModelType.KEYPHRASE;
+ model.phrases = new Phrase[1];
+ model.phrases[0] = new Phrase();
+ model.phrases[0].id = 321;
+ model.phrases[0].users = new int[] {4, 3, 2, 1};
+ model.phrases[0].locale = "differentLocale";
+ model.phrases[0].text = "differentText";
+ model.phrases[0].recognitionModes = 0;
+ return model;
+ }
+}
diff --git a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
index 39561f7..3b7bc95 100644
--- a/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
+++ b/services/tests/voiceinteractiontests/src/com/android/server/soundtrigger_middleware/TestUtil.java
@@ -466,7 +466,7 @@
assertEquals(43, event.phraseExtras[0].levels[0].levelPercent);
}
- private static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
+ static ParcelFileDescriptor byteArrayToParcelFileDescriptor(byte[] data) {
try (SharedMemory shmem = SharedMemory.create("", data.length)) {
ByteBuffer buffer = shmem.mapReadWrite();
buffer.put(data);
diff --git a/services/tests/wmtests/AndroidManifest.xml b/services/tests/wmtests/AndroidManifest.xml
index 2696d2b..f12b53a 100644
--- a/services/tests/wmtests/AndroidManifest.xml
+++ b/services/tests/wmtests/AndroidManifest.xml
@@ -87,6 +87,8 @@
android:showWhenLocked="true"/>
<activity android:name="android.view.cts.surfacevalidator.CapturedActivity"/>
+ <activity android:name="com.android.server.wm.SurfaceControlViewHostTests$TestActivity" />
+
<service android:name="android.view.cts.surfacevalidator.LocalMediaProjectionService"
android:foregroundServiceType="mediaProjection"
android:enabled="true">
diff --git a/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
new file mode 100644
index 0000000..41bfc80
--- /dev/null
+++ b/services/tests/wmtests/src/com/android/server/wm/SurfaceControlViewHostTests.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.wm;
+
+import static android.Manifest.permission.ACCESS_SURFACE_FLINGER;
+import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowVisible;
+import static android.server.wm.CtsWindowInfoUtils.waitForWindowFocus;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+
+import static org.junit.Assert.assertTrue;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.Configuration;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.platform.test.annotations.Presubmit;
+import android.view.Gravity;
+import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowlessWindowManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.test.filters.SmallTest;
+import androidx.test.platform.app.InstrumentationRegistry;
+import androidx.test.rule.ActivityTestRule;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.CountDownLatch;
+
+@Presubmit
+@SmallTest
+@RunWith(WindowTestRunner.class)
+public class SurfaceControlViewHostTests {
+ private final ActivityTestRule<TestActivity> mActivityRule = new ActivityTestRule<>(
+ TestActivity.class);
+ private Instrumentation mInstrumentation;
+ private TestActivity mActivity;
+
+ private View mView1;
+ private View mView2;
+ private SurfaceControlViewHost mScvh1;
+ private SurfaceControlViewHost mScvh2;
+
+ @Before
+ public void setUp() throws Exception {
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+
+ // ACCESS_SURFACE_FLINGER is necessary to call waitForWindow
+ // INTERNAL_SYSTEM_WINDOW is necessary to add SCVH with no host parent
+ mInstrumentation.getUiAutomation().adoptShellPermissionIdentity(ACCESS_SURFACE_FLINGER,
+ INTERNAL_SYSTEM_WINDOW);
+ mActivity = mActivityRule.launchActivity(null);
+ }
+
+ @After
+ public void tearDown() {
+ mInstrumentation.getUiAutomation().dropShellPermissionIdentity();
+ }
+
+ @Test
+ public void requestFocusWithMultipleWindows() throws InterruptedException, RemoteException {
+ SurfaceControl sc = new SurfaceControl.Builder()
+ .setName("SurfaceControlViewHostTests")
+ .setCallsite("requestFocusWithMultipleWindows")
+ .build();
+ mView1 = new Button(mActivity);
+ mView2 = new Button(mActivity);
+
+ mActivity.runOnUiThread(() -> {
+ TestWindowlessWindowManager wwm = new TestWindowlessWindowManager(
+ mActivity.getResources().getConfiguration(), sc, null);
+
+ try {
+ mActivity.attachToSurfaceView(sc);
+ } catch (InterruptedException e) {
+ }
+
+ mScvh1 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
+ wwm, "requestFocusWithMultipleWindows");
+ mScvh2 = new SurfaceControlViewHost(mActivity, mActivity.getDisplay(),
+ wwm, "requestFocusWithMultipleWindows");
+
+
+ mView1.setBackgroundColor(Color.RED);
+ mView2.setBackgroundColor(Color.BLUE);
+
+ WindowManager.LayoutParams lp1 = new WindowManager.LayoutParams(200, 200,
+ TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+ WindowManager.LayoutParams lp2 = new WindowManager.LayoutParams(100, 100,
+ TYPE_APPLICATION, 0, PixelFormat.OPAQUE);
+ mScvh1.setView(mView1, lp1);
+ mScvh2.setView(mView2, lp2);
+ });
+
+ assertTrue("Failed to wait for view1", waitForWindowVisible(mView1));
+ assertTrue("Failed to wait for view2", waitForWindowVisible(mView2));
+
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mScvh1.getFocusGrantToken(), true);
+ assertTrue("Failed to gain focus for view1", waitForWindowFocus(mView1, true));
+
+ WindowManagerGlobal.getWindowSession().grantEmbeddedWindowFocus(null /* window */,
+ mScvh2.getFocusGrantToken(), true);
+ assertTrue("Failed to gain focus for view2", waitForWindowFocus(mView2, true));
+ }
+
+ private static class TestWindowlessWindowManager extends WindowlessWindowManager {
+ private final SurfaceControl mRoot;
+
+ TestWindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
+ IBinder hostInputToken) {
+ super(c, rootSurface, hostInputToken);
+ mRoot = rootSurface;
+ }
+
+ @Override
+ protected SurfaceControl getParentSurface(IWindow window,
+ WindowManager.LayoutParams attrs) {
+ return mRoot;
+ }
+ }
+
+ public static class TestActivity extends Activity implements SurfaceHolder.Callback {
+ private SurfaceView mSurfaceView;
+ private final CountDownLatch mSvReadyLatch = new CountDownLatch(1);
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ final FrameLayout content = new FrameLayout(this);
+ mSurfaceView = new SurfaceView(this);
+ mSurfaceView.setBackgroundColor(Color.BLACK);
+ mSurfaceView.setZOrderOnTop(true);
+ final FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(500, 500,
+ Gravity.LEFT | Gravity.TOP);
+ content.addView(mSurfaceView, lp);
+ setContentView(content);
+ mSurfaceView.getHolder().addCallback(this);
+ }
+
+ @Override
+ public void surfaceCreated(@NonNull SurfaceHolder holder) {
+ mSvReadyLatch.countDown();
+ }
+
+ @Override
+ public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width,
+ int height) {
+ }
+
+ @Override
+ public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
+ }
+
+ public void attachToSurfaceView(SurfaceControl sc) throws InterruptedException {
+ mSvReadyLatch.await();
+ new SurfaceControl.Transaction().reparent(sc, mSurfaceView.getSurfaceControl())
+ .show(sc).apply();
+ }
+ }
+}
+
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java
new file mode 100644
index 0000000..0104193
--- /dev/null
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerDuplicateModelHandler.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.soundtrigger_middleware;
+
+import android.annotation.NonNull;
+import android.media.soundtrigger.ModelParameterRange;
+import android.media.soundtrigger.PhraseSoundModel;
+import android.media.soundtrigger.Properties;
+import android.media.soundtrigger.RecognitionConfig;
+import android.media.soundtrigger.SoundModel;
+import android.media.soundtrigger.Status;
+import android.os.IBinder;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This wrapper prevents a models with the same UUID from being loaded concurrently. This is used to
+ * protect STHAL implementations, which don't support concurrent loads of the same model. We reject
+ * the duplicate load with {@link Status#RESOURCE_CONTENTION}.
+ */
+public class SoundTriggerDuplicateModelHandler implements ISoundTriggerHal {
+ private final @NonNull ISoundTriggerHal mDelegate;
+
+ private GlobalCallback mGlobalCallback;
+ // There are rarely more than two models loaded.
+ private final List<ModelData> mModelList = new ArrayList<>();
+
+ private static final class ModelData {
+ ModelData(int modelId, String uuid) {
+ mModelId = modelId;
+ mUuid = uuid;
+ }
+
+ int getModelId() {
+ return mModelId;
+ }
+
+ String getUuid() {
+ return mUuid;
+ }
+
+ boolean getWasContended() {
+ return mWasContended;
+ }
+
+ void setWasContended() {
+ mWasContended = true;
+ }
+
+ private int mModelId;
+ private String mUuid;
+ private boolean mWasContended = false;
+ }
+
+ public SoundTriggerDuplicateModelHandler(@NonNull ISoundTriggerHal delegate) {
+ mDelegate = delegate;
+ }
+
+ @Override
+ public void reboot() {
+ mDelegate.reboot();
+ }
+
+ @Override
+ public void detach() {
+ mDelegate.detach();
+ }
+
+ @Override
+ public Properties getProperties() {
+ return mDelegate.getProperties();
+ }
+
+ @Override
+ public void registerCallback(GlobalCallback callback) {
+ mGlobalCallback = callback;
+ mDelegate.registerCallback(mGlobalCallback);
+ }
+
+ @Override
+ public int loadSoundModel(SoundModel soundModel, ModelCallback callback) {
+ synchronized (this) {
+ checkDuplicateModelUuid(soundModel.uuid);
+ var result = mDelegate.loadSoundModel(soundModel, callback);
+ mModelList.add(new ModelData(result, soundModel.uuid));
+ return result;
+ }
+ }
+
+ @Override
+ public int loadPhraseSoundModel(PhraseSoundModel soundModel, ModelCallback callback) {
+ synchronized (this) {
+ checkDuplicateModelUuid(soundModel.common.uuid);
+ var result = mDelegate.loadPhraseSoundModel(soundModel, callback);
+ mModelList.add(new ModelData(result, soundModel.common.uuid));
+ return result;
+ }
+ }
+
+ @Override
+ public void unloadSoundModel(int modelHandle) {
+ mDelegate.unloadSoundModel(modelHandle);
+ for (int i = 0; i < mModelList.size(); i++) {
+ if (mModelList.get(i).getModelId() == modelHandle) {
+ var modelData = mModelList.remove(i);
+ if (modelData.getWasContended()) {
+ mGlobalCallback.onResourcesAvailable();
+ }
+ // Model ID is unique
+ return;
+ }
+ }
+ }
+
+ // Uninteresting delegation calls to follow.
+ @Override
+ public void stopRecognition(int modelHandle) {
+ mDelegate.stopRecognition(modelHandle);
+ }
+
+ @Override
+ public void startRecognition(
+ int modelHandle, int deviceHandle, int ioHandle, RecognitionConfig config) {
+ mDelegate.startRecognition(modelHandle, deviceHandle, ioHandle, config);
+ }
+
+ @Override
+ public void forceRecognitionEvent(int modelHandle) {
+ mDelegate.forceRecognitionEvent(modelHandle);
+ }
+
+ @Override
+ public int getModelParameter(int modelHandle, int param) {
+ return mDelegate.getModelParameter(modelHandle, param);
+ }
+
+ @Override
+ public void setModelParameter(int modelHandle, int param, int value) {
+ mDelegate.setModelParameter(modelHandle, param, value);
+ }
+
+ @Override
+ public ModelParameterRange queryParameter(int modelHandle, int param) {
+ return mDelegate.queryParameter(modelHandle, param);
+ }
+
+ @Override
+ public void linkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.linkToDeath(recipient);
+ }
+
+ @Override
+ public void unlinkToDeath(IBinder.DeathRecipient recipient) {
+ mDelegate.unlinkToDeath(recipient);
+ }
+
+ @Override
+ public String interfaceDescriptor() {
+ return mDelegate.interfaceDescriptor();
+ }
+
+ @Override
+ public void flushCallbacks() {
+ mDelegate.flushCallbacks();
+ }
+
+ @Override
+ public void clientAttached(IBinder binder) {
+ mDelegate.clientAttached(binder);
+ }
+
+ @Override
+ public void clientDetached(IBinder binder) {
+ mDelegate.clientDetached(binder);
+ }
+
+ /**
+ * Helper for handling duplicate model. If there is a load attempt for a model with a UUID which
+ * is already loaded: 1) Reject with {@link Status.RESOURCE_CONTENTION} 2) Mark the already
+ * loaded model as contended, as we need to dispatch a resource available callback following the
+ * original model being unloaded.
+ */
+ private void checkDuplicateModelUuid(String uuid) {
+ var model = mModelList.stream().filter(x -> x.getUuid().equals(uuid)).findFirst();
+ if (model.isPresent()) {
+ model.get().setWasContended();
+ throw new RecoverableException(Status.RESOURCE_CONTENTION);
+ }
+ }
+}
diff --git a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
index d2d8f1a..6223b2e 100644
--- a/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
+++ b/services/voiceinteraction/java/com/android/server/soundtrigger_middleware/SoundTriggerModule.java
@@ -170,7 +170,8 @@
*/
private void attachToHal() {
mHalService = new SoundTriggerHalEnforcer(
- new SoundTriggerHalWatchdog(mHalFactory.create()));
+ new SoundTriggerHalWatchdog(
+ new SoundTriggerDuplicateModelHandler(mHalFactory.create())));
mHalService.linkToDeath(this);
mHalService.registerCallback(this);
mProperties = mHalService.getProperties();
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
index a1adee7..486945d 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/HotwordDetectionConnection.java
@@ -154,7 +154,8 @@
private int mRestartCount = 0;
@NonNull private ServiceConnection mRemoteHotwordDetectionService;
@NonNull private ServiceConnection mRemoteVisualQueryDetectionService;
- private IBinder mAudioFlinger;
+ @GuardedBy("mLock")
+ @Nullable private IBinder mAudioFlinger;
@GuardedBy("mLock")
private boolean mDebugHotwordLogging = false;
@@ -194,7 +195,7 @@
new Intent(VisualQueryDetectionService.SERVICE_INTERFACE);
visualQueryDetectionServiceIntent.setComponent(mVisualQueryDetectionComponentName);
- initAudioFlingerLocked();
+ initAudioFlinger();
mHotwordDetectionServiceConnectionFactory =
new ServiceConnectionFactory(hotwordDetectionServiceIntent,
@@ -227,31 +228,41 @@
}
}
- private void initAudioFlingerLocked() {
+ private void initAudioFlinger() {
if (DEBUG) {
- Slog.d(TAG, "initAudioFlingerLocked");
+ Slog.d(TAG, "initAudioFlinger");
}
- mAudioFlinger = ServiceManager.waitForService("media.audio_flinger");
- if (mAudioFlinger == null) {
+ final IBinder audioFlinger = ServiceManager.waitForService("media.audio_flinger");
+ if (audioFlinger == null) {
+ setAudioFlinger(null);
throw new IllegalStateException("Service media.audio_flinger wasn't found.");
}
if (DEBUG) {
Slog.d(TAG, "Obtained audio_flinger binder.");
}
try {
- mAudioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
+ audioFlinger.linkToDeath(mAudioServerDeathRecipient, /* flags= */ 0);
} catch (RemoteException e) {
Slog.w(TAG, "Audio server died before we registered a DeathRecipient; "
- + "retrying init.", e);
- initAudioFlingerLocked();
+ + "retrying init.", e);
+ initAudioFlinger();
+ return;
+ }
+
+ setAudioFlinger(audioFlinger);
+ }
+
+ private void setAudioFlinger(@Nullable IBinder audioFlinger) {
+ synchronized (mLock) {
+ mAudioFlinger = audioFlinger;
}
}
private void audioServerDied() {
Slog.w(TAG, "Audio server died; restarting the HotwordDetectionService.");
+ // TODO: Check if this needs to be scheduled on a different thread.
+ initAudioFlinger();
synchronized (mLock) {
- // TODO: Check if this needs to be scheduled on a different thread.
- initAudioFlingerLocked();
// We restart the process instead of simply sending over the new binder, to avoid race
// conditions with audio reading in the service.
restartProcessLocked();
diff --git a/telephony/java/android/telephony/satellite/PointingInfo.java b/telephony/java/android/telephony/satellite/PointingInfo.java
index e8a8576..a559b32 100644
--- a/telephony/java/android/telephony/satellite/PointingInfo.java
+++ b/telephony/java/android/telephony/satellite/PointingInfo.java
@@ -17,7 +17,6 @@
package android.telephony.satellite;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -34,7 +33,7 @@
/**
* @hide
*/
- @UnsupportedAppUsage
+
public PointingInfo(float satelliteAzimuthDegrees, float satelliteElevationDegrees) {
mSatelliteAzimuthDegrees = satelliteAzimuthDegrees;
mSatelliteElevationDegrees = satelliteElevationDegrees;
diff --git a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
index 0092890..6856cc0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
+++ b/telephony/java/android/telephony/satellite/SatelliteCapabilities.java
@@ -17,7 +17,6 @@
package android.telephony.satellite;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -57,7 +56,6 @@
/**
* @hide
*/
- @UnsupportedAppUsage
public SatelliteCapabilities(Set<Integer> supportedRadioTechnologies,
boolean isPointingRequired, int maxBytesPerOutgoingDatagram,
@NonNull Map<Integer, AntennaPosition> antennaPositionMap) {
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagram.java b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
index 44f56f3..d3cb8a0 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagram.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagram.java
@@ -17,7 +17,6 @@
package android.telephony.satellite;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
import android.os.Parcel;
import android.os.Parcelable;
@@ -33,7 +32,6 @@
/**
* @hide
*/
- @UnsupportedAppUsage
public SatelliteDatagram(@NonNull byte[] data) {
mData = data;
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
index d0409bf..b2dec71 100644
--- a/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteDatagramCallback.java
@@ -17,7 +17,6 @@
package android.telephony.satellite;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
import java.util.function.Consumer;
@@ -37,7 +36,6 @@
* that they received the datagram. If the callback is not received within
* five minutes, Telephony will resend the datagram.
*/
- @UnsupportedAppUsage
void onSatelliteDatagramReceived(long datagramId, @NonNull SatelliteDatagram datagram,
int pendingCount, @NonNull Consumer<Void> callback);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteManager.java b/telephony/java/android/telephony/satellite/SatelliteManager.java
index 5681ab2..c5830b8 100644
--- a/telephony/java/android/telephony/satellite/SatelliteManager.java
+++ b/telephony/java/android/telephony/satellite/SatelliteManager.java
@@ -23,7 +23,6 @@
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
-import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Binder;
@@ -37,8 +36,8 @@
import android.telephony.TelephonyFrameworkInitializer;
import com.android.internal.telephony.IIntegerConsumer;
-import com.android.internal.telephony.IVoidConsumer;
import com.android.internal.telephony.ITelephony;
+import com.android.internal.telephony.IVoidConsumer;
import com.android.telephony.Rlog;
import java.lang.annotation.Retention;
@@ -83,7 +82,7 @@
* @param context The context the SatelliteManager belongs to.
* @hide
*/
- @UnsupportedAppUsage
+
public SatelliteManager(@Nullable Context context) {
this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
}
@@ -129,7 +128,7 @@
* {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_ENABLED = "satellite_enabled";
/**
@@ -137,7 +136,7 @@
* {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled";
/**
@@ -145,7 +144,7 @@
* {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported";
/**
@@ -153,7 +152,7 @@
* {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities";
/**
@@ -161,7 +160,7 @@
* {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned";
/**
@@ -169,7 +168,7 @@
* {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED =
"satellite_communication_allowed";
@@ -178,7 +177,7 @@
* {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}.
* @hide
*/
- @UnsupportedAppUsage
+
public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility";
/**
@@ -389,7 +388,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestSatelliteEnabled(boolean enableSatellite, boolean enableDemoMode,
@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -432,7 +431,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestIsSatelliteEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -487,7 +486,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestIsDemoModeEnabled(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -540,7 +539,7 @@
*
* @throws IllegalStateException if the Telephony process is not currently available.
*/
- @UnsupportedAppUsage
+
public void requestIsSatelliteSupported(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -594,7 +593,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestSatelliteCapabilities(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<SatelliteCapabilities, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -787,7 +786,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void startSatelliteTransmissionUpdates(@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener,
@NonNull SatelliteTransmissionUpdateCallback callback) {
@@ -857,7 +856,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void stopSatelliteTransmissionUpdates(
@NonNull SatelliteTransmissionUpdateCallback callback,
@NonNull @CallbackExecutor Executor executor,
@@ -913,7 +912,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void provisionSatelliteService(@NonNull String token, @NonNull byte[] provisionData,
@Nullable CancellationSignal cancellationSignal,
@NonNull @CallbackExecutor Executor executor,
@@ -963,7 +962,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void deprovisionSatelliteService(@NonNull String token,
@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
@@ -1003,7 +1002,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
@SatelliteError public int registerForSatelliteProvisionStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteProvisionStateCallback callback) {
@@ -1046,7 +1045,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void unregisterForSatelliteProvisionStateChanged(
@NonNull SatelliteProvisionStateCallback callback) {
Objects.requireNonNull(callback);
@@ -1085,7 +1084,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestIsSatelliteProvisioned(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
Objects.requireNonNull(executor);
@@ -1137,7 +1136,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
@SatelliteError public int registerForSatelliteModemStateChanged(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteStateCallback callback) {
@@ -1177,7 +1176,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void unregisterForSatelliteModemStateChanged(@NonNull SatelliteStateCallback callback) {
Objects.requireNonNull(callback);
ISatelliteStateCallback internalCallback = sSatelliteStateCallbackMap.remove(callback);
@@ -1212,7 +1211,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
@SatelliteError public int registerForSatelliteDatagram(
@NonNull @CallbackExecutor Executor executor,
@NonNull SatelliteDatagramCallback callback) {
@@ -1268,7 +1267,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void unregisterForSatelliteDatagram(@NonNull SatelliteDatagramCallback callback) {
Objects.requireNonNull(callback);
ISatelliteDatagramCallback internalCallback =
@@ -1306,7 +1305,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void pollPendingSatelliteDatagrams(@NonNull @CallbackExecutor Executor executor,
@SatelliteError @NonNull Consumer<Integer> resultListener) {
Objects.requireNonNull(executor);
@@ -1359,7 +1358,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void sendSatelliteDatagram(@DatagramType int datagramType,
@NonNull SatelliteDatagram datagram, boolean needFullScreenPointingUI,
@NonNull @CallbackExecutor Executor executor,
@@ -1405,7 +1404,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestIsSatelliteCommunicationAllowedForCurrentLocation(
@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
@@ -1463,7 +1462,7 @@
* @throws IllegalStateException if the Telephony process is not currently available.
*/
@RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
- @UnsupportedAppUsage
+
public void requestTimeForNextSatelliteVisibility(@NonNull @CallbackExecutor Executor executor,
@NonNull OutcomeReceiver<Duration, SatelliteException> callback) {
Objects.requireNonNull(executor);
diff --git a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
index 20875af..a62eb8b 100644
--- a/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteProvisionStateCallback.java
@@ -16,8 +16,6 @@
package android.telephony.satellite;
-import android.compat.annotation.UnsupportedAppUsage;
-
/**
* A callback class for monitoring satellite provision state change events.
*
@@ -30,6 +28,5 @@
* @param provisioned The new provision state. {@code true} means satellite is provisioned
* {@code false} means satellite is not provisioned.
*/
- @UnsupportedAppUsage
void onSatelliteProvisionStateChanged(boolean provisioned);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
index 3312e3a..d9ecaa3 100644
--- a/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteStateCallback.java
@@ -16,8 +16,6 @@
package android.telephony.satellite;
-import android.compat.annotation.UnsupportedAppUsage;
-
/**
* A callback class for monitoring satellite modem state change events.
*
@@ -28,6 +26,5 @@
* Called when satellite modem state changes.
* @param state The new satellite modem state.
*/
- @UnsupportedAppUsage
void onSatelliteModemStateChanged(@SatelliteManager.SatelliteModemState int state);
}
diff --git a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
index 17fff44..d4fe57a 100644
--- a/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
+++ b/telephony/java/android/telephony/satellite/SatelliteTransmissionUpdateCallback.java
@@ -17,7 +17,6 @@
package android.telephony.satellite;
import android.annotation.NonNull;
-import android.compat.annotation.UnsupportedAppUsage;
/**
* A callback class for monitoring satellite position update and datagram transfer state change
@@ -31,7 +30,6 @@
*
* @param pointingInfo The pointing info containing the satellite location.
*/
- @UnsupportedAppUsage
void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo);
/**
@@ -41,7 +39,6 @@
* @param sendPendingCount The number of datagrams that are currently being sent.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
- @UnsupportedAppUsage
void onSendDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int sendPendingCount,
@SatelliteManager.SatelliteError int errorCode);
@@ -53,7 +50,6 @@
* @param receivePendingCount The number of datagrams that are currently pending to be received.
* @param errorCode If datagram transfer failed, the reason for failure.
*/
- @UnsupportedAppUsage
void onReceiveDatagramStateChanged(
@SatelliteManager.SatelliteDatagramTransferState int state, int receivePendingCount,
@SatelliteManager.SatelliteError int errorCode);
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/Input/Android.bp b/tests/Input/Android.bp
index a4c48fd..4fa6fbe 100644
--- a/tests/Input/Android.bp
+++ b/tests/Input/Android.bp
@@ -25,7 +25,7 @@
"services.core.unboosted",
"testables",
"truth-prebuilt",
- "ub-uiautomator",
+ "androidx.test.uiautomator_uiautomator",
],
test_suites: ["device-tests"],
}
diff --git a/tests/Input/src/com/android/test/input/AnrTest.kt b/tests/Input/src/com/android/test/input/AnrTest.kt
index 0246426..d185ee6 100644
--- a/tests/Input/src/com/android/test/input/AnrTest.kt
+++ b/tests/Input/src/com/android/test/input/AnrTest.kt
@@ -27,14 +27,15 @@
import android.os.SystemClock
import android.provider.Settings
import android.provider.Settings.Global.HIDE_ERROR_DIALOGS
-import android.support.test.uiautomator.By
-import android.support.test.uiautomator.UiDevice
-import android.support.test.uiautomator.UiObject2
-import android.support.test.uiautomator.Until
import android.testing.PollingCheck
import android.view.InputDevice
import android.view.MotionEvent
+import androidx.test.uiautomator.By
+import androidx.test.uiautomator.UiDevice
+import androidx.test.uiautomator.UiObject2
+import androidx.test.uiautomator.Until
+
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
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/TestableSettingsProvider.java b/tests/testables/src/android/testing/TestableSettingsProvider.java
index c6f18fd..b850cb8 100644
--- a/tests/testables/src/android/testing/TestableSettingsProvider.java
+++ b/tests/testables/src/android/testing/TestableSettingsProvider.java
@@ -72,7 +72,11 @@
public Bundle call(String method, String arg, Bundle extras) {
// Methods are "GET_system", "GET_global", "PUT_secure", etc.
- final int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.myUserId());
+ int userId = extras.getInt(Settings.CALL_METHOD_USER_KEY, UserHandle.USER_CURRENT);
+ if (userId == UserHandle.USER_CURRENT || userId == UserHandle.USER_CURRENT_OR_SELF) {
+ userId = UserHandle.myUserId();
+ }
+
final String[] commands = method.split("_", 2);
final String op = commands[0];
final String table = commands[1];