diff options
166 files changed, 3954 insertions, 1376 deletions
diff --git a/apct-tests/perftests/blobstore/Android.bp b/apct-tests/perftests/blobstore/Android.bp index 9064b4494ed1..2590fe3d843f 100644 --- a/apct-tests/perftests/blobstore/Android.bp +++ b/apct-tests/perftests/blobstore/Android.bp @@ -29,7 +29,7 @@ android_test { "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 0208dab33746..4e4780ff8948 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.app.UiAutomation; 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 03e5468a342a..3cd9f50aee6a 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.ManualBenchmarkState; 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 64b242334a8a..fd8ddbcf3809 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 @@ public interface JobSchedulerInternal { * @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 0650ce3d141c..f779b4d96b45 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 @@ public class DeviceIdleController extends SystemService @GuardedBy("this") private boolean mHasGps; @GuardedBy("this") - private boolean mHasNetworkLocation; + private boolean mHasFusedLocation; @GuardedBy("this") private Location mLastGenericLocation; @GuardedBy("this") @@ -3782,12 +3782,14 @@ public class DeviceIdleController extends SystemService 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 @@ public class DeviceIdleController extends SystemService 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 8bd3d127c21b..b9b825c9f75c 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 @@ class JobConcurrencyManager { 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 f6e00ec24b33..d94674b5cab0 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.content.pm.UserPackage; 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 @@ class JobNotificationCoordinator { 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 @@ class JobNotificationCoordinator { 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 @@ class JobNotificationCoordinator { 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 @@ class JobNotificationCoordinator { } 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 @@ class JobNotificationCoordinator { 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 @@ class JobNotificationCoordinator { || 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 @@ class JobNotificationCoordinator { 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 @@ class JobNotificationCoordinator { && 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 3aec8ba39a35..6eeff82f1158 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 @@ public class JobSchedulerService extends com.android.server.SystemService @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 44700c86efef..fb36cdec490f 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 @@ public final class JobServiceContext implements ServiceConnection { 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 eb43c38f76a3..ef634b565b65 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 @@ public class ThermalStatusRestriction extends JobRestriction { 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 699808156033..b6dc32a29f04 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.annotation.UserIdInt; 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 @@ public class Bmgr { 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 15ed45041d32..24d1c9577f82 100644 --- a/core/java/android/inputmethodservice/InkWindow.java +++ b/core/java/android/inputmethodservice/InkWindow.java @@ -195,6 +195,7 @@ final class InkWindow extends PhoneWindow { 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 f19d30258393..2240fe7f7d3b 100644 --- a/core/java/android/nfc/tech/NdefFormatable.java +++ b/core/java/android/nfc/tech/NdefFormatable.java @@ -124,6 +124,9 @@ public final class NdefFormatable extends BasicTagTechnology { 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 63259edeae7d..ecce2ff8e960 100644 --- a/core/java/android/os/image/DynamicSystemClient.java +++ b/core/java/android/os/image/DynamicSystemClient.java @@ -202,6 +202,13 @@ public class DynamicSystemClient { 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,19 @@ public class DynamicSystemClient { */ 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"; 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 7cb959dee245..aa5a0d0c162b 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -2585,7 +2585,7 @@ public final class Settings { * <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 d2a4a660452c..fb2f4ad2abe6 100644 --- a/core/java/android/service/credentials/CredentialProviderInfoFactory.java +++ b/core/java/android/service/credentials/CredentialProviderInfoFactory.java @@ -42,7 +42,6 @@ import android.os.RemoteException; 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 org.xmlpull.v1.XmlPullParserException; 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { // 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { } 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { } 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { 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 @@ public final class CredentialProviderInfoFactory { 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 e87333fe4941..53a5fd5c634d 100644 --- a/core/java/android/service/credentials/CredentialProviderService.java +++ b/core/java/android/service/credentials/CredentialProviderService.java @@ -226,7 +226,7 @@ public abstract class CredentialProviderService extends Service { 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 dd5373ff8c38..82571db469be 100644 --- a/core/java/android/service/dreams/DreamManagerInternal.java +++ b/core/java/android/service/dreams/DreamManagerInternal.java @@ -80,10 +80,10 @@ public abstract class DreamManagerInternal { */ 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 77bbeb59927a..74ab7095d392 100644 --- a/core/java/android/service/wallpaper/WallpaperService.java +++ b/core/java/android/service/wallpaper/WallpaperService.java @@ -112,6 +112,7 @@ import java.util.Objects; 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 @@ public abstract class WallpaperService extends Service { Message msg = mCaller.obtainMessageIO(MSG_WINDOW_RESIZED, reportDraw ? 1 : 0, mergedConfiguration); + mIWallpaperEngine.mPendingResizeCount.incrementAndGet(); mCaller.sendMessage(msg); } @@ -510,6 +512,7 @@ public abstract class WallpaperService extends Service { public Engine(Supplier<Long> clockFunction, Handler handler) { mClockFunction = clockFunction; mHandler = handler; + mMergedConfiguration.setOverrideConfiguration(getResources().getConfiguration()); } /** @@ -1051,6 +1054,10 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { } } - 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 @@ public abstract class WallpaperService extends Service { | 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 @@ public abstract class WallpaperService extends Service { 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 5dd2d82200bc..edc59931d4f8 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -195,7 +195,7 @@ public final class Choreographer { 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 @@ public final class Choreographer { mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; mLastFrameIntervalNanos = frameIntervalNanos; - mLastVsyncEventData = vsyncEventData; + mLastVsyncEventData.copyFrom(vsyncEventData); } if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { @@ -1262,7 +1262,7 @@ public final class Choreographer { 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 @@ public final class Choreographer { 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 03074894b2ff..54db34e788e9 100644 --- a/core/java/android/view/DisplayEventReceiver.java +++ b/core/java/android/view/DisplayEventReceiver.java @@ -81,7 +81,10 @@ public abstract class DisplayEventReceiver { // 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 @@ public abstract class DisplayEventReceiver { } 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 @@ public abstract class DisplayEventReceiver { * @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 @@ public abstract class DisplayEventReceiver { 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 @@ public abstract class DisplayEventReceiver { * {@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 @@ public abstract class DisplayEventReceiver { 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 @@ public abstract class DisplayEventReceiver { // 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 e9984daaf389..bd6224b07b16 100644 --- a/core/java/android/view/SurfaceControlViewHost.java +++ b/core/java/android/view/SurfaceControlViewHost.java @@ -120,6 +120,8 @@ public class SurfaceControlViewHost { 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 @@ public class SurfaceControlViewHost { 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 @@ public class SurfaceControlViewHost { 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 @@ public class SurfaceControlViewHost { 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 class SurfaceControlViewHost { 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 class SurfaceControlViewHost { */ 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 @@ public class SurfaceControlViewHost { * @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 98681446446b..96bfb2d9e1e6 100644 --- a/core/java/android/view/WindowlessWindowManager.java +++ b/core/java/android/view/WindowlessWindowManager.java @@ -106,8 +106,22 @@ public class WindowlessWindowManager implements IWindowSession { 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 70db6e513c88..50249da08c3c 100644 --- a/core/java/android/view/translation/Translator.java +++ b/core/java/android/view/translation/Translator.java @@ -18,7 +18,6 @@ package android.view.translation; 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 @@ public class Translator { @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 514df59f1989..140e3f1b6fdc 100644 --- a/core/java/android/view/translation/UiTranslationController.java +++ b/core/java/android/view/translation/UiTranslationController.java @@ -122,8 +122,9 @@ public class UiTranslationController implements Dumpable { 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 @@ public class UiTranslationController implements Dumpable { } 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 @@ public class UiTranslationController implements Dumpable { */ 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 @@ public class UiTranslationController implements Dumpable { } final LongSparseArray<ViewTranslationResponse> virtualChildResponse = translatedResult.valueAt(i); - if (DEBUG) { + if (isLoggable) { Log.v(TAG, "onVirtualViewTranslationCompleted: received response for " + "AutofillId " + autofillId); } @@ -379,7 +381,7 @@ public class UiTranslationController implements Dumpable { } 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 @@ public class UiTranslationController implements Dumpable { * 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 @@ public class UiTranslationController implements Dumpable { } 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 @@ public class UiTranslationController implements Dumpable { (TextViewTranslationCallback) callback; if (textViewCallback.isShowingTranslation() || textViewCallback.isAnimationRunning()) { - if (DEBUG) { + if (isLoggable) { Log.d(TAG, "Duplicate ViewTranslationResponse for " + autofillId + ". Ignoring."); } @@ -458,7 +461,7 @@ public class UiTranslationController implements Dumpable { 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 @@ public class UiTranslationController implements Dumpable { 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 @@ public class UiTranslationController implements Dumpable { 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 @@ public class UiTranslationController implements Dumpable { 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 dd72689206ba..410b44161cf6 100644 --- a/core/jni/android_view_DisplayEventReceiver.cpp +++ b/core/jni/android_view_DisplayEventReceiver.cpp @@ -48,12 +48,22 @@ static struct { 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 @@ static struct { 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 @@ protected: private: jobject mReceiverWeakGlobal; + jobject mVsyncEventDataWeakGlobal; sp<MessageQueue> mMessageQueue; void dispatchVsync(nsecs_t timestamp, PhysicalDisplayId displayId, uint32_t count, @@ -85,6 +96,7 @@ private: }; NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject receiverWeak, + jobject vsyncEventDataWeak, const sp<MessageQueue>& messageQueue, jint vsyncSource, jint eventRegistration, jlong layerHandle) @@ -96,6 +108,7 @@ NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env, jobject rece 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 @@ void NativeDisplayEventReceiver::dispatchVsync(nsecs_t timestamp, PhysicalDispla 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 @@ void NativeDisplayEventReceiver::dispatchFrameRateOverrides( 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 @@ static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverWeak, jobject } 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 jobject nativeGetLatestVsyncEventData(JNIEnv* env, jclass clazz, jlong re 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 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { 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 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { 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 @@ int register_android_view_DisplayEventReceiver(JNIEnv* env) { "([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 9f10ae6066f5..5544701d9325 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 a823d1fd8ff4..cea603265930 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 e59b2597722f..706845360bf4 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -1665,47 +1665,47 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 @@ easier. <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 4fae8fb77687..8b3667e9f2a9 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 b3f399363aef..8d9461d8035d 100644 --- a/core/tests/coretests/src/android/app/OWNERS +++ b/core/tests/coretests/src/android/app/OWNERS @@ -4,3 +4,6 @@ per-file Window*.java = file:/services/core/java/com/android/server/wm/OWNERS 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 2fd65dc29363..cea535e8f480 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 0eb4caaf7a0f..7434cb02cc7c 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 000000000000..14fb4e29c33c --- /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 521a65cc4df6..bfbddbbe4aa0 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 android.window.TransitionInfo.FLAG_IN_TASK_WITH_EMBEDDED_ACTIVITY; 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 androidx.annotation.Nullable; 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 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle @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 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle // 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 @@ public class ActivityEmbeddingController implements Transitions.TransitionHandle 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 8c98c77a29ce..1d7e64988359 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 @@ public class PipAnimationController { * @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 @@ public class PipAnimationController { } 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 @@ public class PipAnimationController { 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 @@ public class PipAnimationController { .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 000624499f79..0775f5279e31 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 @@ public interface PipMenuController { 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 @@ public interface PipMenuController { * 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 5670fe6eaeba..23706a5e35a4 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, }, null); } - private boolean shouldSyncPipTransactionWithMenu() { + protected boolean shouldSyncPipTransactionWithMenu() { return mPipMenuController.isMenuVisible(); } @@ -277,9 +277,9 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, return mPipTransitionController; } + PipAnimationController.PipTransactionHandler getPipTransactionHandler() { + return mPipTransactionHandler; + } + public Rect getCurrentOrAnimatingBounds() { PipAnimationController.PipTransitionAnimator animator = mPipAnimationController.getCurrentAnimator(); @@ -1385,7 +1389,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, .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 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener, 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 b743140b2403..c5e92294794e 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 @@ public class PipTransition extends PipTransitionController { 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 @@ public class PipTransition extends PipTransitionController { } 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 94e593b106a5..e7a1395f541c 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 @@ public class PhonePipMenuController implements PipMenuController { } // 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 @@ public class PhonePipMenuController implements PipMenuController { @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 b18e21c03c63..b2a189b45d6c 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.os.Handler; 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 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis } 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 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis @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 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis 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 0940490e9944..4819f665d6d3 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 @@ public class TvPipTaskOrganizer extends PipTaskOrganizer { 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 2cd16be9590c..498f95c8595e 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 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, 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 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, 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 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, 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 @@ public class SplitScreenController implements DragAndDropPolicy.Starter, 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 8b890bba20b6..2c08cd44becc 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 @@ class SplitScreenTransitions { 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 ce5a2af65646..e4f27240b2cb 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, 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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, @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 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler, } } 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/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 cbbb29199d75..b8f615a1855f 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 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation @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 @@ public class ActivityEmbeddingControllerTests extends ActivityEmbeddingAnimation 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 718d4a16d5c8..96bfc1061d4e 100644 --- a/libs/hwui/renderthread/VulkanManager.cpp +++ b/libs/hwui/renderthread/VulkanManager.cpp @@ -42,6 +42,36 @@ namespace android { 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 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe 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 @@ void VulkanManager::setupDevice(GrVkExtensions& grExtensions, VkPhysicalDeviceFe 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 116237f6b998..609c7a40bb36 100644 --- a/media/jni/android_media_MediaExtractor.cpp +++ b/media/jni/android_media_MediaExtractor.cpp @@ -196,6 +196,15 @@ status_t JMediaExtractor::readSampleData( 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 @@ status_t JMediaExtractor::readSampleData( 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 24f92c00c772..7581b5c0aa91 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/CredentialSelectorActivity.kt @@ -19,9 +19,9 @@ package com.android.credentialmanager 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 @@ class CredentialSelectorActivity : ComponentActivity() { 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 @@ class CredentialSelectorActivity : ComponentActivity() { 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 e8d3b1f45be3..108f4945c9e9 100644 --- a/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt +++ b/packages/CredentialManager/src/com/android/credentialmanager/DataConverter.kt @@ -198,7 +198,8 @@ class GetFlowUtils { it.type, it.credentialRetrievalData, it.credentialRetrievalData, - it.isSystemProviderRequired + it.isSystemProviderRequired, + it.allowedProviders, ) if (credentialOptionJetpack is GetPublicKeyCredentialOption) { credentialOptionJetpack.preferImmediatelyAvailableCredentials @@ -462,7 +463,8 @@ class CreateFlowUtils { 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 c2aaeace1af6..776bf2b761ce 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 2c4b4786c968..b265a425d2e7 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/DynamicSystemInstallationService.java @@ -19,6 +19,7 @@ package com.android.dynsystem; 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_ERROR_IO; 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 @@ public class DynamicSystemInstallationService extends Service 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 @@ public class DynamicSystemInstallationService extends Service // 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 @@ public class DynamicSystemInstallationService extends Service 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 @@ public class DynamicSystemInstallationService extends Service 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 @@ public class DynamicSystemInstallationService extends Service 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 @@ public class DynamicSystemInstallationService extends Service 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 @@ public class DynamicSystemInstallationService extends Service } } + 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 a41399fb0d0d..42b620abe734 100644 --- a/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java +++ b/packages/DynamicSystemInstallationService/src/com/android/dynsystem/InstallationAsyncTask.java @@ -803,7 +803,7 @@ class InstallationAsyncTask extends AsyncTask<String, Long, Throwable> { return mIsCompleted; } - boolean commit() { - return mDynSystem.setEnable(true, true); + boolean commit(boolean oneShot) { + return mDynSystem.setEnable(true, oneShot); } } diff --git a/packages/PrintSpooler/tests/outofprocess/Android.bp b/packages/PrintSpooler/tests/outofprocess/Android.bp index 69a1d7fa59e4..ef0d122c7273 100644 --- a/packages/PrintSpooler/tests/outofprocess/Android.bp +++ b/packages/PrintSpooler/tests/outofprocess/Android.bp @@ -31,7 +31,7 @@ android_test { 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 132545b2d5d2..1509b7077046 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.AddPrintersActivity; 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 1b3020af3d78..81340f561bc1 100644 --- a/packages/SettingsLib/OWNERS +++ b/packages/SettingsLib/OWNERS @@ -3,9 +3,8 @@ dsandler@android.com 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 a7e95b58a6e4..eaf3229bf0c8 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.PluginLifecycleManager 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 @@ open class ClockRegistry( 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 0326b6d3edca..5ba0ad62e9fb 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitch.java @@ -294,11 +294,11 @@ public class KeyguardClockSwitch extends RelativeLayout { 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 25d17928351a..ad333b7bfbb6 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardClockSwitchController.java @@ -493,7 +493,7 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS if (mDateWeatherView != null) { mUiExecutor.execute(() -> { mDateWeatherView.setVisibility( - clockHasCustomWeatherDataDisplay() ? View.GONE : View.VISIBLE); + clockHasCustomWeatherDataDisplay() ? View.INVISIBLE : View.VISIBLE); }); } } @@ -519,9 +519,10 @@ public class KeyguardClockSwitchController extends ViewController<KeyguardClockS @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 33a822440866..cc6438913667 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1259,6 +1259,9 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * 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 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab * 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 6e98a1805d62..cde8ff78c620 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 com.android.systemui.shared.clocks.DefaultClockProvider; 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 ffacf01347e0..f01749a6c031 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 @@ object Flags { /** 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 c4fc8834df83..5f6098b8758f 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.dagger.qualifiers.Main 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 @@ constructor( 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 @@ constructor( 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 4cc041074a8d..fa4211487d1d 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.service.notification.StatusBarNotification 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 @@ class MediaDataManager( 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 @@ class MediaDataManager( 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 cb1f12cf412f..40027a1d8f2c 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.Process; 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.animation.TransitionLayout; 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 java.util.concurrent.Executor; import javax.inject.Inject; -import dagger.Lazy; -import kotlin.Triple; -import kotlin.Unit; - /** * A view controller used for Media Playback. */ @@ -1000,18 +1002,9 @@ public class MediaControlPanel { 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 e95106e0987a..0239d367e52d 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.ApplicationInfo; 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 @@ public class MediaDataUtils { } 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 cffe45fadaa3..0a188e0e7d0b 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.Context; 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 dagger.Lazy; 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis 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; } - Log.d(TAG_OPS, "launcherStateChanged intent: " + intent + extraComponentList); - updateEnabledState(); - // Reconnect immediately, instead of waiting for resume to arrive. - startConnectionToCurrentUser(); + // 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; + } + } } }; @@ -621,8 +633,7 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis screenLifecycle.addObserver(mScreenLifecycleObserver); wakefulnessLifecycle.addObserver(mWakefulnessLifecycleObserver); // Connect to the service - updateEnabledState(); - startConnectionToCurrentUser(); + updateEnabledAndBinding(); // Listen for assistant changes assistUtils.registerVoiceInteractionSessionListener(mVoiceInteractionSessionListener); @@ -644,6 +655,10 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis 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 @@ public class OverviewProxyService implements CallbackController<OverviewProxyLis } } + 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 222a0f4fa248..a4a7d4cd76e5 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.fragments.FragmentService; 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump DumpManager dumpManager, KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor, - ActivityStarter activityStarter) { + ActivityStarter activityStarter, + KeyguardFaceAuthInteractor keyguardFaceAuthInteractor) { mInteractionJankMonitor = interactionJankMonitor; keyguardStateController.addCallback(new KeyguardStateController.Callback() { @Override @@ -891,6 +894,7 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump 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 82bd45ce2279..6322edf5a1b4 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 androidx.annotation.VisibleForTesting 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.HeadsUpManager 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 @@ constructor( 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 @@ constructor( } 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 @@ constructor( } 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 @@ constructor( 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 eaa145582ba3..b3d246164e87 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.data.repository.UserSetupR 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.Module 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 @@ abstract class StatusBarPipelineModule { @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 @@ abstract class StatusBarPipelineModule { 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 9af5e836659f..40b8c90fb9f5 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.CoroutineScope 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 @@ constructor( } .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 @@ constructor( ) } + 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 9e8c814ca2a9..e819c4fc96ac 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 com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWi 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 @@ object WifiViewBinder { 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 @@ object WifiViewBinder { } } + 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 c9a0786acc72..d9c214452ef1 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.log.table.logDiffsForTable 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.domain.interactor.WifiIntera 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 @@ import kotlinx.coroutines.flow.stateIn /** * 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 @@ constructor( 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 eccf02397a82..617e19200a0a 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 @@ interface WifiViewModelCommon { /** 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 b9f8dd945293..fc906dedfa1d 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 @@ public class KeyguardClockSwitchControllerTest extends SysuiTestCase { 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 f21ea3dbed6d..fc75d47c01b7 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.FakeFeatureFlags 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 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { 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 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { 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 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { alternateBouncerInteractor, faceDetectBuffer, faceAuthBuffer, + keyguardTransitionInteractor, dumpManager, ) } @@ -772,6 +780,50 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() { } } + @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 000000000000..86f3062bca35 --- /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 4bfd6a2e28f4..068d933652ac 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 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { protected ArgumentCaptor<NotificationStackScrollLayout.OnEmptySpaceClickListener> mEmptySpaceClickListenerCaptor; @Mock protected ActivityStarter mActivityStarter; + @Mock protected KeyguardFaceAuthInteractor mKeyguardFaceAuthInteractor; protected KeyguardBottomAreaInteractor mKeyguardBottomAreaInteractor; protected KeyguardInteractor mKeyguardInteractor; @@ -600,7 +601,8 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mDumpManager, mKeyuardLongPressViewModel, mKeyguardInteractor, - mActivityStarter); + mActivityStarter, + mKeyguardFaceAuthInteractor); mNotificationPanelViewController.initDependencies( mCentralSurfaces, null, @@ -667,7 +669,7 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { 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 2db9c9788bf8..600fb5c2e1bc 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 @@ public class NotificationPanelViewControllerTest extends NotificationPanelViewCo 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 8109e24a1e52..c2a2a40b7e5d 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 androidx.test.filters.SmallTest 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 @@ class KeyguardCoordinatorTest : SysuiTestCase() { 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 @@ class KeyguardCoordinatorTest : SysuiTestCase() { } @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 @@ class KeyguardCoordinatorTest : SysuiTestCase() { 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 3ced7b2c4e6e..b2bbcfd3d6ef 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 @@ class FakeMobileIconsInteractor( 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 @@ class FakeMobileIconsInteractor( /** 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 01bec879102d..f8e1aa94c387 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.data.repository.FakeAirp 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 @@ class MobileIconsViewModelTest : SysuiTestCase() { 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 5c19108cc17e..0d51af2754f0 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.android.systemui.util.mockito.whenever 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 @@ class ModernStatusBarWifiViewTest : SysuiTestCase() { 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 ffe990bf1cf6..e6724d86ec55 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.log.table.TableLogBuffer 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 com.google.common.truth.Truth.assertThat 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 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase 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 @@ internal class WifiViewModelIconParameterizedTest(private val testCase: TestCase 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 802e360797a4..0e303b244094 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.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.LocationBasedWi 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.Mockito.`when` as whenever import org.mockito.MockitoAnnotations @Suppress("EXPERIMENTAL_IS_NOT_ENABLED") -@OptIn(ExperimentalCoroutinesApi::class) @SmallTest class WifiViewModelTest : SysuiTestCase() { @@ -68,6 +67,7 @@ class WifiViewModelTest : SysuiTestCase() { 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 @@ class WifiViewModelTest : SysuiTestCase() { 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 @@ class WifiViewModelTest : SysuiTestCase() { 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 0b019d1285e3..30418883eaf8 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 @@ class UnfoldRemoteFilter( 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 1990fe277af9..98aebddddac9 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 @@ public class BackupAndRestoreFeatureFlags { /* 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 18e28de75782..1656b6f0ab9b 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.AppWidgetBackupBridge; 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { executeNextState(UnifiedRestoreState.RUNNING_QUEUE); } + @VisibleForTesting void executeNextState(UnifiedRestoreState nextState) { if (MORE_DEBUG) { Slog.i(TAG, " => executing next step on " @@ -1369,6 +1378,26 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask { 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 291c05877c17..96446422dd85 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 @@ public class VirtualDeviceManagerService extends SystemService { } 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 @@ public class VirtualDeviceManagerService extends SystemService { 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 @@ public class VirtualDeviceManagerService extends SystemService { 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 @@ public class VirtualDeviceManagerService extends SystemService { 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 @@ public class VirtualDeviceManagerService extends SystemService { 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 @@ public class VirtualDeviceManagerService extends SystemService { 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 @@ public class VirtualDeviceManagerService extends SystemService { @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 @@ public class VirtualDeviceManagerService extends SystemService { @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 @@ public class VirtualDeviceManagerService extends SystemService { @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 c6f63dd73a25..12ee13183221 100644 --- a/services/core/java/android/os/BatteryStatsInternal.java +++ b/services/core/java/android/os/BatteryStatsInternal.java @@ -39,12 +39,14 @@ public abstract class BatteryStatsInternal { 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 3487613d313c..5156c541011f 100644 --- a/services/core/java/com/android/server/DockObserver.java +++ b/services/core/java/com/android/server/DockObserver.java @@ -70,7 +70,7 @@ final class DockObserver extends SystemService { private boolean mUpdatesStopped; - private final boolean mKeepDreamingWhenUndocking; + private final boolean mKeepDreamingWhenUnplugging; private final boolean mAllowTheaterModeWakeFromDock; private final List<ExtconStateConfig> mExtconStateConfigs; @@ -167,8 +167,8 @@ final class DockObserver extends SystemService { 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 @@ final class DockObserver extends SystemService { } 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 409f0541eed7..123cd3288343 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/net/OWNERS 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 e248007eca19..ee18ed566725 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } 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 @@ public final class ActiveServices { } 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 b32f8c973e6a..97d34b8dd718 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 @@ public class ActivityManagerService extends IActivityManager.Stub 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 @@ public class ActivityManagerService extends IActivityManager.Stub * {@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 @@ public class ActivityManagerService extends IActivityManager.Stub // 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 e080a80e1c37..17a0d62c27b3 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 @@ final class ActivityManagerShellCommand extends ShellCommand { 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 ed297d0867a1..0744f751354d 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.BatteryExternalStatsWorker; 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 @@ public final class BatteryStatsService extends IBatteryStats.Stub @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 8ad76cb668bf..1426cfd65286 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { 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 @@ public final class CachedAppOptimizer { @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 @@ public final class CachedAppOptimizer { } } 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 @@ public final class CachedAppOptimizer { } @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 @@ public final class CachedAppOptimizer { @GuardedBy({"mAm", "mProcLock"}) void unfreezeAppLSP(ProcessRecord app, @UnfreezeReason int reason) { synchronized (mFreezerLock) { - unfreezeAppInternalLSP(app, reason); + unfreezeAppInternalLSP(app, reason, false); } } @@ -2080,15 +2104,6 @@ public final class CachedAppOptimizer { 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 f2331072ce51..e8c8f6dd5462 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 @@ final class ProcessCachedOptimizerRecord { 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 @@ final class ProcessCachedOptimizerRecord { 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 edf0dbd65ef2..a875860f016f 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN // 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 @@ final class ServiceRecord extends Binder implements ComponentName.WithComponentN } 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 ac55f28a8ab0..d1cbbfcba2e2 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 @@ public class AudioService extends IAudioService.Stub 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 7802b9d24de9..0e26d4661017 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 @@ public final class DreamManagerService extends SystemService { 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 @@ public final class DreamManagerService extends SystemService { 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 @@ public final class DreamManagerService extends SystemService { 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 @@ public final class DreamManagerService extends SystemService { } } - 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 @@ public final class DreamManagerService extends SystemService { } 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 @@ public final class DreamManagerService extends SystemService { } } - private boolean shouldKeepDreamingWhenUndocked() { - return mKeepDreamingWhenUndockedDefault && mSystemDreamComponent == null; + private boolean shouldKeepDreamingWhenUnplugging() { + return mKeepDreamingWhenUnpluggingDefault && mSystemDreamComponent == null; } private ComponentName getDefaultDreamComponentForUser(int userId) { @@ -1057,7 +1056,7 @@ public final class DreamManagerService extends SystemService { 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 00cd700541c0..6e5eb5631112 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 1301cd476c26..ebcbfed93ac7 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 @@ public class NotificationManagerService extends SystemService { } 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 73be5490e0e1..5f8efe29459d 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 @@ public class UserManagerService extends IUserManager.Stub { 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 2e8a150f2b6d..e392c24026a7 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 @@ public final class PowerManagerService extends SystemService // 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 @@ public final class PowerManagerService extends SystemService 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 @@ public final class PowerManagerService extends SystemService + 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 32f1bcfaad76..546dc819b4e6 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 @@ public final class ShutdownCheckPoints { @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 c2d4ac694c39..b1430e7138e7 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 @@ public final class ShutdownThread extends Thread { // 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 index 231ffc6464c1..1d63489f3c4f 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 @@ public class CpuWakeupStats { 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 @@ public class CpuWakeupStats { 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 @@ public class CpuWakeupStats { } } 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 @@ public class CpuWakeupStats { 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 @@ public class CpuWakeupStats { 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 @@ public class CpuWakeupStats { return null; } + int type = TYPE_IRQ; int parsedDeviceCount = 0; final IrqDevice[] parsedDevices = new IrqDevice[components.length]; @@ -574,6 +609,10 @@ public class CpuWakeupStats { 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 @@ public class CpuWakeupStats { 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 index 091d18e30ccc..8644f720c661 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 7718dd88b772..8bbcd2787931 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 android.window.TransitionInfo; 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 @@ class ActivityClientController extends IActivityClientController.Stub { // 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 0b98495c8e99..64e1ae5b8c9e 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A 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 e780716dd06c..064af0f3165e 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.notification.SystemNotificationChannels; 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { 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 c6db8a7acb6f..8660becf56a9 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 @@ class Dimmer { * {@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 26f56a2e5c0b..9f59f5a30caf 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 @@ public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> { @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 ad934541267e..cd4b3c565a41 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 @@ class RootWindowContainer extends WindowContainer<DisplayContent> // 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 89f975387667..9363eb5cefc6 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 android.window.WindowContainerToken; 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 @@ class Task extends TaskFragment { @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 @@ class Task extends TaskFragment { 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 1d232fe99e3c..311b9a6d2876 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 @@ class TaskFragment extends WindowContainer<WindowContainer> { 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 652c2977f40e..76b0e7b82ba6 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 @@ class Transition implements BLASTSyncEngine.TransactionReadyListener { 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 4c1c2ff1ac10..cbb4fe2eaa21 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 @@ class TransitionController { 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 f63470f2bea4..ee86b97e9404 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 @@ class WindowOrganizerController extends IWindowOrganizerController.Stub 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 a04143afadcd..5a718086d71c 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 @@ public final class CreateRequestSession extends RequestSession<CreateCredentialR 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 87509067f993..0dee7a44375d 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.IBinder; 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 class CredentialManagerUi { 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 class CredentialManagerUi { */ 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 208a4be9a70e..3dba4a929859 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 @@ public class GetRequestSession extends RequestSession<GetCredentialRequest, 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 c1e9bc6a5c3c..0ad73c945284 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.IBeginGetCredentialCallback; 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 class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr */ 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 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr @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 class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr */ 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 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr 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 class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr */ 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 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr 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 @@ public class RemoteCredentialService extends ServiceConnector.Impl<ICredentialPr 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 8fd02691e190..15a30e427688 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 @@ package com.android.server.credentials; 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 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential private final Set<ComponentName> mEnabledProviders; + protected PendingIntent mPendingIntent; + @NonNull protected RequestSessionStatus mRequestSessionStatus = RequestSessionStatus.IN_PROGRESS; @@ -202,11 +205,23 @@ abstract class RequestSession<T, U, V> implements CredentialManagerUi.Credential 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 926c7e400144..3d5686dfe66e 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 @@ final class DevicePolicyEngine { 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 @@ final class DevicePolicyEngine { } } + /** + * 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 @@ final class DevicePolicyEngine { } /** - * 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 @@ final class DevicePolicyEngine { 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 @@ final class DevicePolicyEngine { */ 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 @@ final class DevicePolicyEngine { } @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 @@ final class DevicePolicyEngine { } } - // 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 29f9a306880f..c2766c200d2c 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { // 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } for (Integer userId : deletedUsers) { removeUserData(userId); + if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { + mDevicePolicyEngine.handleUserRemoved(userId); + } } } @@ -4153,6 +4159,11 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { mInjector.binderWithCleanCallingIdentity(() -> removeActiveAdminLocked(adminReceiver, userHandle)); + if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { + mDevicePolicyEngine.removePoliciesForAdmin( + EnforcingAdmin.createEnterpriseEnforcingAdmin( + adminReceiver, userHandle, admin)); + } } } @@ -9992,6 +10003,12 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { toggleBackupServiceActive(userId, true); applyProfileRestrictionsIfDeviceOwnerLocked(); setNetworkLoggingActiveInternal(false); + + if (isPolicyEngineForFinanceFlagEnabled() || isPermissionCheckFlagEnabled()) { + mDevicePolicyEngine.removePoliciesForAdmin( + EnforcingAdmin.createEnterpriseEnforcingAdmin( + admin.info.getComponent(), userId, admin)); + } } @Override @@ -13264,6 +13287,9 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ? 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { /* 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { userId); } } - private void setGlobalUserRestrictionInternal( EnforcingAdmin admin, String key, boolean enabled) { PolicyDefinition<Boolean> policyDefinition = @@ -16088,6 +16112,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { } 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) { - result = new Bundle(); - result.putInt(Intent.EXTRA_USER_ID, enforcingUserId); - 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) { + 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, deviceOwner.first); + result.putInt(Intent.EXTRA_USER_ID, admin.getUserHandle().getIdentifier()); result.putParcelable(DevicePolicyManager.EXTRA_DEVICE_ADMIN, - deviceOwner.second); + admin.info.getComponent()); return result; } } else if (sourceType == UserManager.RESTRICTION_SOURCE_SYSTEM) { @@ -22549,53 +22561,53 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { ); /** - * 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 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { 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/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BroadcastQueueModernImplTest.java index bc3f5ab5c42b..3fb7fb4e6aea 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.BundleMerger; 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.io.Writer; 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 @@ public final class BroadcastQueueModernImplTest { @Mock BroadcastProcessQueue mQueue4; HandlerThread mHandlerThread; + TestLooperManager mLooper; BroadcastConstants mConstants; BroadcastQueueModernImpl mImpl; @@ -127,6 +130,10 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { .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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 @@ public final class BroadcastQueueModernImplTest { 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 90e6a2d0f34a..7be1d7cde27f 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.HandlerThread; 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 @@ public class BroadcastQueueTest { private Context mContext; private HandlerThread mHandlerThread; + private TestLooperManager mLooper; private AtomicInteger mNextPid; @Mock @@ -206,6 +208,11 @@ public class BroadcastQueueTest { 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 @@ public class BroadcastQueueTest { } } - /** - * 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 @@ public class BroadcastQueueTest { } } + /** + * 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 @@ public class BroadcastQueueTest { 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))))); - - 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); - } + 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); + + // 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 @@ public class BroadcastQueueTest { 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))))); - - synchronized (mAms) { - mQueue.cleanupDisabledPackageReceiversLocked(null, null, 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); } waitForIdle(); @@ -1240,15 +1231,12 @@ public class BroadcastQueueTest { 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 @@ public class BroadcastQueueTest { 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 @@ public class BroadcastQueueTest { 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 @@ public class BroadcastQueueTest { 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 @@ public class BroadcastQueueTest { 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(); - - 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; - - assertFalse(mQueue.isIdleLocked()); - assertTrue(mQueue.isBeyondBarrierLocked(beforeFirst)); - assertFalse(mQueue.isBeyondBarrierLocked(afterFirst)); - assertFalse(mQueue.isBeyondBarrierLocked(afterSecond)); - } + 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)); + + 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)); + + mLooper.release(); mQueue.waitForBarrier(LOG_WRITER_INFO); assertTrue(mQueue.isBeyondBarrierLocked(afterFirst)); @@ -2048,25 +2029,23 @@ public class BroadcastQueueTest { 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))); - - 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()); - } + 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()); 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 201da359aeb6..9f685b479d47 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 @@ public class BackupAndRestoreFeatureFlagsTest { 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 017c93975286..c84797febcfd 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.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.when; -import androidx.test.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - 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 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 @@ public class PerformUnifiedRestoreTaskTest { 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 @@ public class PerformUnifiedRestoreTaskTest { 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 @@ public class PerformUnifiedRestoreTaskTest { } }); - mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService); + mRestoreTask = new PerformUnifiedRestoreTask(mBackupManagerService, mTransportConnection); } private void populateTestData() { @@ -179,4 +205,64 @@ public class PerformUnifiedRestoreTaskTest { 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 45fefe477f8a..ca857f121624 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.spy; 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.Handler; 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 com.android.server.testutils.OffsettableClock; 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 @@ public final class DisplayPowerController2Test { 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 @@ public final class DisplayPowerController2Test { @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()); + + // 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); - when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock); - when(mContextSpy.getResources()).thenReturn(mResourcesMock); + 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 @@ public final class DisplayPowerController2Test { @After public void tearDown() { - mSession.finishMocking(); LocalServices.removeServiceForTest(WindowManagerPolicy.class); + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); } @Test @@ -421,11 +421,9 @@ public final class DisplayPowerController2Test { @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 @@ public final class DisplayPowerController2Test { 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 @@ public final class DisplayPowerController2Test { @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 @@ public final class DisplayPowerController2Test { @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 @@ public final class DisplayPowerController2Test { @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 @@ public final class DisplayPowerController2Test { @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 final class DisplayPowerController2Test { 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 @@ public final class DisplayPowerController2Test { any(HysteresisLevels.class), any(HysteresisLevels.class), any(HysteresisLevels.class), - eq(mContextSpy), + eq(mContext), any(HighBrightnessModeController.class), any(BrightnessThrottler.class), isNull(), @@ -862,7 +848,7 @@ public final class DisplayPowerController2Test { 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 d9133a441aff..0b97c5cb6ca1 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.spy; 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.Handler; 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 com.android.server.testutils.OffsettableClock; 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 @@ public final class DisplayPowerControllerTest { 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 @@ public final class DisplayPowerControllerTest { @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()); + // 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); - when(mContextSpy.getSystemService(eq(PowerManager.class))).thenReturn(mPowerManagerMock); - when(mContextSpy.getResources()).thenReturn(mResourcesMock); + 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 @@ public final class DisplayPowerControllerTest { @After public void tearDown() { - mSession.finishMocking(); LocalServices.removeServiceForTest(WindowManagerPolicy.class); + LocalServices.removeServiceForTest(ColorDisplayService.ColorDisplayServiceInternal.class); } @Test @@ -425,11 +425,9 @@ public final class DisplayPowerControllerTest { @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 @@ public final class DisplayPowerControllerTest { @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 @@ public final class DisplayPowerControllerTest { @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 @@ public final class DisplayPowerControllerTest { @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 @@ public final class DisplayPowerControllerTest { @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 final class DisplayPowerControllerTest { 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 @@ public final class DisplayPowerControllerTest { any(HysteresisLevels.class), any(HysteresisLevels.class), any(HysteresisLevels.class), - eq(mContextSpy), + eq(mContext), any(HighBrightnessModeController.class), any(BrightnessThrottler.class), isNull(), @@ -866,7 +854,7 @@ public final class DisplayPowerControllerTest { 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 df2f59a1cefc..e24354f26d8b 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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { 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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { 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 @@ public class JobNotificationCoordinatorTest { 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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { 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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { .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 @@ public class JobNotificationCoordinatorTest { 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 02fdfadb2d8a..754f409b3966 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 @@ public class ThermalStatusRestrictionTest { 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 1d2a7d37d613..7e2529aa0090 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 120ddf6af7de..b539a76b521c 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 @@ public class DevicePolicyManagerServiceMigrationTest extends DpmTestBase { // 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 dd81abeec5d3..16aadacb5af6 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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetUserRestriction_asPo() { setAsProfileOwner(admin1); @@ -2259,6 +2262,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { ); @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testCreateAdminSupportIntent() throws Exception { // Setup device owner. mContext.binder.callingUid = DpmMockContext.CALLER_SYSTEM_USER_UID; @@ -4993,6 +4999,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testWipeDataManagedProfileOnOrganizationOwnedDevice() throws Exception { setupProfileOwner(); configureProfileOwnerOfOrgOwnedDevice(admin1, CALLER_USER_HANDLE); @@ -7023,6 +7030,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetUserControlDisabledPackages_asDO() throws Exception { final List<String> testPackages = new ArrayList<>(); testPackages.add("package_1"); @@ -7038,6 +7046,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetUserControlDisabledPackages_asPO() { final List<String> testPackages = new ArrayList<>(); testPackages.add("package_1"); @@ -7776,6 +7785,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetUserRestriction_financeDo_validRestrictions_setsRestriction() throws Exception { setDeviceOwner(); @@ -7858,6 +7868,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @Test + @Ignore("b/277916462") public void testSetUninstallBlocked_financeDo_success() throws Exception { String packageName = "com.android.foo.package"; setDeviceOwner(); @@ -7871,6 +7882,7 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 @@ public class DevicePolicyManagerTest extends DpmTestBase { } @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 a0fb3deeb131..21a11bc0e49e 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 @@ public class PowerManagerServiceTest { 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 @@ public class PowerManagerServiceTest { 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 @@ public class PowerManagerServiceTest { 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 @@ public class PowerManagerServiceTest { 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 @@ public class PowerManagerServiceTest { } @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 2230ddd8a4a7..2bde51b4eb57 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 @@ public class ShutdownCheckPointsTest { } @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" - + ".testCallerProcessBinderEntry\n" - + "From process process_name (pid=1)\n\n", + + ".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" + + ".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 index 7cf5bc88c213..dca67d6da7ad 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.InstrumentationRegistry; 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 @@ import java.util.concurrent.ThreadLocalRandom; @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 @@ public class CpuWakeupStatsTest { } @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 @@ public class CpuWakeupStatsTest { } @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); + } + + @Test + public void abortIgnored() { + final CpuWakeupStats obj = new CpuWakeupStats(sContext, R.xml.irq_device_map_3, mHandler); - wakeupTime = 883124; + 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 index 43d9e60c4a2b..47a8f49e7025 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 000000000000..bc94dacea324 --- /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 39561f74d7ed..3b7bc952639e 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 @@ class TestUtil { 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 2696d2bf58b9..f12b53acd06b 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 000000000000..41bfc806f839 --- /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 000000000000..01041932567c --- /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 d2d8f1ad7a71..6223b2e8ace4 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 @@ class SoundTriggerModule implements IBinder.DeathRecipient, ISoundTriggerHal.Glo */ 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 a1adee732701..486945d9159e 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 @@ final class HotwordDetectionConnection { 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 @@ final class HotwordDetectionConnection { new Intent(VisualQueryDetectionService.SERVICE_INTERFACE); visualQueryDetectionServiceIntent.setComponent(mVisualQueryDetectionComponentName); - initAudioFlingerLocked(); + initAudioFlinger(); mHotwordDetectionServiceConnectionFactory = new ServiceConnectionFactory(hotwordDetectionServiceIntent, @@ -227,31 +228,41 @@ final class HotwordDetectionConnection { } } - 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 e8a8576daf58..a559b32f0021 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 @@ public final class PointingInfo implements Parcelable { /** * @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 00928904a4c4..6856cc0c5df2 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 @@ public final class SatelliteCapabilities implements Parcelable { /** * @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 44f56f3d315c..d3cb8a07e4ba 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 @@ public final class SatelliteDatagram implements Parcelable { /** * @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 d0409bf5df49..b2dec71ecb32 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 @@ public interface SatelliteDatagramCallback { * 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 5681ab266c17..c5830b8249c8 100644 --- a/telephony/java/android/telephony/satellite/SatelliteManager.java +++ b/telephony/java/android/telephony/satellite/SatelliteManager.java @@ -23,7 +23,6 @@ import android.annotation.NonNull; 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.SubscriptionManager; 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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * {@link #requestIsSatelliteEnabled(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_ENABLED = "satellite_enabled"; /** @@ -137,7 +136,7 @@ public class SatelliteManager { * {@link #requestIsDemoModeEnabled(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_DEMO_MODE_ENABLED = "demo_mode_enabled"; /** @@ -145,7 +144,7 @@ public class SatelliteManager { * {@link #requestIsSatelliteSupported(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_SUPPORTED = "satellite_supported"; /** @@ -153,7 +152,7 @@ public class SatelliteManager { * {@link #requestSatelliteCapabilities(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_CAPABILITIES = "satellite_capabilities"; /** @@ -161,7 +160,7 @@ public class SatelliteManager { * {@link #requestIsSatelliteProvisioned(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_PROVISIONED = "satellite_provisioned"; /** @@ -169,7 +168,7 @@ public class SatelliteManager { * {@link #requestIsSatelliteCommunicationAllowedForCurrentLocation(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_COMMUNICATION_ALLOWED = "satellite_communication_allowed"; @@ -178,7 +177,7 @@ public class SatelliteManager { * {@link #requestTimeForNextSatelliteVisibility(Executor, OutcomeReceiver)}. * @hide */ - @UnsupportedAppUsage + public static final String KEY_SATELLITE_NEXT_VISIBILITY = "satellite_next_visibility"; /** @@ -389,7 +388,7 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 @@ public class SatelliteManager { * @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 20875af94f5a..a62eb8b8a5fb 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 @@ public interface SatelliteProvisionStateCallback { * @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 3312e3a90281..d9ecaa3467e3 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 @@ public interface SatelliteStateCallback { * 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 17fff4480679..d4fe57a0be2e 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 @@ public interface SatelliteTransmissionUpdateCallback { * * @param pointingInfo The pointing info containing the satellite location. */ - @UnsupportedAppUsage void onSatellitePositionChanged(@NonNull PointingInfo pointingInfo); /** @@ -41,7 +39,6 @@ public interface SatelliteTransmissionUpdateCallback { * @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 @@ public interface SatelliteTransmissionUpdateCallback { * @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 000000000000..6b924f335ef7 --- /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 2fd236895467..8380dcf4b4a4 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.MotionEvent; 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.RemoveSpaceGesture; 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.Random; 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 @@ public class HandwritingIme extends InputMethodService { 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 class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { 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 @@ public class HandwritingIme extends InputMethodService { Log.d(TAG, "onPrepareStylusHandwriting "); if (mInk == null) { mInk = new InkView(this, new HandwritingFinisherImpl(), new StylusConsumer()); + mInk.setBoundsInfoMode(mBoundsInfoMode); } } @@ -323,4 +388,16 @@ public class HandwritingIme extends InputMethodService { 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 e94c79ecca00..86b324cf08d9 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.View; 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 @@ class InkView extends View { 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 @@ class InkView extends View { 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 @@ class InkView extends View { 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 a4c48fd5f342..4fa6fbe1d4e1 100644 --- a/tests/Input/Android.bp +++ b/tests/Input/Android.bp @@ -25,7 +25,7 @@ android_test { "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 0246426c1d33..d185ee6ae116 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.IInputConstants.UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLI 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 ed86aa5f90ea..8542f885d645 100644 --- a/tests/WindowAnimationJank/Android.bp +++ b/tests/WindowAnimationJank/Android.bp @@ -25,7 +25,7 @@ android_test { 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 25314644ca7e..48a359c4d0c6 100644 --- a/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java +++ b/tests/WindowAnimationJank/src/android/windowanimationjank/Utils.java @@ -18,11 +18,12 @@ import android.app.UiAutomation; 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 a8ace162c4d0..cb7c5112cba7 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 c6f18fd453c5..b850cb8e6dfd 100644 --- a/tests/testables/src/android/testing/TestableSettingsProvider.java +++ b/tests/testables/src/android/testing/TestableSettingsProvider.java @@ -72,7 +72,11 @@ public class TestableSettingsProvider extends MockContentProvider { 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]; |