summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobInfo.java5
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobScheduler.java4
-rw-r--r--apex/jobscheduler/framework/java/android/app/job/JobService.java9
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java32
-rw-r--r--apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java28
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java10
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java52
-rw-r--r--apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING12
-rw-r--r--core/api/test-current.txt2
-rw-r--r--core/java/android/app/LocaleConfig.java2
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java11
-rw-r--r--core/java/android/net/Uri.java39
-rw-r--r--core/java/android/view/IWindowManager.aidl12
-rw-r--r--core/java/android/view/View.java37
-rw-r--r--core/java/android/view/ViewConfiguration.java16
-rw-r--r--core/java/android/view/ViewRootImpl.java46
-rw-r--r--core/java/android/view/ViewRootRectTracker.java33
-rw-r--r--core/java/com/android/internal/jank/InteractionJankMonitor.java8
-rw-r--r--core/java/com/android/internal/os/BatteryUsageStatsStore.java15
-rw-r--r--core/proto/android/server/jobscheduler.proto1
-rw-r--r--core/res/res/color/hint_foreground_material_dark.xml6
-rw-r--r--core/res/res/color/hint_foreground_material_light.xml6
-rw-r--r--core/res/res/layout/log_access_user_consent_dialog_permission.xml58
-rw-r--r--core/res/res/layout/search_view.xml4
-rw-r--r--core/res/res/values-television/styles.xml21
-rw-r--r--core/res/res/values/colors_material.xml8
-rw-r--r--core/res/res/values/config.xml5
-rw-r--r--core/res/res/values/styles.xml14
-rw-r--r--core/res/res/values/symbols.xml2
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java8
-rw-r--r--core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java20
-rw-r--r--media/java/android/media/projection/IMediaProjectionManager.aidl12
-rw-r--r--media/java/android/media/projection/MediaProjection.java15
-rw-r--r--packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml2
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java33
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java6
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt74
-rw-r--r--packages/SettingsLib/tests/robotests/Android.bp1
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java3
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java144
-rw-r--r--packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowInteractionJankMonitor.java41
-rw-r--r--packages/SystemUI/TEST_MAPPING44
-rw-r--r--packages/SystemUI/res/layout/clipboard_edit_text_activity.xml1
-rw-r--r--packages/SystemUI/res/values/strings.xml2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt27
-rw-r--r--packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt14
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/power/PowerUI.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt11
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java65
-rw-r--r--services/core/java/com/android/server/VcnManagementService.java60
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java12
-rw-r--r--services/core/java/com/android/server/am/TEST_MAPPING26
-rw-r--r--services/core/java/com/android/server/content/ContentService.java26
-rw-r--r--services/core/java/com/android/server/input/InputManagerService.java243
-rw-r--r--services/core/java/com/android/server/location/LocationManagerService.java7
-rw-r--r--services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java24
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java21
-rw-r--r--services/core/java/com/android/server/wm/ContentRecorder.java3
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerInternal.java13
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java18
-rw-r--r--services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt61
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java4
-rw-r--r--services/usage/java/com/android/server/usage/TEST_MAPPING22
-rw-r--r--services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java60
-rw-r--r--tests/vcn/java/com/android/server/VcnManagementServiceTest.java100
72 files changed, 1233 insertions, 545 deletions
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 4710322db283..f49cdbf403f0 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
@@ -1777,10 +1777,7 @@ public class JobInfo implements Parcelable {
* {@link Build.VERSION_CODES#S}, but starting from Android version
* {@link Build.VERSION_CODES#TIRAMISU}, expedited jobs for the foreground app are
* guaranteed to be started before {@link JobScheduler#schedule(JobInfo)} returns (assuming
- * all requested constraints are satisfied), similar to foreground services. However, this
- * start guarantee means there is a higher chance of overlapping executions, as noted in
- * {@link JobService}, so be sure to handle that properly if you intend to reschedule the
- * job while it's actively running.
+ * all requested constraints are satisfied), similar to foreground services.
*
* @see JobInfo#isExpedited()
*/
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
index 388bbf1b26a0..632ecb2c0381 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java
@@ -107,9 +107,7 @@ public abstract class JobScheduler {
/**
* Schedule a job to be executed. Will replace any currently scheduled job with the same
* ID with the new information in the {@link JobInfo}. If a job with the given ID is currently
- * running, it will be stopped. Note that in some cases, the newly scheduled job may be started
- * before the previously running job has been fully stopped. See {@link JobService} for
- * additional details.
+ * running, it will be stopped.
*
* <p class="caution"><strong>Note:</strong> Scheduling a job can have a high cost, even if it's
* rescheduling the same job and the job didn't execute, especially on platform versions before
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobService.java b/apex/jobscheduler/framework/java/android/app/job/JobService.java
index 7ed4b62ae7e4..d184d44239ed 100644
--- a/apex/jobscheduler/framework/java/android/app/job/JobService.java
+++ b/apex/jobscheduler/framework/java/android/app/job/JobService.java
@@ -32,16 +32,11 @@ import android.os.IBinder;
* {@link #onStopJob(android.app.job.JobParameters)}, which is meant to inform you that the
* scheduling requirements are no longer being met.</p>
*
- * As a subclass of {@link Service}, there will only be one active instance of any JobService
+ * <p>As a subclass of {@link Service}, there will only be one active instance of any JobService
* subclasses, regardless of job ID. This means that if you schedule multiple jobs with different
* job IDs but using the same JobService class, that JobService may receive multiple calls to
* {@link #onStartJob(JobParameters)} and {@link #onStopJob(JobParameters)}, with each call being
- * for the separate jobs.
- *
- * <p class="note">Note that if you cancel and reschedule an already executing job,
- * there may be a small period of time where {@link #onStartJob(JobParameters)} has been called for
- * the newly scheduled job instance before {@link #onStopJob(JobParameters)} has been called or
- * fully processed for the old job.</p>
+ * for the separate jobs.</p>
*/
public abstract class JobService extends Service {
private static final String TAG = "JobService";
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 d5a7f2851d03..28116a8df180 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -717,17 +717,9 @@ class JobConcurrencyManager {
final boolean isTopEj = nextPending.shouldTreatAsExpeditedJob()
&& nextPending.lastEvaluatedBias == JobInfo.BIAS_TOP_APP;
- // Avoid overlapping job execution as much as possible.
- if (!isTopEj && isSimilarJobRunningLocked(nextPending)) {
- if (DEBUG) {
- Slog.w(TAG, "Delaying execution of job because of similarly running one: "
- + nextPending);
- }
- // It would be nice to let the JobService running the other similar job know about
- // this new job so that it doesn't unbind from the JobService and we can call
- // onStartJob as soon as the older job finishes.
- // TODO: optimize the job reschedule flow to reduce service binding churn
- continue;
+ if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
+ Slog.w(TAG, "Already running similar " + (isTopEj ? "TOP-EJ" : "job")
+ + " to: " + nextPending);
}
// Find an available slot for nextPending. The context should be one of the following:
@@ -1206,13 +1198,8 @@ class JobConcurrencyManager {
continue;
}
- // Avoid overlapping job execution as much as possible.
- if (isSimilarJobRunningLocked(nextPending)) {
- if (DEBUG) {
- Slog.w(TAG, "Avoiding execution of job because of similarly running one: "
- + nextPending);
- }
- continue;
+ if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
+ Slog.w(TAG, "Already running similar job to: " + nextPending);
}
if (worker.getPreferredUid() != nextPending.getUid()) {
@@ -1298,13 +1285,8 @@ class JobConcurrencyManager {
continue;
}
- // Avoid overlapping job execution as much as possible.
- if (isSimilarJobRunningLocked(nextPending)) {
- if (DEBUG) {
- Slog.w(TAG, "Avoiding execution of job because of similarly running one: "
- + nextPending);
- }
- continue;
+ if (DEBUG && isSimilarJobRunningLocked(nextPending)) {
+ Slog.w(TAG, "Already running similar job to: " + nextPending);
}
if (isPkgConcurrencyLimitedLocked(nextPending)) {
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 cd70e88b18aa..60afdc76d249 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -1208,22 +1208,12 @@ public class JobSchedulerService extends com.android.server.SystemService
// This may throw a SecurityException.
jobStatus.prepareLocked();
- final boolean canExecuteImmediately;
if (toCancel != null) {
// Implicitly replaces the existing job record with the new instance
- final boolean wasJobExecuting = cancelJobImplLocked(toCancel, jobStatus,
- JobParameters.STOP_REASON_CANCELLED_BY_APP,
- JobParameters.INTERNAL_STOP_REASON_CANCELED,
- "job rescheduled by app");
- // Avoid overlapping job executions. Don't push for immediate execution if an old
- // job with the same ID was running, but let TOP EJs start immediately.
- canExecuteImmediately = !wasJobExecuting
- || (jobStatus.isRequestedExpeditedJob()
- && mUidBiasOverride.get(jobStatus.getSourceUid(), JobInfo.BIAS_DEFAULT)
- == JobInfo.BIAS_TOP_APP);
+ cancelJobImplLocked(toCancel, jobStatus, JobParameters.STOP_REASON_CANCELLED_BY_APP,
+ JobParameters.INTERNAL_STOP_REASON_CANCELED, "job rescheduled by app");
} else {
startTrackingJobLocked(jobStatus, null);
- canExecuteImmediately = true;
}
if (work != null) {
@@ -1266,12 +1256,7 @@ public class JobSchedulerService extends com.android.server.SystemService
// list and try to run it.
mJobPackageTracker.notePending(jobStatus);
mPendingJobQueue.add(jobStatus);
- if (canExecuteImmediately) {
- // Don't ask the JobConcurrencyManager to try to run the job immediately. The
- // JobServiceContext will ask the JobConcurrencyManager for another job once
- // it finishes cleaning up the old job.
- maybeRunPendingJobsLocked();
- }
+ maybeRunPendingJobsLocked();
} else {
evaluateControllerStatesLocked(jobStatus);
}
@@ -1392,10 +1377,8 @@ public class JobSchedulerService extends com.android.server.SystemService
* is null, the cancelled job is removed outright from the system. If
* {@code incomingJob} is non-null, it replaces {@code cancelled} in the store of
* currently scheduled jobs.
- *
- * @return true if the cancelled job was running
*/
- private boolean cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
+ private void cancelJobImplLocked(JobStatus cancelled, JobStatus incomingJob,
@JobParameters.StopReason int reason, int internalReasonCode, String debugReason) {
if (DEBUG) Slog.d(TAG, "CANCEL: " + cancelled.toShortString());
cancelled.unprepareLocked();
@@ -1406,7 +1389,7 @@ public class JobSchedulerService extends com.android.server.SystemService
}
mChangedJobList.remove(cancelled);
// Cancel if running.
- boolean wasRunning = mConcurrencyManager.stopJobOnServiceContextLocked(
+ mConcurrencyManager.stopJobOnServiceContextLocked(
cancelled, reason, internalReasonCode, debugReason);
// If this is a replacement, bring in the new version of the job
if (incomingJob != null) {
@@ -1414,7 +1397,6 @@ public class JobSchedulerService extends com.android.server.SystemService
startTrackingJobLocked(incomingJob, cancelled);
}
reportActiveLocked();
- return wasRunning;
}
void updateUidState(int uid, int procState) {
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
index c90291e5f264..fb342b9ba9b3 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppIdleHistory.java
@@ -328,11 +328,13 @@ public class AppIdleHistory {
appUsageHistory.lastUsedScreenTime = getScreenOnTime(nowElapsedRealtimeMs);
}
- if (appUsageHistory.currentBucket > newBucket) {
- appUsageHistory.currentBucket = newBucket;
- logAppStandbyBucketChanged(packageName, userId, newBucket, bucketingReason);
+ if (appUsageHistory.currentBucket >= newBucket) {
+ if (appUsageHistory.currentBucket > newBucket) {
+ appUsageHistory.currentBucket = newBucket;
+ logAppStandbyBucketChanged(packageName, userId, newBucket, bucketingReason);
+ }
+ appUsageHistory.bucketingReason = bucketingReason;
}
- appUsageHistory.bucketingReason = bucketingReason;
return appUsageHistory;
}
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 1c4ec857538f..c9afdad8e40e 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -225,6 +225,11 @@ public class AppStandbyController
| PackageManager.MATCH_DISABLED_COMPONENTS
| PackageManager.MATCH_SYSTEM_ONLY;
+ private static final int NOTIFICATION_SEEN_PROMOTED_BUCKET_FOR_PRE_T_APPS =
+ STANDBY_BUCKET_WORKING_SET;
+ private static final long NOTIFICATION_SEEN_HOLD_DURATION_FOR_PRE_T_APPS =
+ COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
+
// To name the lock for stack traces
static class Lock {}
@@ -320,11 +325,17 @@ public class AppStandbyController
int mNotificationSeenPromotedBucket =
ConstantsObserver.DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET;
/**
- * If true, tell each {@link AppIdleStateChangeListener} to give quota bump for each
+ * If {@code true}, tell each {@link AppIdleStateChangeListener} to give quota bump for each
* notification seen event.
*/
private boolean mTriggerQuotaBumpOnNotificationSeen =
ConstantsObserver.DEFAULT_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN;
+ /**
+ * If {@code true}, we will retain the pre-T impact of notification signal on apps targeting
+ * pre-T sdk levels regardless of other flag changes.
+ */
+ boolean mRetainNotificationSeenImpactForPreTApps =
+ ConstantsObserver.DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS;
/** Minimum time a system update event should keep the buckets elevated. */
long mSystemUpdateUsageTimeoutMillis = ConstantsObserver.DEFAULT_SYSTEM_UPDATE_TIMEOUT;
/** Maximum time to wait for a prediction before using simple timeouts to downgrade buckets. */
@@ -1098,17 +1109,29 @@ public class AppStandbyController
final int subReason = usageEventToSubReason(eventType);
final int reason = REASON_MAIN_USAGE | subReason;
if (eventType == UsageEvents.Event.NOTIFICATION_SEEN) {
- if (mTriggerQuotaBumpOnNotificationSeen) {
- mHandler.obtainMessage(MSG_TRIGGER_LISTENER_QUOTA_BUMP, userId, -1, pkg)
- .sendToTarget();
+ final int notificationSeenPromotedBucket;
+ final long notificationSeenTimeoutMillis;
+ if (mRetainNotificationSeenImpactForPreTApps
+ && getTargetSdkVersion(pkg) < Build.VERSION_CODES.TIRAMISU) {
+ notificationSeenPromotedBucket =
+ NOTIFICATION_SEEN_PROMOTED_BUCKET_FOR_PRE_T_APPS;
+ notificationSeenTimeoutMillis =
+ NOTIFICATION_SEEN_HOLD_DURATION_FOR_PRE_T_APPS;
+ } else {
+ if (mTriggerQuotaBumpOnNotificationSeen) {
+ mHandler.obtainMessage(MSG_TRIGGER_LISTENER_QUOTA_BUMP, userId, -1, pkg)
+ .sendToTarget();
+ }
+ notificationSeenPromotedBucket = mNotificationSeenPromotedBucket;
+ notificationSeenTimeoutMillis = mNotificationSeenTimeoutMillis;
}
// Notification-seen elevates to a higher bucket (depending on
// {@link ConstantsObserver#KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET}) but doesn't
// change usage time.
mAppIdleHistory.reportUsage(appHistory, pkg, userId,
- mNotificationSeenPromotedBucket, subReason,
- 0, elapsedRealtime + mNotificationSeenTimeoutMillis);
- nextCheckDelay = mNotificationSeenTimeoutMillis;
+ notificationSeenPromotedBucket, subReason,
+ 0, elapsedRealtime + notificationSeenTimeoutMillis);
+ nextCheckDelay = notificationSeenTimeoutMillis;
} else if (eventType == UsageEvents.Event.SLICE_PINNED) {
// Mild usage elevates to WORKING_SET but doesn't change usage time.
mAppIdleHistory.reportUsage(appHistory, pkg, userId,
@@ -1149,6 +1172,10 @@ public class AppStandbyController
}
}
+ private int getTargetSdkVersion(String packageName) {
+ return mInjector.getPackageManagerInternal().getPackageTargetSdkVersion(packageName);
+ }
+
/**
* Returns the lowest standby bucket that is better than {@code targetBucket} and has an
* valid expiry time (i.e. the expiry time is not yet elapsed).
@@ -2226,6 +2253,9 @@ public class AppStandbyController
pw.print(" mTriggerQuotaBumpOnNotificationSeen=");
pw.print(mTriggerQuotaBumpOnNotificationSeen);
pw.println();
+ pw.print(" mRetainNotificationSeenImpactForPreTApps=");
+ pw.print(mRetainNotificationSeenImpactForPreTApps);
+ pw.println();
pw.print(" mSlicePinnedTimeoutMillis=");
TimeUtils.formatDuration(mSlicePinnedTimeoutMillis, pw);
pw.println();
@@ -2712,6 +2742,8 @@ public class AppStandbyController
"notification_seen_duration";
private static final String KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET =
"notification_seen_promoted_bucket";
+ private static final String KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS =
+ "retain_notification_seen_impact_for_pre_t_apps";
private static final String KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN =
"trigger_quota_bump_on_notification_seen";
private static final String KEY_SLICE_PINNED_HOLD_DURATION =
@@ -2773,6 +2805,7 @@ public class AppStandbyController
COMPRESS_TIME ? 12 * ONE_MINUTE : 12 * ONE_HOUR;
public static final int DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET =
STANDBY_BUCKET_WORKING_SET;
+ public static final boolean DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS = false;
public static final boolean DEFAULT_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN = false;
public static final long DEFAULT_SYSTEM_UPDATE_TIMEOUT =
COMPRESS_TIME ? 2 * ONE_MINUTE : 2 * ONE_HOUR;
@@ -2874,6 +2907,11 @@ public class AppStandbyController
KEY_NOTIFICATION_SEEN_PROMOTED_BUCKET,
DEFAULT_NOTIFICATION_SEEN_PROMOTED_BUCKET);
break;
+ case KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS:
+ mRetainNotificationSeenImpactForPreTApps = properties.getBoolean(
+ KEY_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS,
+ DEFAULT_RETAIN_NOTIFICATION_SEEN_IMPACT_FOR_PRE_T_APPS);
+ break;
case KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN:
mTriggerQuotaBumpOnNotificationSeen = properties.getBoolean(
KEY_TRIGGER_QUOTA_BUMP_ON_NOTIFICATION_SEEN,
diff --git a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
index e407e3126058..a6a3aafdb4f4 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
+++ b/apex/jobscheduler/service/java/com/android/server/usage/TEST_MAPPING
@@ -19,6 +19,18 @@
]
}
],
+ "presubmit-large": [
+ {
+ "name": "CtsUsageStatsTestCases",
+ "options": [
+ {"include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"},
+ {"exclude-annotation": "android.platform.test.annotations.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.FlakyTest"},
+ {"exclude-annotation": "androidx.test.filters.MediumTest"},
+ {"exclude-annotation": "androidx.test.filters.LargeTest"}
+ ]
+ }
+ ],
"postsubmit": [
{
"name": "CtsUsageStatsTestCases"
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index fe99c71d9a9a..48277fb3b488 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2923,7 +2923,7 @@ package android.view {
method public static int getHoverTooltipHideTimeout();
method public static int getHoverTooltipShowTimeout();
method public static int getLongPressTooltipHideTimeout();
- method public int getPreferKeepClearForFocusDelay();
+ method public boolean isPreferKeepClearForFocusEnabled();
}
public class ViewDebug {
diff --git a/core/java/android/app/LocaleConfig.java b/core/java/android/app/LocaleConfig.java
index 7c83d5850f76..bbe3ce3c1cdb 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -99,7 +99,7 @@ public class LocaleConfig {
XmlResourceParser parser = res.getXml(resId);
parseLocaleConfig(parser, res);
} catch (Resources.NotFoundException e) {
- Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.", e);
+ Slog.w(TAG, "The resource file pointed to by the given resource ID isn't found.");
mStatus = STATUS_NOT_SPECIFIED;
} catch (XmlPullParserException | IOException e) {
Slog.w(TAG, "Failed to parse XML configuration from "
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 93f91e5af3eb..b7f113609188 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2515,7 +2515,8 @@ public class DevicePolicyManager {
* If another app already had delegated network logging access,
* it will lose the delegation when a new app is delegated.
*
- * <p> Can only be granted by Device Owner or Profile Owner of a managed profile.
+ * <p> Device Owner can grant this access since Android 10. Profile Owner of a managed profile
+ * can grant this access since Android 12.
*/
public static final String DELEGATION_NETWORK_LOGGING = "delegation-network-logging";
@@ -13255,9 +13256,11 @@ public class DevicePolicyManager {
* Called by a device owner, profile owner of a managed profile or delegated app with
* {@link #DELEGATION_NETWORK_LOGGING} to control the network logging feature.
*
- * <p> Supported for a device owner from Android 8. Supported for a profile owner of a managed
- * profile from Android 12. When network logging is enabled by a profile owner, the network logs
- * will only include work profile network activity, not activity on the personal profile.
+ * <p> Supported for a device owner from Android 8 and a delegated app granted by a device
+ * owner from Android 10. Supported for a profile owner of a managed profile and a delegated
+ * app granted by a profile owner from Android 12. When network logging is enabled by a
+ * profile owner, the network logs will only include work profile network activity, not
+ * activity on the personal profile.
*
* <p> Network logs contain DNS lookup and connect() library call events. The following library
* functions are recorded while network logging is active:
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index d71faee4cc8d..3da696ad0bc7 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -390,7 +390,8 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
* Return a string representation of this URI that has common forms of PII redacted,
* making it safer to use for logging purposes. For example, {@code tel:800-466-4411} is
* returned as {@code tel:xxx-xxx-xxxx} and {@code http://example.com/path/to/item/} is
- * returned as {@code http://example.com/...}.
+ * returned as {@code http://example.com/...}. For all other uri schemes, only the scheme,
+ * host and port are returned.
* @return the common forms PII redacted string of this URI
* @hide
*/
@@ -398,13 +399,14 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
public @NonNull String toSafeString() {
String scheme = getScheme();
String ssp = getSchemeSpecificPart();
+ StringBuilder builder = new StringBuilder(64);
+
if (scheme != null) {
+ builder.append(scheme);
+ builder.append(":");
if (scheme.equalsIgnoreCase("tel") || scheme.equalsIgnoreCase("sip")
|| scheme.equalsIgnoreCase("sms") || scheme.equalsIgnoreCase("smsto")
|| scheme.equalsIgnoreCase("mailto") || scheme.equalsIgnoreCase("nfc")) {
- StringBuilder builder = new StringBuilder(64);
- builder.append(scheme);
- builder.append(':');
if (ssp != null) {
for (int i=0; i<ssp.length(); i++) {
char c = ssp.charAt(i);
@@ -415,25 +417,20 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
}
}
}
- return builder.toString();
- } else if (scheme.equalsIgnoreCase("http") || scheme.equalsIgnoreCase("https")
- || scheme.equalsIgnoreCase("ftp") || scheme.equalsIgnoreCase("rtsp")) {
- ssp = "//" + ((getHost() != null) ? getHost() : "")
- + ((getPort() != -1) ? (":" + getPort()) : "")
- + "/...";
+ } else {
+ // For other schemes, let's be conservative about
+ // the data we include -- only the host and port, not the query params, path or
+ // fragment, because those can often have sensitive info.
+ final String host = getHost();
+ final int port = getPort();
+ final String path = getPath();
+ final String authority = getAuthority();
+ if (authority != null) builder.append("//");
+ if (host != null) builder.append(host);
+ if (port != -1) builder.append(":").append(port);
+ if (authority != null || path != null) builder.append("/...");
}
}
- // Not a sensitive scheme, but let's still be conservative about
- // the data we include -- only the ssp, not the query params or
- // fragment, because those can often have sensitive info.
- StringBuilder builder = new StringBuilder(64);
- if (scheme != null) {
- builder.append(scheme);
- builder.append(':');
- }
- if (ssp != null) {
- builder.append(ssp);
- }
return builder.toString();
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c83869c9ee68..fb562d8e97db 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,6 @@ import android.graphics.Region;
import android.os.Bundle;
import android.os.IRemoteCallback;
import android.os.ParcelFileDescriptor;
-import android.view.ContentRecordingSession;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.IAppTransitionAnimationSpecsFuture;
@@ -874,17 +873,6 @@ interface IWindowManager
void detachWindowContextFromWindowContainer(IBinder clientToken);
/**
- * Updates the content recording session. If a different session is already in progress, then
- * the pre-existing session is stopped, and the new incoming session takes over.
- *
- * The DisplayContent for the new session will begin recording when
- * {@link RootWindowContainer#onDisplayChanged} is invoked for the new {@link VirtualDisplay}.
- *
- * @param incomingSession the nullable incoming content recording session
- */
- void setContentRecordingSession(in ContentRecordingSession incomingSession);
-
- /**
* Registers a listener, which is to be called whenever cross-window blur is enabled/disabled.
*
* @param listener the listener to be registered
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 38ca2481726b..90497e7adbf7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4781,9 +4781,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@UnsupportedAppUsage
ListenerInfo mListenerInfo;
- private boolean mPreferKeepClearForFocus;
- private Runnable mMarkPreferKeepClearForFocus;
-
private static class TooltipInfo {
/**
* Text to be displayed in a tooltip popup.
@@ -11962,8 +11959,9 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
@NonNull
List<Rect> collectPreferKeepClearRects() {
ListenerInfo info = mListenerInfo;
- boolean keepBoundsClear =
- (info != null && info.mPreferKeepClear) || mPreferKeepClearForFocus;
+ boolean keepClearForFocus = isFocused()
+ && ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled();
+ boolean keepBoundsClear = (info != null && info.mPreferKeepClear) || keepClearForFocus;
boolean hasCustomKeepClearRects = info != null && info.mKeepClearRects != null;
if (!keepBoundsClear && !hasCustomKeepClearRects) {
@@ -11985,33 +11983,12 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
private void updatePreferKeepClearForFocus() {
- if (mMarkPreferKeepClearForFocus != null) {
- removeCallbacks(mMarkPreferKeepClearForFocus);
- mMarkPreferKeepClearForFocus = null;
- }
-
- final ViewConfiguration configuration = ViewConfiguration.get(mContext);
- final int delay = configuration.getPreferKeepClearForFocusDelay();
- if (delay >= 0) {
- mMarkPreferKeepClearForFocus = () -> {
- mPreferKeepClearForFocus = isFocused();
- mMarkPreferKeepClearForFocus = null;
-
- updatePositionUpdateListener();
- post(this::updateKeepClearRects);
- };
- postDelayed(mMarkPreferKeepClearForFocus, delay);
+ if (ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()) {
+ updatePositionUpdateListener();
+ post(this::updateKeepClearRects);
}
}
- private void cancelMarkPreferKeepClearForFocus() {
- if (mMarkPreferKeepClearForFocus != null) {
- removeCallbacks(mMarkPreferKeepClearForFocus);
- mMarkPreferKeepClearForFocus = null;
- }
- mPreferKeepClearForFocus = false;
- }
-
/**
* Retrieve the list of unrestricted areas within this view's post-layout coordinate space
* which the system will try to not cover with other floating elements, like the pip window.
@@ -13754,7 +13731,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
}
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- updatePreferKeepClearForFocus();
return true;
}
return false;
@@ -21154,7 +21130,6 @@ public class View implements Drawable.Callback, KeyEvent.Callback,
removePerformClickCallback();
clearAccessibilityThrottles();
stopNestedScroll();
- cancelMarkPreferKeepClearForFocus();
// Anything that started animating right before detach should already
// be in its final state when re-attached.
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index ebc409e470e9..638b8f9f9b40 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -347,7 +347,7 @@ public class ViewConfiguration {
private final long mScreenshotChordKeyTimeout;
private final int mSmartSelectionInitializedTimeout;
private final int mSmartSelectionInitializingTimeout;
- private final int mPreferKeepClearForFocusDelay;
+ private final boolean mPreferKeepClearForFocusEnabled;
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 123768915)
private boolean sHasPermanentMenuKey;
@@ -393,7 +393,7 @@ public class ViewConfiguration {
mMinScalingSpan = 0;
mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
- mPreferKeepClearForFocusDelay = -1;
+ mPreferKeepClearForFocusEnabled = false;
}
/**
@@ -508,8 +508,8 @@ public class ViewConfiguration {
com.android.internal.R.integer.config_smartSelectionInitializedTimeoutMillis);
mSmartSelectionInitializingTimeout = res.getInteger(
com.android.internal.R.integer.config_smartSelectionInitializingTimeoutMillis);
- mPreferKeepClearForFocusDelay = res.getInteger(
- com.android.internal.R.integer.config_preferKeepClearForFocusDelayMillis);
+ mPreferKeepClearForFocusEnabled = res.getBoolean(
+ com.android.internal.R.bool.config_preferKeepClearForFocus);
}
/**
@@ -1100,13 +1100,13 @@ public class ViewConfiguration {
}
/**
- * @return The delay in milliseconds before focused Views set themselves as preferred to keep
- * clear, or -1 if Views should not set themselves as preferred to keep clear.
+ * @return {@code true} if Views should set themselves as preferred to keep clear when focused,
+ * {@code false} otherwise.
* @hide
*/
@TestApi
- public int getPreferKeepClearForFocusDelay() {
- return mPreferKeepClearForFocusDelay;
+ public boolean isPreferKeepClearForFocusEnabled() {
+ return mPreferKeepClearForFocusEnabled;
}
/**
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index eabc13ad5ab0..127c7b7a8dc9 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -222,7 +222,6 @@ import java.io.StringWriter;
import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -800,8 +799,7 @@ public final class ViewRootImpl implements ViewParent,
new ViewRootRectTracker(v -> v.collectPreferKeepClearRects());
private final ViewRootRectTracker mUnrestrictedKeepClearRectsTracker =
new ViewRootRectTracker(v -> v.collectUnrestrictedPreferKeepClearRects());
- private List<Rect> mPendingKeepClearAreas;
- private List<Rect> mPendingUnrestrictedKeepClearAreas;
+ private boolean mHasPendingKeepClearAreaChange;
private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection;
@@ -4819,45 +4817,31 @@ public final class ViewRootImpl implements ViewParent,
}
void keepClearRectsChanged() {
- List<Rect> restrictedKeepClearRects = mKeepClearRectsTracker.computeChangedRects();
- List<Rect> unrestrictedKeepClearRects =
- mUnrestrictedKeepClearRectsTracker.computeChangedRects();
- if ((restrictedKeepClearRects != null || unrestrictedKeepClearRects != null)
- && mView != null) {
- if (restrictedKeepClearRects == null) {
- restrictedKeepClearRects = Collections.emptyList();
- }
- if (unrestrictedKeepClearRects == null) {
- unrestrictedKeepClearRects = Collections.emptyList();
- }
+ boolean restrictedKeepClearRectsChanged = mKeepClearRectsTracker.computeChanges();
+ boolean unrestrictedKeepClearRectsChanged =
+ mUnrestrictedKeepClearRectsTracker.computeChanges();
- if (mHandler.hasMessages(MSG_REPORT_KEEP_CLEAR_RECTS)) {
- // Keep clear areas have been reported recently, wait before reporting new set
- // of keep clear areas
- mPendingKeepClearAreas = restrictedKeepClearRects;
- mPendingUnrestrictedKeepClearAreas = unrestrictedKeepClearRects;
- } else {
+ if ((restrictedKeepClearRectsChanged || unrestrictedKeepClearRectsChanged)
+ && mView != null) {
+ mHasPendingKeepClearAreaChange = true;
+ // Only report keep clear areas immediately if they have not been reported recently
+ if (!mHandler.hasMessages(MSG_REPORT_KEEP_CLEAR_RECTS)) {
mHandler.sendEmptyMessageDelayed(MSG_REPORT_KEEP_CLEAR_RECTS,
KEEP_CLEAR_AREA_REPORT_RATE_MILLIS);
- try {
- mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
- unrestrictedKeepClearRects);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
+ reportKeepClearAreasChanged();
}
}
}
void reportKeepClearAreasChanged() {
- final List<Rect> restrictedKeepClearRects = mPendingKeepClearAreas;
- final List<Rect> unrestrictedKeepClearRects = mPendingUnrestrictedKeepClearAreas;
- if (restrictedKeepClearRects == null && unrestrictedKeepClearRects == null) {
+ if (!mHasPendingKeepClearAreaChange) {
return;
}
+ mHasPendingKeepClearAreaChange = false;
- mPendingKeepClearAreas = null;
- mPendingUnrestrictedKeepClearAreas = null;
+ final List<Rect> restrictedKeepClearRects = mKeepClearRectsTracker.getLastComputedRects();
+ final List<Rect> unrestrictedKeepClearRects =
+ mUnrestrictedKeepClearRectsTracker.getLastComputedRects();
try {
mWindowSession.reportKeepClearAreasChanged(mWindow, restrictedKeepClearRects,
diff --git a/core/java/android/view/ViewRootRectTracker.java b/core/java/android/view/ViewRootRectTracker.java
index fd9cc1920b39..152729b8d1d8 100644
--- a/core/java/android/view/ViewRootRectTracker.java
+++ b/core/java/android/view/ViewRootRectTracker.java
@@ -73,10 +73,25 @@ class ViewRootRectTracker {
}
/**
- * @return all visible rects from all views in the global (root) coordinate system
+ * @return all Rects from all visible Views in the global (root) coordinate system,
+ * or {@code null} if Rects are unchanged since the last call to this method.
*/
@Nullable
public List<Rect> computeChangedRects() {
+ if (computeChanges()) {
+ return mRects;
+ }
+ return null;
+ }
+
+ /**
+ * Computes changes to all Rects from all Views.
+ * After calling this method, the updated list of Rects can be retrieved
+ * with {@link #getLastComputedRects()}.
+ *
+ * @return {@code true} if there were changes, {@code false} otherwise.
+ */
+ public boolean computeChanges() {
boolean changed = mRootRectsChanged;
final Iterator<ViewInfo> i = mViewInfos.iterator();
final List<Rect> rects = new ArrayList<>(mRootRects);
@@ -100,10 +115,22 @@ class ViewRootRectTracker {
mRootRectsChanged = false;
if (!mRects.equals(rects)) {
mRects = rects;
- return rects;
+ return true;
}
}
- return null;
+ return false;
+ }
+
+ /**
+ * Returns a List of all Rects from all visible Views in the global (root) coordinate system.
+ * This list is only updated when calling {@link #computeChanges()} or
+ * {@link #computeChangedRects()}.
+ *
+ * @return all Rects from all visible Views in the global (root) coordinate system
+ */
+ @NonNull
+ public List<Rect> getLastComputedRects() {
+ return mRects;
}
/**
diff --git a/core/java/com/android/internal/jank/InteractionJankMonitor.java b/core/java/com/android/internal/jank/InteractionJankMonitor.java
index 2dcc58564f82..66b78f38cc0d 100644
--- a/core/java/com/android/internal/jank/InteractionJankMonitor.java
+++ b/core/java/com/android/internal/jank/InteractionJankMonitor.java
@@ -20,6 +20,7 @@ import static com.android.internal.jank.FrameTracker.REASON_CANCEL_NORMAL;
import static com.android.internal.jank.FrameTracker.REASON_CANCEL_TIMEOUT;
import static com.android.internal.jank.FrameTracker.REASON_END_NORMAL;
import static com.android.internal.jank.FrameTracker.REASON_END_UNKNOWN;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_ALL_APPS_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_HOME;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_APP_CLOSE_TO_PIP;
@@ -46,6 +47,7 @@ import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_IN
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SCREEN_OFF_SHOW_AOD;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_PAGE_SCROLL;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER;
+import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON;
import static com.android.internal.util.FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SHADE_APP_LAUNCH_FROM_MEDIA_PLAYER;
@@ -198,6 +200,7 @@ public class InteractionJankMonitor {
public static final int CUJ_SETTINGS_SLIDER = 53;
public static final int CUJ_TAKE_SCREENSHOT = 54;
public static final int CUJ_VOLUME_CONTROL = 55;
+ public static final int CUJ_SETTINGS_TOGGLE = 57;
private static final int NO_STATSD_LOGGING = -1;
@@ -262,6 +265,8 @@ public class InteractionJankMonitor {
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_SLIDER,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__TAKE_SCREENSHOT,
UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__VOLUME_CONTROL,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BIOMETRIC_PROMPT_TRANSITION,
+ UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__SETTINGS_TOGGLE,
};
private static volatile InteractionJankMonitor sInstance;
@@ -338,6 +343,7 @@ public class InteractionJankMonitor {
CUJ_SETTINGS_SLIDER,
CUJ_TAKE_SCREENSHOT,
CUJ_VOLUME_CONTROL,
+ CUJ_SETTINGS_TOGGLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -768,6 +774,8 @@ public class InteractionJankMonitor {
return "TAKE_SCREENSHOT";
case CUJ_VOLUME_CONTROL:
return "VOLUME_CONTROL";
+ case CUJ_SETTINGS_TOGGLE:
+ return "SETTINGS_TOGGLE";
}
return "UNKNOWN";
}
diff --git a/core/java/com/android/internal/os/BatteryUsageStatsStore.java b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
index 09c9cfcd2e71..04f72c7d703d 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsStore.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -320,4 +320,19 @@ public class BatteryUsageStatsStore {
mFileSizes.remove(file);
}
}
+
+ public void removeAllSnapshots() {
+ lockSnapshotDirectory();
+ try {
+ for (File file : mStoreDir.listFiles()) {
+ if (file.getName().endsWith(SNAPSHOT_FILE_EXTENSION)) {
+ if (!file.delete()) {
+ Slog.e(TAG, "Cannot delete battery usage stats " + file);
+ }
+ }
+ }
+ } finally {
+ unlockSnapshotDirectory();
+ }
+ }
}
diff --git a/core/proto/android/server/jobscheduler.proto b/core/proto/android/server/jobscheduler.proto
index c4ff49cfbfaf..00127c134ce6 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -1073,6 +1073,7 @@ message JobStatusDumpProto {
RARE = 3;
NEVER = 4;
RESTRICTED = 5;
+ EXEMPTED = 6;
}
optional Bucket standby_bucket = 17;
optional bool is_exempted_from_app_standby = 27;
diff --git a/core/res/res/color/hint_foreground_material_dark.xml b/core/res/res/color/hint_foreground_material_dark.xml
index 5cc955952524..66fcb041dd7a 100644
--- a/core/res/res/color/hint_foreground_material_dark.xml
+++ b/core/res/res/color/hint_foreground_material_dark.xml
@@ -15,10 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true"
- android:state_pressed="true"
- android:alpha="@dimen/hint_pressed_alpha_material_dark"
- android:color="@color/foreground_material_dark" />
- <item android:alpha="@dimen/hint_alpha_material_dark"
+ <item android:alpha="@dimen/secondary_content_alpha_material_dark"
android:color="@color/foreground_material_dark" />
</selector>
diff --git a/core/res/res/color/hint_foreground_material_light.xml b/core/res/res/color/hint_foreground_material_light.xml
index f7465e0e5139..63dd3b0131ca 100644
--- a/core/res/res/color/hint_foreground_material_light.xml
+++ b/core/res/res/color/hint_foreground_material_light.xml
@@ -15,10 +15,6 @@
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
- <item android:state_enabled="true"
- android:state_pressed="true"
- android:alpha="@dimen/hint_pressed_alpha_material_light"
- android:color="@color/foreground_material_light" />
- <item android:alpha="@dimen/hint_alpha_material_light"
+ <item android:alpha="@dimen/secondary_content_alpha_material_light"
android:color="@color/foreground_material_light" />
</selector>
diff --git a/core/res/res/layout/log_access_user_consent_dialog_permission.xml b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
index c88bc9282708..1a395b9fc30f 100644
--- a/core/res/res/layout/log_access_user_consent_dialog_permission.xml
+++ b/core/res/res/layout/log_access_user_consent_dialog_permission.xml
@@ -26,8 +26,7 @@
android:paddingLeft="24dp"
android:paddingRight="24dp"
android:paddingTop="24dp"
- android:paddingBottom="24dp"
- android:background="?attr/colorSurface">
+ android:paddingBottom="24dp">
<ImageView
android:id="@+id/log_access_image_view"
@@ -37,8 +36,7 @@
android:src="@drawable/ic_doc_document"
tools:layout_editor_absoluteX="148dp"
tools:layout_editor_absoluteY="35dp"
- android:gravity="center"
- android:tint="?attr/colorAccentPrimaryVariant"/>
+ android:gravity="center" />
<TextView
android:id="@+id/log_access_dialog_title"
@@ -60,29 +58,35 @@
android:textColor="?android:attr/textColorPrimary"
android:gravity="center" />
- <Button
- android:id="@+id/log_access_dialog_allow_button"
+ <LinearLayout
+ android:orientation="vertical"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/log_access_confirmation_allow"
- style="@style/PermissionGrantButtonTop"
- android:layout_marginBottom="5dp"
- android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:clipToOutline="true"
- android:gravity="center" />
-
- <Button
- android:id="@+id/log_access_dialog_deny_button"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:text="@string/log_access_confirmation_deny"
- style="@style/PermissionGrantButtonBottom"
- android:layout_centerHorizontal="true"
- android:layout_alignParentTop="true"
- android:layout_alignParentBottom="true"
- android:clipToOutline="true"
- android:gravity="center" />
+ android:layout_height="wrap_content">
+ <Button
+ android:id="@+id/log_access_dialog_allow_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/log_access_confirmation_allow"
+ style="@style/PermissionGrantButtonTop"
+ android:textAppearance="@style/PermissionGrantButtonTextAppearance"
+ android:layout_marginBottom="5dp"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+ <Button
+ android:id="@+id/log_access_dialog_deny_button"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/log_access_confirmation_deny"
+ style="@style/PermissionGrantButtonBottom"
+ android:textAppearance="@style/PermissionGrantButtonTextAppearance"
+ android:layout_centerHorizontal="true"
+ android:layout_alignParentTop="true"
+ android:layout_alignParentBottom="true"
+ android:clipToOutline="true"
+ android:gravity="center" />
+ </LinearLayout>
</LinearLayout> \ No newline at end of file
diff --git a/core/res/res/layout/search_view.xml b/core/res/res/layout/search_view.xml
index 0c462fda9235..39034dc61620 100644
--- a/core/res/res/layout/search_view.xml
+++ b/core/res/res/layout/search_view.xml
@@ -96,8 +96,8 @@
android:id="@+id/search_close_btn"
android:layout_width="wrap_content"
android:layout_height="match_parent"
- android:paddingStart="8dip"
- android:paddingEnd="8dip"
+ android:paddingStart="12dip"
+ android:paddingEnd="12dip"
android:layout_gravity="center_vertical"
android:background="?attr/selectableItemBackgroundBorderless"
android:focusable="true"
diff --git a/core/res/res/values-television/styles.xml b/core/res/res/values-television/styles.xml
new file mode 100644
index 000000000000..ad9140cdc6a9
--- /dev/null
+++ b/core/res/res/values-television/styles.xml
@@ -0,0 +1,21 @@
+<!--
+ ~ 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.
+ -->
+<resources>
+ <style name="PermissionGrantButtonTop"
+ parent="@style/Widget.Leanback.Button.ButtonBarGravityStart" />
+ <style name="PermissionGrantButtonBottom"
+ parent="@style/Widget.Leanback.Button.ButtonBarGravityStart" />
+</resources> \ No newline at end of file
diff --git a/core/res/res/values/colors_material.xml b/core/res/res/values/colors_material.xml
index d357f0102777..ea6e1f182fbf 100644
--- a/core/res/res/values/colors_material.xml
+++ b/core/res/res/values/colors_material.xml
@@ -65,18 +65,12 @@
<!-- 70% white -->
<color name="secondary_text_default_material_dark">#b3ffffff</color>
- <item name="hint_alpha_material_dark" format="float" type="dimen">0.50</item>
- <item name="hint_alpha_material_light" format="float" type="dimen">0.38</item>
-
- <item name="hint_pressed_alpha_material_dark" format="float" type="dimen">0.70</item>
- <item name="hint_pressed_alpha_material_light" format="float" type="dimen">0.54</item>
-
<item name="disabled_alpha_material_light" format="float" type="dimen">0.26</item>
<item name="disabled_alpha_material_dark" format="float" type="dimen">0.30</item>
<item name="primary_content_alpha_material_dark" format="float" type="dimen">1</item>
<item name="primary_content_alpha_material_light" format="float" type="dimen">0.87</item>
<item name="secondary_content_alpha_material_dark" format="float" type="dimen">.7</item>
- <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.54</item>
+ <item name="secondary_content_alpha_material_light" format="float" type="dimen">0.60</item>
<item name="highlight_alpha_material_light" format="float" type="dimen">0.10</item>
<item name="highlight_alpha_material_dark" format="float" type="dimen">0.10</item>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 054695ef8f93..70766cc79eb4 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -5163,9 +5163,8 @@
when TextClassifier has not been initialized. -->
<integer name="config_smartSelectionInitializingTimeoutMillis">500</integer>
- <!-- The delay in milliseconds before focused Views set themselves as preferred to keep clear.
- Set to -1 if Views should not set themselves as preferred to keep clear. -->
- <integer name="config_preferKeepClearForFocusDelayMillis">-1</integer>
+ <!-- If true, Views will declare they prefer to be kept clear from overlays when focused. -->
+ <bool name="config_preferKeepClearForFocus">false</bool>
<!-- Indicates that default fitness tracker app needs to request sensor and location permissions. -->
<bool name="config_trackerAppNeedsPermissions">false</bool>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 0c35f93147a6..cfc298c74110 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1526,26 +1526,27 @@ please see styles_device_defaults.xml.
<!-- The style for log access consent text -->
<style name="AllowLogAccess">
- <item name="android:layout_width">332dp</item>
<item name="android:textSize">24sp</item>
<item name="android:fontFamily">google-sans</item>
</style>
<style name="PrimaryAllowLogAccess">
- <item name="android:layout_width">332dp</item>
<item name="android:textSize">14sp</item>
<item name="android:fontFamily">google-sans-text</item>
</style>
+ <style name="PermissionGrantButtonTextAppearance">
+ <item name="android:fontFamily">google-sans-medium</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">@android:color/system_neutral1_900</item>
+ </style>
+
<style name="PermissionGrantButtonTop"
parent="@android:style/Widget.DeviceDefault.Button.Borderless.Colored">
<item name="android:layout_width">332dp</item>
<item name="android:layout_height">56dp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_marginBottom">2dp</item>
- <item name="android:fontFamily">google-sans-medium</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">@android:color/system_neutral1_900</item>
<item name="android:background">@drawable/grant_permissions_buttons_top</item>
</style>
@@ -1555,9 +1556,6 @@ please see styles_device_defaults.xml.
<item name="android:layout_height">56dp</item>
<item name="android:layout_marginTop">2dp</item>
<item name="android:layout_marginBottom">2dp</item>
- <item name="android:fontFamily">google-sans-medium</item>
- <item name="android:textSize">14sp</item>
- <item name="android:textColor">@android:color/system_neutral1_900</item>
<item name="android:background">@drawable/grant_permissions_buttons_bottom</item>
</style>
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 5bfc56888789..e111ee184be1 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -479,7 +479,7 @@
<java-symbol type="bool" name="config_useAssistantVolume" />
<java-symbol type="integer" name="config_smartSelectionInitializedTimeoutMillis" />
<java-symbol type="integer" name="config_smartSelectionInitializingTimeoutMillis" />
- <java-symbol type="integer" name="config_preferKeepClearForFocusDelayMillis" />
+ <java-symbol type="bool" name="config_preferKeepClearForFocus" />
<java-symbol type="bool" name="config_hibernationDeletesOatArtifactsEnabled"/>
<java-symbol type="integer" name="config_defaultAnalogClockSecondsHandFps"/>
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 3733bfa586d1..89632a46267e 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -989,10 +989,14 @@ public class UriTest extends TestCase {
checkToSafeString("ftp://ftp.android.com:2121/...",
"ftp://root:love@ftp.android.com:2121/");
- checkToSafeString("unsupported://ajkakjah/askdha/secret?secret",
+ checkToSafeString("unsupported://ajkakjah/...",
"unsupported://ajkakjah/askdha/secret?secret");
- checkToSafeString("unsupported:ajkakjah/askdha/secret?secret",
+ checkToSafeString("unsupported:",
"unsupported:ajkakjah/askdha/secret?secret");
+ checkToSafeString("unsupported:/...",
+ "unsupported:/ajkakjah/askdha/secret?secret");
+ checkToSafeString("file:///...",
+ "file:///path/to/secret.doc");
}
private void checkToSafeString(String expectedSafeString, String original) {
diff --git a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
index 21f6e7c82d7f..c9729fab3b5e 100644
--- a/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/BatteryUsageStatsStoreTest.java
@@ -132,6 +132,26 @@ public class BatteryUsageStatsStoreTest {
}
@Test
+ public void testRemoveAllSnapshots() throws Exception {
+ prepareBatteryStats();
+
+ for (int i = 0; i < 3; i++) {
+ mMockClock.realtime += 10_000_000;
+ mMockClock.uptime += 10_000_000;
+ mMockClock.currentTime += 10_000_000;
+ prepareBatteryStats();
+
+ mBatteryStats.resetAllStatsCmdLocked();
+ }
+
+ assertThat(getDirectorySize(mStoreDirectory)).isNotEqualTo(0);
+
+ mBatteryUsageStatsStore.removeAllSnapshots();
+
+ assertThat(getDirectorySize(mStoreDirectory)).isEqualTo(0);
+ }
+
+ @Test
public void testSavingStatsdAtomPullTimestamp() {
mBatteryUsageStatsStore.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(1234);
assertThat(mBatteryUsageStatsStore.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp())
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index d190fcec6c8c..1d58a409718d 100644
--- a/media/java/android/media/projection/IMediaProjectionManager.aidl
+++ b/media/java/android/media/projection/IMediaProjectionManager.aidl
@@ -21,6 +21,7 @@ import android.media.projection.IMediaProjectionCallback;
import android.media.projection.IMediaProjectionWatcherCallback;
import android.media.projection.MediaProjectionInfo;
import android.os.IBinder;
+import android.view.ContentRecordingSession;
/** {@hide} */
interface IMediaProjectionManager {
@@ -33,4 +34,15 @@ interface IMediaProjectionManager {
void stopActiveProjection();
void addCallback(IMediaProjectionWatcherCallback callback);
void removeCallback(IMediaProjectionWatcherCallback callback);
+
+ /**
+ * Updates the content recording session. If a different session is already in progress, then
+ * the pre-existing session is stopped, and the new incoming session takes over. Only updates
+ * the session if the given projection is valid.
+ *
+ * @param incomingSession the nullable incoming content recording session
+ * @param projection the non-null projection the session describes
+ */
+ void setContentRecordingSession(in ContentRecordingSession incomingSession,
+ in IMediaProjection projection);
}
diff --git a/media/java/android/media/projection/MediaProjection.java b/media/java/android/media/projection/MediaProjection.java
index b5f95938f845..ba7bf3f5f5d3 100644
--- a/media/java/android/media/projection/MediaProjection.java
+++ b/media/java/android/media/projection/MediaProjection.java
@@ -26,12 +26,11 @@ import android.hardware.display.VirtualDisplay;
import android.hardware.display.VirtualDisplayConfig;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.ArrayMap;
import android.util.Log;
import android.view.ContentRecordingSession;
-import android.view.IWindowManager;
import android.view.Surface;
-import android.view.WindowManagerGlobal;
import android.window.WindowContainerToken;
import java.util.Map;
@@ -53,6 +52,7 @@ public final class MediaProjection {
private final IMediaProjection mImpl;
private final Context mContext;
private final Map<Callback, CallbackRecord> mCallbacks;
+ @Nullable private IMediaProjectionManager mProjectionService = null;
/** @hide */
public MediaProjection(Context context, IMediaProjection impl) {
@@ -172,7 +172,6 @@ public final class MediaProjection {
@NonNull VirtualDisplayConfig.Builder virtualDisplayConfig,
@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) {
try {
- final IWindowManager wmService = WindowManagerGlobal.getWindowManagerService();
final WindowContainerToken taskWindowContainerToken =
mImpl.getTaskRecordingWindowContainerToken();
Context windowContext = null;
@@ -199,7 +198,7 @@ public final class MediaProjection {
}
session.setDisplayId(virtualDisplay.getDisplay().getDisplayId());
// Successfully set up, so save the current session details.
- wmService.setContentRecordingSession(session);
+ getProjectionService().setContentRecordingSession(session, mImpl);
return virtualDisplay;
} catch (RemoteException e) {
// Can not capture if WMS is not accessible, so bail out.
@@ -207,6 +206,14 @@ public final class MediaProjection {
}
}
+ private IMediaProjectionManager getProjectionService() {
+ if (mProjectionService == null) {
+ mProjectionService = IMediaProjectionManager.Stub.asInterface(
+ ServiceManager.getService(Context.MEDIA_PROJECTION_SERVICE));
+ }
+ return mProjectionService;
+ }
+
/**
* Stops projection.
*/
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 5e3907caf860..5411591d8df6 100644
--- a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
+++ b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
@@ -48,8 +48,6 @@
<color name="settingslib_text_color_preference_category_title">@android:color/system_accent1_100</color>
- <color name="settingslib_ripple_color">@color/settingslib_material_grey_900</color>
-
<color name="settingslib_surface_dark">@android:color/system_neutral1_800</color>
<color name="settingslib_colorSurface">@color/settingslib_surface_dark</color>
diff --git a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
index b43b44421fb8..fb06976ebfe3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -19,8 +19,6 @@ package com.android.settingslib;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
-import android.view.View;
-import android.view.View.OnClickListener;
import android.widget.Switch;
import androidx.annotation.Keep;
@@ -28,6 +26,7 @@ import androidx.annotation.Nullable;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.core.instrumentation.SettingsJankMonitor;
/**
* A custom preference that provides inline switch toggle. It has a mandatory field for title, and
@@ -65,31 +64,25 @@ public class PrimarySwitchPreference extends RestrictedPreference {
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
- final View switchWidget = holder.findViewById(R.id.switchWidget);
- if (switchWidget != null) {
- switchWidget.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- if (mSwitch != null && !mSwitch.isEnabled()) {
- return;
- }
- setChecked(!mChecked);
- if (!callChangeListener(mChecked)) {
- setChecked(!mChecked);
- } else {
- persistBoolean(mChecked);
- }
+ mSwitch = (Switch) holder.findViewById(R.id.switchWidget);
+ if (mSwitch != null) {
+ mSwitch.setOnClickListener(v -> {
+ if (mSwitch != null && !mSwitch.isEnabled()) {
+ return;
+ }
+ final boolean newChecked = !mChecked;
+ if (callChangeListener(newChecked)) {
+ SettingsJankMonitor.detectToggleJank(getKey(), mSwitch);
+ setChecked(newChecked);
+ persistBoolean(newChecked);
}
});
// Consumes move events to ignore drag actions.
- switchWidget.setOnTouchListener((v, event) -> {
+ mSwitch.setOnTouchListener((v, event) -> {
return event.getActionMasked() == MotionEvent.ACTION_MOVE;
});
- }
- mSwitch = (Switch) holder.findViewById(R.id.switchWidget);
- if (mSwitch != null) {
mSwitch.setContentDescription(getTitle());
mSwitch.setChecked(mChecked);
mSwitch.setEnabled(mEnableSwitch);
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
index 2f30baa79b9d..cb8e7e8bb6ef 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -198,10 +198,9 @@ public class RestrictedPreferenceHelper {
if (mDisabledByAdmin != disabled) {
mDisabledByAdmin = disabled;
changed = true;
+ updateDisabledState();
}
- updateDisabledState();
-
return changed;
}
@@ -210,10 +209,9 @@ public class RestrictedPreferenceHelper {
if (mDisabledByAppOps != disabled) {
mDisabledByAppOps = disabled;
changed = true;
+ updateDisabledState();
}
- updateDisabledState();
-
return changed;
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
index e6160bb9896d..b5e4fa38d244 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -254,9 +254,11 @@ public class RestrictedSwitchPreference extends SwitchPreference {
final boolean ecmEnabled = getContext().getResources().getBoolean(
com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);
final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;
- if (appOpsAllowed || isEnabled) {
+ if (isEnabled) {
setEnabled(true);
- } else {
+ } else if (appOpsAllowed && isDisabledByAppOps()) {
+ setEnabled(true);
+ } else if (!appOpsAllowed){
setDisabledByAppOps(true);
}
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
new file mode 100644
index 000000000000..a5f69ffec4b4
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/core/instrumentation/SettingsJankMonitor.kt
@@ -0,0 +1,74 @@
+/*
+ * 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.settingslib.core.instrumentation
+
+import android.view.View
+import androidx.annotation.VisibleForTesting
+import androidx.preference.PreferenceGroupAdapter
+import androidx.preference.SwitchPreference
+import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.jank.InteractionJankMonitor
+import java.util.concurrent.Executors
+import java.util.concurrent.TimeUnit
+
+/**
+ * Helper class for Settings library to trace jank.
+ */
+object SettingsJankMonitor {
+ private val jankMonitor = InteractionJankMonitor.getInstance()
+ private val scheduledExecutorService = Executors.newSingleThreadScheduledExecutor()
+
+ // Switch toggle animation duration is 250ms, and there is also a ripple effect animation when
+ // clicks, which duration is variable. Use 300ms here to cover.
+ @VisibleForTesting
+ const val MONITORED_ANIMATION_DURATION_MS = 300L
+
+ /**
+ * Detects the jank when click on a SwitchPreference.
+ *
+ * @param recyclerView the recyclerView contains the preference
+ * @param preference the clicked preference
+ */
+ @JvmStatic
+ fun detectSwitchPreferenceClickJank(recyclerView: RecyclerView, preference: SwitchPreference) {
+ val adapter = recyclerView.adapter as? PreferenceGroupAdapter ?: return
+ val adapterPosition = adapter.getPreferenceAdapterPosition(preference)
+ val viewHolder = recyclerView.findViewHolderForAdapterPosition(adapterPosition) ?: return
+ detectToggleJank(preference.key, viewHolder.itemView)
+ }
+
+ /**
+ * Detects the animation jank on the given view.
+ *
+ * @param tag the tag for jank monitor
+ * @param view the instrumented view
+ */
+ @JvmStatic
+ fun detectToggleJank(tag: String?, view: View) {
+ val builder = InteractionJankMonitor.Configuration.Builder.withView(
+ InteractionJankMonitor.CUJ_SETTINGS_TOGGLE,
+ view
+ )
+ if (tag != null) {
+ builder.setTag(tag)
+ }
+ if (jankMonitor.begin(builder)) {
+ scheduledExecutorService.schedule({
+ jankMonitor.end(InteractionJankMonitor.CUJ_SETTINGS_TOGGLE)
+ }, MONITORED_ANIMATION_DURATION_MS, TimeUnit.MILLISECONDS)
+ }
+ }
+} \ No newline at end of file
diff --git a/packages/SettingsLib/tests/robotests/Android.bp b/packages/SettingsLib/tests/robotests/Android.bp
index 2d1a516f4f11..5c55a435b463 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -63,6 +63,7 @@ java_library {
libs: [
"Robolectric_all-target",
+ "mockito-robolectric-prebuilt",
"truth-prebuilt",
],
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
index 9c16740061fe..74c2fc8cce4c 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/PrimarySwitchPreferenceTest.java
@@ -30,14 +30,17 @@ import androidx.preference.Preference.OnPreferenceChangeListener;
import androidx.preference.PreferenceViewHolder;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
+import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowInteractionJankMonitor.class})
public class PrimarySwitchPreferenceTest {
private Context mContext;
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
new file mode 100644
index 000000000000..d67d44b9035d
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/core/instrumentation/SettingsJankMonitorTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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.settingslib.core.instrumentation;
+
+import static com.android.internal.jank.InteractionJankMonitor.CUJ_SETTINGS_TOGGLE;
+import static com.android.settingslib.core.instrumentation.SettingsJankMonitor.MONITORED_ANIMATION_DURATION_MS;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.annotation.NonNull;
+import android.view.View;
+
+import androidx.preference.PreferenceGroupAdapter;
+import androidx.preference.SwitchPreference;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.android.internal.jank.InteractionJankMonitor;
+import com.android.internal.jank.InteractionJankMonitor.CujType;
+import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
+
+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.junit.MockitoJUnit;
+import org.mockito.junit.MockitoRule;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.annotation.Config;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+import org.robolectric.util.ReflectionHelpers;
+
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+@RunWith(RobolectricTestRunner.class)
+@Config(shadows = {ShadowInteractionJankMonitor.class, SettingsJankMonitorTest.ShadowBuilder.class})
+public class SettingsJankMonitorTest {
+ private static final String TEST_KEY = "key";
+
+ @Rule
+ public MockitoRule mocks = MockitoJUnit.rule();
+
+ @Mock
+ private View mView;
+
+ @Mock
+ private RecyclerView mRecyclerView;
+
+ @Mock
+ private PreferenceGroupAdapter mPreferenceGroupAdapter;
+
+ @Mock
+ private SwitchPreference mSwitchPreference;
+
+ @Mock
+ private ScheduledExecutorService mScheduledExecutorService;
+
+ @Before
+ public void setUp() {
+ ShadowInteractionJankMonitor.reset();
+ when(ShadowInteractionJankMonitor.MOCK_INSTANCE.begin(any())).thenReturn(true);
+ ReflectionHelpers.setStaticField(SettingsJankMonitor.class, "scheduledExecutorService",
+ mScheduledExecutorService);
+ }
+
+ @Test
+ public void detectToggleJank() {
+ SettingsJankMonitor.detectToggleJank(TEST_KEY, mView);
+
+ verifyToggleJankMonitored();
+ }
+
+ @Test
+ public void detectSwitchPreferenceClickJank() {
+ int adapterPosition = 7;
+ when(mRecyclerView.getAdapter()).thenReturn(mPreferenceGroupAdapter);
+ when(mPreferenceGroupAdapter.getPreferenceAdapterPosition(mSwitchPreference))
+ .thenReturn(adapterPosition);
+ when(mRecyclerView.findViewHolderForAdapterPosition(adapterPosition))
+ .thenReturn(new RecyclerView.ViewHolder(mView) {
+ });
+ when(mSwitchPreference.getKey()).thenReturn(TEST_KEY);
+
+ SettingsJankMonitor.detectSwitchPreferenceClickJank(mRecyclerView, mSwitchPreference);
+
+ verifyToggleJankMonitored();
+ }
+
+ private void verifyToggleJankMonitored() {
+ verify(ShadowInteractionJankMonitor.MOCK_INSTANCE).begin(ShadowBuilder.sBuilder);
+ assertThat(ShadowBuilder.sView).isSameInstanceAs(mView);
+ ArgumentCaptor<Runnable> runnableCaptor = ArgumentCaptor.forClass(Runnable.class);
+ verify(mScheduledExecutorService).schedule(runnableCaptor.capture(),
+ eq(MONITORED_ANIMATION_DURATION_MS), eq(TimeUnit.MILLISECONDS));
+ runnableCaptor.getValue().run();
+ verify(ShadowInteractionJankMonitor.MOCK_INSTANCE).end(CUJ_SETTINGS_TOGGLE);
+ }
+
+ @Implements(InteractionJankMonitor.Configuration.Builder.class)
+ static class ShadowBuilder {
+ private static InteractionJankMonitor.Configuration.Builder sBuilder;
+ private static View sView;
+
+ @Resetter
+ public static void reset() {
+ sBuilder = null;
+ sView = null;
+ }
+
+ @Implementation
+ public static InteractionJankMonitor.Configuration.Builder withView(
+ @CujType int cuj, @NonNull View view) {
+ assertThat(cuj).isEqualTo(CUJ_SETTINGS_TOGGLE);
+ sView = view;
+ sBuilder = mock(InteractionJankMonitor.Configuration.Builder.class);
+ when(sBuilder.setTag(TEST_KEY)).thenReturn(sBuilder);
+ return sBuilder;
+ }
+ }
+}
diff --git a/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowInteractionJankMonitor.java b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowInteractionJankMonitor.java
new file mode 100644
index 000000000000..855da16b1dfa
--- /dev/null
+++ b/packages/SettingsLib/tests/robotests/testutils/com/android/settingslib/testutils/shadow/ShadowInteractionJankMonitor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2021 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.settingslib.testutils.shadow;
+
+import static org.mockito.Mockito.mock;
+
+import com.android.internal.jank.InteractionJankMonitor;
+
+import org.mockito.Mockito;
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+import org.robolectric.annotation.Resetter;
+
+@Implements(InteractionJankMonitor.class)
+public class ShadowInteractionJankMonitor {
+ public static final InteractionJankMonitor MOCK_INSTANCE = mock(InteractionJankMonitor.class);
+
+ @Resetter
+ public static void reset() {
+ Mockito.reset(MOCK_INSTANCE);
+ }
+
+ @Implementation
+ public static InteractionJankMonitor getInstance() {
+ return MOCK_INSTANCE;
+ }
+}
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index d146fc91bfb5..b01fd6de5243 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -124,49 +124,5 @@
}
]
}
- ],
- "hubui-postsubmit": [
- {
- "name": "PlatformScenarioTests",
- "options": [
- {
- "include-filter": "android.platform.test.scenario.hubui"
- },
- {
- "include-annotation": "android.platform.test.scenario.annotation.HubUi"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- }
- ]
- }
- ],
- "hubui-presubmit": [
- {
- "name": "PlatformScenarioTests",
- "options": [
- {
- "include-annotation": "android.platform.test.annotations.Presubmit"
- },
- {
- "include-annotation": "android.platform.test.scenario.annotation.HubUi"
- },
- {
- "include-filter": "android.platform.test.scenario.hubui"
- },
- {
- "exclude-annotation": "org.junit.Ignore"
- },
- {
- "exclude-annotation": "androidx.test.filters.FlakyTest"
- },
- {
- "exclude-annotation": "android.platform.test.annotations.Postsubmit"
- }
- ]
- }
]
}
diff --git a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
index 1c09e81f92ca..18f870d27772 100644
--- a/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
+++ b/packages/SystemUI/res/layout/clipboard_edit_text_activity.xml
@@ -58,6 +58,7 @@
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
+ android:minHeight="48dp"
android:background="@null"
android:gravity="start|top"
android:textSize="24sp" />
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 7010a289df68..1351ec144ec5 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2485,7 +2485,7 @@
<!-- Text informing user where text being edited was copied from [CHAR LIMIT=NONE] -->
<string name="clipboard_edit_source">From <xliff:g id="appName" example="Gmail">%1$s</xliff:g></string>
<!-- Label for button to dismiss clipboard overlay [CHAR LIMIT=NONE] -->
- <string name="clipboard_dismiss_description">Dismiss copy UI</string>
+ <string name="clipboard_dismiss_description">Dismiss copied text</string>
<!-- Label for button to edit text that was copied to the clipboard [CHAR LIMIT=NONE] -->
<string name="clipboard_edit_text_description">Edit copied text</string>
<!-- Label for button to edit an image that was copied to the clipboard [CHAR LIMIT=NONE] -->
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
index 44879aa9e85b..b8a00133c728 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlActionCoordinatorImpl.kt
@@ -261,7 +261,7 @@ class ControlActionCoordinatorImpl @Inject constructor(
taskViewFactory.get().create(context, uiExecutor, {
dialog = DetailDialog(
activityContext, broadcastSender,
- it, pendingIntent, cvh
+ it, pendingIntent, cvh, keyguardStateController, activityStarter
).also {
it.setOnDismissListener { _ -> dialog = null }
it.show()
diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
index 80589a2711cc..edd1c6891946 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/DetailDialog.kt
@@ -34,6 +34,8 @@ import android.widget.ImageView
import com.android.internal.policy.ScreenDecorationsUtils
import com.android.systemui.R
import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.wm.shell.TaskView
/**
@@ -46,7 +48,9 @@ class DetailDialog(
val broadcastSender: BroadcastSender,
val taskView: TaskView,
val pendingIntent: PendingIntent,
- val cvh: ControlViewHolder
+ val cvh: ControlViewHolder,
+ val keyguardStateController: KeyguardStateController,
+ val activityStarter: ActivityStarter
) : Dialog(
activityContext,
R.style.Theme_SystemUI_Dialog_Control_DetailPanel
@@ -145,12 +149,25 @@ class DetailDialog(
requireViewById<ImageView>(R.id.control_detail_open_in_app).apply {
setOnClickListener { v: View ->
- // Remove the task explicitly, since onRelease() callback will be executed after
- // startActivity() below is called.
removeDetailTask()
dismiss()
- broadcastSender.closeSystemDialogs()
- pendingIntent.send()
+
+ val action = ActivityStarter.OnDismissAction {
+ // Remove the task explicitly, since onRelease() callback will be executed after
+ // startActivity() below is called.
+ broadcastSender.closeSystemDialogs()
+ pendingIntent.send()
+ false
+ }
+ if (keyguardStateController.isUnlocked()) {
+ action.onDismiss()
+ } else {
+ activityStarter.dismissKeyguardThenExecute(
+ action,
+ null /* cancel */,
+ true /* afterKeyguardGone */
+ )
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index 0f8687183e94..e9caaaf54422 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -150,12 +150,13 @@ class MediaCarouselController @Inject constructor(
}
private val configListener = object : ConfigurationController.ConfigurationListener {
override fun onDensityOrFontScaleChanged() {
- recreatePlayers()
+ // System font changes should only happen when UMO is offscreen or a flicker may occur
+ updatePlayers(recreateMedia = true)
inflateSettingsButton()
}
override fun onThemeChanged() {
- recreatePlayers()
+ updatePlayers(recreateMedia = false)
inflateSettingsButton()
}
@@ -165,7 +166,7 @@ class MediaCarouselController @Inject constructor(
}
override fun onUiModeChanged() {
- recreatePlayers()
+ updatePlayers(recreateMedia = false)
inflateSettingsButton()
}
}
@@ -539,7 +540,7 @@ class MediaCarouselController @Inject constructor(
}
}
- private fun recreatePlayers() {
+ private fun updatePlayers(recreateMedia: Boolean) {
pageIndicator.tintList = ColorStateList.valueOf(
context.getColor(R.color.media_paging_indicator)
)
@@ -554,7 +555,9 @@ class MediaCarouselController @Inject constructor(
}
} else {
val isSsReactivated = MediaPlayerData.isSsReactivated(key)
- removePlayer(key, dismissMediaData = false, dismissRecommendation = false)
+ if (recreateMedia) {
+ removePlayer(key, dismissMediaData = false, dismissRecommendation = false)
+ }
addOrUpdatePlayer(
key = key, oldKey = null, data = data, isSsReactivated = isSsReactivated)
}
@@ -945,6 +948,7 @@ internal object MediaPlayerData {
.thenByDescending { shouldPrioritizeSs == it.isSsMediaRec }
.thenByDescending { !it.data.resumption }
.thenByDescending { it.data.playbackLocation != MediaData.PLAYBACK_CAST_REMOTE }
+ .thenByDescending { it.data.lastActive }
.thenByDescending { it.updateTime }
.thenByDescending { it.data.notificationKey }
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
index 429f2df6b50c..731e17742538 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerNotificationWarnings.java
@@ -292,10 +292,8 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
return;
}
- final int warningLevel = mContext.getResources().getInteger(
- com.android.internal.R.integer.config_lowBatteryWarningLevel);
final String percentage = NumberFormat.getPercentInstance()
- .format((double) warningLevel / 100.0);
+ .format((double) mCurrentBatterySnapshot.getBatteryLevel() / 100.0);
final String title = mContext.getString(R.string.battery_low_title);
final String contentText = mContext.getString(
R.string.battery_low_description, percentage);
@@ -309,7 +307,6 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
.setContentText(contentText)
.setContentTitle(title)
.setOnlyAlertOnce(true)
- .setOngoing(true)
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
@@ -340,8 +337,7 @@ public class PowerNotificationWarnings implements PowerUI.WarningsUI {
}
private boolean showSevereLowBatteryDialog() {
- final boolean isSevereState = !mCurrentBatterySnapshot.isHybrid() || mBucket < -1;
- return isSevereState && mUseSevereDialog;
+ return mBucket < -1 && mUseSevereDialog;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
index dcdd784bd2a1..67dae9e7a0ea 100644
--- a/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
+++ b/packages/SystemUI/src/com/android/systemui/power/PowerUI.java
@@ -359,11 +359,7 @@ public class PowerUI extends CoreStartable implements CommandQueue.Callbacks {
}
mWarnings.updateSnapshot(mCurrentBatteryStateSnapshot);
- if (mCurrentBatteryStateSnapshot.isHybrid()) {
- maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
- } else {
- maybeShowBatteryWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
- }
+ maybeShowHybridWarning(mCurrentBatteryStateSnapshot, mLastBatteryStateSnapshot);
}
// updates the time estimate if we don't have one or battery level has changed.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
index 90a3d4586fd3..f1fdae7db482 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/dialog/InternetDialogController.java
@@ -451,11 +451,11 @@ public class InternetDialogController implements AccessPointController.AccessPoi
final SignalStrength strength = mTelephonyManager.getSignalStrength();
int level = (strength == null) ? 0 : strength.getLevel();
int numLevels = SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
- if ((mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId))
- || isCarrierNetworkActive) {
- level = isCarrierNetworkActive
- ? SignalStrength.NUM_SIGNAL_STRENGTH_BINS
- : (level + 1);
+ if (isCarrierNetworkActive) {
+ level = getCarrierNetworkLevel();
+ numLevels = WifiEntry.WIFI_LEVEL_MAX + 1;
+ } else if (mSubscriptionManager != null && shouldInflateSignalStrength(mDefaultDataSubId)) {
+ level += 1;
numLevels += 1;
}
return getSignalStrengthIcon(mContext, level, numLevels, NO_CELL_DATA_TYPE_ICON,
@@ -689,6 +689,17 @@ public class InternetDialogController implements AccessPointController.AccessPoi
return mergedCarrierEntry != null && mergedCarrierEntry.isDefaultNetwork();
}
+ int getCarrierNetworkLevel() {
+ final MergedCarrierEntry mergedCarrierEntry =
+ mAccessPointController.getMergedCarrierEntry();
+ if (mergedCarrierEntry == null) return WifiEntry.WIFI_LEVEL_MIN;
+
+ int level = mergedCarrierEntry.getLevel();
+ // To avoid icons not found with WIFI_LEVEL_UNREACHABLE(-1), use WIFI_LEVEL_MIN(0) instead.
+ if (level < WifiEntry.WIFI_LEVEL_MIN) level = WifiEntry.WIFI_LEVEL_MIN;
+ return level;
+ }
+
@WorkerThread
void setMergedCarrierWifiEnabledIfNeed(int subId, boolean enabled) {
// If the Carrier Provisions Wi-Fi Merged Networks enabled, do not set the merged carrier
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
index 22a6b0a4bae4..929dda60b1b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableView.java
@@ -157,7 +157,7 @@ public abstract class ExpandableView extends FrameLayout implements Dumpable {
@Override
public boolean pointInView(float localX, float localY, float slop) {
- float top = mClipTopAmount;
+ float top = Math.max(0, mClipTopAmount);
float bottom = mActualHeight;
return localX >= -slop && localY >= top - slop && localX < ((mRight - mLeft) + slop) &&
localY < (bottom + slop);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
index 0166fa25d526..6a6a65a601fb 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/DetailDialogTest.kt
@@ -22,6 +22,8 @@ import android.testing.TestableLooper
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastSender
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.wm.shell.TaskView
import org.junit.Before
import org.junit.Test
@@ -45,6 +47,10 @@ class DetailDialogTest : SysuiTestCase() {
private lateinit var controlViewHolder: ControlViewHolder
@Mock
private lateinit var pendingIntent: PendingIntent
+ @Mock
+ private lateinit var keyguardStateController: KeyguardStateController
+ @Mock
+ private lateinit var activityStarter: ActivityStarter
@Before
fun setUp() {
@@ -69,7 +75,9 @@ class DetailDialogTest : SysuiTestCase() {
broadcastSender,
taskView,
pendingIntent,
- controlViewHolder
+ controlViewHolder,
+ keyguardStateController,
+ activityStarter
)
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
index ceb811b8d8aa..219b3c8561b2 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaCarouselControllerTest.kt
@@ -136,9 +136,18 @@ class MediaCarouselControllerTest : SysuiTestCase() {
val resume2 = Triple("resume 2",
DATA.copy(active = false, isPlaying = false,
- playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true),
1000L)
+ val activeMoreRecent = Triple("active more recent",
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true, lastActive = 2L),
+ 1000L)
+
+ val activeLessRecent = Triple("active less recent",
+ DATA.copy(active = false, isPlaying = false,
+ playbackLocation = MediaData.PLAYBACK_LOCAL, resumption = true, lastActive = 1L),
+ 1000L)
// Expected ordering for media players:
// Actively playing local sessions
// Actively playing cast sessions
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
index e42ae1c2f878..9a7b129f7597 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaDataManagerTest.kt
@@ -38,6 +38,7 @@ import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.After
import org.junit.Before
+import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@@ -574,6 +575,7 @@ class MediaDataManagerTest : SysuiTestCase() {
.onSmartspaceMediaDataLoaded(anyObject(), anyObject(), anyBoolean())
}
+ @Ignore("b/233283726")
@Test
fun testOnSmartspaceMediaDataLoaded_hasNoneMediaTarget_callsRemoveListener() {
smartspaceMediaDataProvider.onTargetsAvailable(listOf(mediaSmartspaceTarget))
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
index 4a8cb0b76dc4..7b1e5c9f7264 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/dialog/InternetDialogControllerTest.java
@@ -1,9 +1,15 @@
package com.android.systemui.qs.tiles.dialog;
import static android.provider.Settings.Global.AIRPLANE_MODE_ON;
+import static android.telephony.SignalStrength.NUM_SIGNAL_STRENGTH_BINS;
+import static android.telephony.SignalStrength.SIGNAL_STRENGTH_GREAT;
+import static android.telephony.SignalStrength.SIGNAL_STRENGTH_POOR;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_HORIZONTAL_WEIGHT;
import static com.android.systemui.qs.tiles.dialog.InternetDialogController.TOAST_PARAMS_VERTICAL_WEIGHT;
+import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MAX;
+import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_MIN;
+import static com.android.wifitrackerlib.WifiEntry.WIFI_LEVEL_UNREACHABLE;
import static com.google.common.truth.Truth.assertThat;
@@ -17,6 +23,7 @@ import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -28,6 +35,7 @@ import android.net.ConnectivityManager;
import android.net.wifi.WifiManager;
import android.os.Handler;
import android.telephony.ServiceState;
+import android.telephony.SignalStrength;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.testing.AndroidTestingRunner;
@@ -140,6 +148,8 @@ public class InternetDialogControllerTest extends SysuiTestCase {
private View mDialogLaunchView;
@Mock
private WifiStateWorker mWifiStateWorker;
+ @Mock
+ private SignalStrength mSignalStrength;
private TestableResources mTestableResources;
private InternetDialogController mInternetDialogController;
@@ -152,6 +162,8 @@ public class InternetDialogControllerTest extends SysuiTestCase {
MockitoAnnotations.initMocks(this);
mTestableResources = mContext.getOrCreateTestableResources();
doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(anyInt());
+ when(mTelephonyManager.getSignalStrength()).thenReturn(mSignalStrength);
+ when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_GREAT);
when(mKeyguardStateController.isUnlocked()).thenReturn(true);
when(mConnectedEntry.isDefaultNetwork()).thenReturn(true);
when(mConnectedEntry.hasInternetAccess()).thenReturn(true);
@@ -380,7 +392,7 @@ public class InternetDialogControllerTest extends SysuiTestCase {
@Test
public void getInternetWifiDrawable_withWifiLevelUnreachable_returnNull() {
- when(mConnectedEntry.getLevel()).thenReturn(WifiEntry.WIFI_LEVEL_UNREACHABLE);
+ when(mConnectedEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE);
Drawable drawable = mInternetDialogController.getInternetWifiDrawable(mConnectedEntry);
@@ -638,6 +650,57 @@ public class InternetDialogControllerTest extends SysuiTestCase {
assertThat(mInternetDialogController.isWifiScanEnabled()).isTrue();
}
+ @Test
+ public void getSignalStrengthDrawableWithLevel_carrierNetworkIsNotActive_useMobileDataLevel() {
+ // Fake mobile data level as SIGNAL_STRENGTH_POOR(1)
+ when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR);
+ // Fake carrier network level as WIFI_LEVEL_MAX(4)
+ when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
+
+ InternetDialogController spyController = spy(mInternetDialogController);
+ spyController.getSignalStrengthDrawableWithLevel(false /* isCarrierNetworkActive */);
+
+ verify(spyController).getSignalStrengthIcon(any(), eq(SIGNAL_STRENGTH_POOR),
+ eq(NUM_SIGNAL_STRENGTH_BINS), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void getSignalStrengthDrawableWithLevel_carrierNetworkIsActive_useCarrierNetworkLevel() {
+ // Fake mobile data level as SIGNAL_STRENGTH_POOR(1)
+ when(mSignalStrength.getLevel()).thenReturn(SIGNAL_STRENGTH_POOR);
+ // Fake carrier network level as WIFI_LEVEL_MAX(4)
+ when(mInternetDialogController.getCarrierNetworkLevel()).thenReturn(WIFI_LEVEL_MAX);
+
+ InternetDialogController spyController = spy(mInternetDialogController);
+ spyController.getSignalStrengthDrawableWithLevel(true /* isCarrierNetworkActive */);
+
+ verify(spyController).getSignalStrengthIcon(any(), eq(WIFI_LEVEL_MAX),
+ eq(WIFI_LEVEL_MAX + 1), anyInt(), anyBoolean());
+ }
+
+ @Test
+ public void getCarrierNetworkLevel_mergedCarrierEntryIsNull_returnMinLevel() {
+ when(mAccessPointController.getMergedCarrierEntry()).thenReturn(null);
+
+ assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN);
+ }
+
+ @Test
+ public void getCarrierNetworkLevel_getUnreachableLevel_returnMinLevel() {
+ when(mMergedCarrierEntry.getLevel()).thenReturn(WIFI_LEVEL_UNREACHABLE);
+
+ assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(WIFI_LEVEL_MIN);
+ }
+
+ @Test
+ public void getCarrierNetworkLevel_getAvailableLevel_returnSameLevel() {
+ for (int level = WIFI_LEVEL_MIN; level <= WIFI_LEVEL_MAX; level++) {
+ when(mMergedCarrierEntry.getLevel()).thenReturn(level);
+
+ assertThat(mInternetDialogController.getCarrierNetworkLevel()).isEqualTo(level);
+ }
+ }
+
private String getResourcesString(String name) {
return mContext.getResources().getString(getResourcesId(name));
}
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f26d9f9f49e6..76cac934fdfe 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -24,6 +24,7 @@ import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_INACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_NOT_CONFIGURED;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.isValidSubscriptionId;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionSnapshot;
import static com.android.server.vcn.TelephonySubscriptionTracker.TelephonySubscriptionTrackerCallback;
@@ -167,6 +168,10 @@ public class VcnManagementService extends IVcnManagementService.Stub {
static final String VCN_CONFIG_FILE =
new File(Environment.getDataSystemDirectory(), "vcn/configs.xml").getPath();
+ // TODO(b/176956496): Directly use CarrierServiceBindHelper.UNBIND_DELAY_MILLIS
+ @VisibleForTesting(visibility = Visibility.PRIVATE)
+ static final long CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS = TimeUnit.SECONDS.toMillis(30);
+
/* Binder context for this service */
@NonNull private final Context mContext;
@NonNull private final Dependencies mDeps;
@@ -360,15 +365,12 @@ public class VcnManagementService extends IVcnManagementService.Stub {
/** Notifies the VcnManagementService that external dependencies can be set up. */
public void systemReady() {
- // Always run on the handler thread to ensure consistency.
- mHandler.post(() -> {
- mNetworkProvider.register();
- mContext.getSystemService(ConnectivityManager.class)
- .registerNetworkCallback(
- new NetworkRequest.Builder().clearCapabilities().build(),
- mTrackingNetworkCallback);
- mTelephonySubscriptionTracker.register();
- });
+ mNetworkProvider.register();
+ mContext.getSystemService(ConnectivityManager.class)
+ .registerNetworkCallback(
+ new NetworkRequest.Builder().clearCapabilities().build(),
+ mTrackingNetworkCallback);
+ mTelephonySubscriptionTracker.register();
}
private void enforcePrimaryUser() {
@@ -509,15 +511,22 @@ public class VcnManagementService extends IVcnManagementService.Stub {
if (!mVcns.containsKey(subGrp)) {
startVcnLocked(subGrp, entry.getValue());
}
+
+ // Cancel any scheduled teardowns for active subscriptions
+ mHandler.removeCallbacksAndMessages(mVcns.get(subGrp));
}
}
- // Schedule teardown of any VCN instances that have lost carrier privileges
+ // Schedule teardown of any VCN instances that have lost carrier privileges (after a
+ // delay)
for (Entry<ParcelUuid, Vcn> entry : mVcns.entrySet()) {
final ParcelUuid subGrp = entry.getKey();
final VcnConfig config = mConfigs.get(subGrp);
final boolean isActiveSubGrp = isActiveSubGroup(subGrp, snapshot);
+ final boolean isValidActiveDataSubIdNotInVcnSubGrp =
+ isValidSubscriptionId(snapshot.getActiveDataSubscriptionId())
+ && !isActiveSubGroup(subGrp, snapshot);
// TODO(b/193687515): Support multiple VCNs active at the same time
if (config == null
@@ -527,12 +536,31 @@ public class VcnManagementService extends IVcnManagementService.Stub {
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- stopVcnLocked(uuidToTeardown);
-
- // TODO(b/181789060): invoke asynchronously after Vcn notifies
- // through VcnCallback
- notifyAllPermissionedStatusCallbacksLocked(
- uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ // TODO(b/193687515): Support multiple VCNs active at the same time
+ // If directly switching to a subscription not in the current group,
+ // teardown immediately to prevent other subscription's network from being
+ // outscored by the VCN. Otherwise, teardown after a delay to ensure that
+ // SIM profile switches do not trigger the VCN to cycle.
+ final long teardownDelayMs =
+ isValidActiveDataSubIdNotInVcnSubGrp
+ ? 0
+ : CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS;
+ mHandler.postDelayed(() -> {
+ synchronized (mLock) {
+ // Guard against case where this is run after a old instance was
+ // torn down, and a new instance was started. Verify to ensure
+ // correct instance is torn down. This could happen as a result of a
+ // Carrier App manually removing/adding a VcnConfig.
+ if (mVcns.get(uuidToTeardown) == instanceToTeardown) {
+ stopVcnLocked(uuidToTeardown);
+
+ // TODO(b/181789060): invoke asynchronously after Vcn notifies
+ // through VcnCallback
+ notifyAllPermissionedStatusCallbacksLocked(
+ uuidToTeardown, VCN_STATUS_CODE_INACTIVE);
+ }
+ }
+ }, instanceToTeardown, teardownDelayMs);
} else {
// If this VCN's status has not changed, update it with the new snapshot
entry.getValue().updateSubscriptionSnapshot(mLastSnapshot);
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 7253b4946661..7fe3c1f5272b 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -2260,7 +2260,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
private void dumpHelp(PrintWriter pw) {
pw.println("Battery stats (batterystats) dump options:");
pw.println(" [--checkin] [--proto] [--history] [--history-start] [--charged] [-c]");
- pw.println(" [--daily] [--reset] [--write] [--new-daily] [--read-daily] [-h] [<package.name>]");
+ pw.println(" [--daily] [--reset] [--reset-all] [--write] [--new-daily] [--read-daily]");
+ pw.println(" [-h] [<package.name>]");
pw.println(" --checkin: generate output for a checkin report; will write (and clear) the");
pw.println(" last old completed stats when they had been reset.");
pw.println(" -c: write the current stats in checkin format.");
@@ -2271,6 +2272,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub
pw.println(" --charged: only output data since last charged.");
pw.println(" --daily: only output full daily data.");
pw.println(" --reset: reset the stats, clearing all current data.");
+ pw.println(" --reset-all: reset the stats, clearing all current and past data.");
pw.println(" --write: force write current collected stats to disk.");
pw.println(" --new-daily: immediately create and write new daily stats record.");
pw.println(" --read-daily: read-load last written daily stats.");
@@ -2407,6 +2409,14 @@ public final class BatteryStatsService extends IBatteryStats.Stub
flags |= BatteryStats.DUMP_CHARGED_ONLY;
} else if ("--daily".equals(arg)) {
flags |= BatteryStats.DUMP_DAILY_ONLY;
+ } else if ("--reset-all".equals(arg)) {
+ awaitCompletion();
+ synchronized (mStats) {
+ mStats.resetAllStatsCmdLocked();
+ mBatteryUsageStatsStore.removeAllSnapshots();
+ pw.println("Battery stats and history reset.");
+ noOutput = true;
+ }
} else if ("--reset".equals(arg)) {
awaitCompletion();
synchronized (mStats) {
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 03eddc9634d7..e4f624d4ac28 100644
--- a/services/core/java/com/android/server/am/TEST_MAPPING
+++ b/services/core/java/com/android/server/am/TEST_MAPPING
@@ -63,6 +63,32 @@
]
}
],
+ "presubmit-large": [
+ {
+ "name": "CtsUsageStatsTestCases",
+ "file_patterns": [
+ "ActivityManagerService\\.java",
+ "BroadcastQueue\\.java"
+ ],
+ "options": [
+ {
+ "include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.MediumTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ],
"postsubmit": [
{
"name": "FrameworksServicesTests",
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index f93e06d73a10..2564ae8d8783 100644
--- a/services/core/java/com/android/server/content/ContentService.java
+++ b/services/core/java/com/android/server/content/ContentService.java
@@ -26,6 +26,7 @@ import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.RestrictionLevel;
import android.app.ActivityManagerInternal;
import android.app.AppGlobals;
import android.app.AppOpsManager;
@@ -52,6 +53,7 @@ import android.content.pm.ProviderInfo;
import android.database.IContentObserver;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
+import android.os.AppBackgroundRestrictionsInfo;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -1544,7 +1546,8 @@ public final class ContentService extends IContentService.Stub {
}
if (procState <= ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND || isUidActive) {
FrameworkStatsLog.write(FrameworkStatsLog.SYNC_EXEMPTION_OCCURRED,
- callingUid, getProcStateForStatsd(procState), isUidActive);
+ callingUid, getProcStateForStatsd(procState), isUidActive,
+ getRestrictionLevelForStatsd(ami.getRestrictionLevel(callingUid)));
return ContentResolver.SYNC_EXEMPTION_PROMOTE_BUCKET;
}
return ContentResolver.SYNC_EXEMPTION_NONE;
@@ -1599,6 +1602,27 @@ public final class ContentService extends IContentService.Stub {
}
}
+ private int getRestrictionLevelForStatsd(@RestrictionLevel int level) {
+ switch (level) {
+ case ActivityManager.RESTRICTION_LEVEL_UNKNOWN:
+ return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
+ case ActivityManager.RESTRICTION_LEVEL_UNRESTRICTED:
+ return AppBackgroundRestrictionsInfo.LEVEL_UNRESTRICTED;
+ case ActivityManager.RESTRICTION_LEVEL_EXEMPTED:
+ return AppBackgroundRestrictionsInfo.LEVEL_EXEMPTED;
+ case ActivityManager.RESTRICTION_LEVEL_ADAPTIVE_BUCKET:
+ return AppBackgroundRestrictionsInfo.LEVEL_ADAPTIVE_BUCKET;
+ case ActivityManager.RESTRICTION_LEVEL_RESTRICTED_BUCKET:
+ return AppBackgroundRestrictionsInfo.LEVEL_RESTRICTED_BUCKET;
+ case ActivityManager.RESTRICTION_LEVEL_BACKGROUND_RESTRICTED:
+ return AppBackgroundRestrictionsInfo.LEVEL_BACKGROUND_RESTRICTED;
+ case ActivityManager.RESTRICTION_LEVEL_HIBERNATION:
+ return AppBackgroundRestrictionsInfo.LEVEL_HIBERNATION;
+ default:
+ return AppBackgroundRestrictionsInfo.LEVEL_UNKNOWN;
+ }
+ }
+
/** {@hide} */
@VisibleForTesting
public static final class ObserverNode {
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index b624d438a4f7..72612a0468cd 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -146,6 +146,7 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.OptionalInt;
+import java.util.function.Consumer;
/** The system implementation of {@link IInputManager} that manages input devices. */
public class InputManagerService extends IInputManager.Stub
@@ -168,6 +169,8 @@ public class InputManagerService extends IInputManager.Stub
private static final int MSG_POINTER_DISPLAY_ID_CHANGED = 7;
private static final int DEFAULT_VIBRATION_MAGNITUDE = 192;
+ private static final AdditionalDisplayInputProperties
+ DEFAULT_ADDITIONAL_DISPLAY_INPUT_PROPERTIES = new AdditionalDisplayInputProperties();
/**
* We know the issue and are working to fix it, so suppressing the toast to not annoy
@@ -281,6 +284,7 @@ public class InputManagerService extends IInputManager.Stub
// Guards per-display input properties and properties relating to the mouse pointer.
// Threads can wait on this lock to be notified the next time the display on which the mouse
// pointer is shown has changed.
+ // WARNING: Do not call other services outside of input while holding this lock.
private final Object mAdditionalDisplayInputPropertiesLock = new Object();
// Forces the PointerController to target a specific display id.
@@ -299,6 +303,11 @@ public class InputManagerService extends IInputManager.Stub
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties =
new SparseArray<>();
+ // This contains the per-display properties that are currently applied by native code. It should
+ // be kept in sync with the properties for mRequestedPointerDisplayId.
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private final AdditionalDisplayInputProperties mCurrentDisplayProperties =
+ new AdditionalDisplayInputProperties();
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
private int mIconType = PointerIcon.TYPE_NOT_SPECIFIED;
@GuardedBy("mAdditionalDisplayInputPropertiesLock")
@@ -571,27 +580,19 @@ public class InputManagerService extends IInputManager.Stub
}
private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
- synchronized (mAdditionalDisplayInputPropertiesLock) {
- final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
- for (int i = viewports.size() - 1; i >= 0; --i) {
- vArray[i] = viewports.get(i);
- }
- mNative.setDisplayViewports(vArray);
- // Always attempt to update the pointer display when viewports change.
- updatePointerDisplayId();
+ final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
+ for (int i = viewports.size() - 1; i >= 0; --i) {
+ vArray[i] = viewports.get(i);
+ }
+ mNative.setDisplayViewports(vArray);
- if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
- final AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
- if (properties != null) {
- updatePointerIconVisibleLocked(properties.pointerIconVisible);
- updatePointerAccelerationLocked(properties.pointerAcceleration);
- return;
- }
+ // Attempt to update the pointer display when viewports change when there is no override.
+ // Take care to not make calls to window manager while holding internal locks.
+ final int pointerDisplayId = mWindowManagerCallbacks.getPointerDisplayId();
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ if (mOverriddenPointerDisplayId == Display.INVALID_DISPLAY) {
+ updatePointerDisplayIdLocked(pointerDisplayId);
}
- updatePointerIconVisibleLocked(
- AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE);
- updatePointerAccelerationLocked(IInputConstants.DEFAULT_POINTER_ACCELERATION);
}
}
@@ -1743,12 +1744,7 @@ public class InputManagerService extends IInputManager.Stub
mPointerIconDisplayContext = null;
}
- synchronized (mAdditionalDisplayInputPropertiesLock) {
- setPointerIconVisible(AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE,
- displayId);
- setPointerAcceleration(AdditionalDisplayInputProperties.DEFAULT_POINTER_ACCELERATION,
- displayId);
- }
+ updateAdditionalDisplayInputProperties(displayId, AdditionalDisplayInputProperties::reset);
mNative.displayRemoved(displayId);
}
@@ -1835,57 +1831,13 @@ public class InputManagerService extends IInputManager.Stub
}
private void setPointerAcceleration(float acceleration, int displayId) {
- synchronized (mAdditionalDisplayInputPropertiesLock) {
- AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(displayId);
- if (properties == null) {
- properties = new AdditionalDisplayInputProperties();
- mAdditionalDisplayInputProperties.put(displayId, properties);
- }
- properties.pointerAcceleration = acceleration;
- if (properties.allDefaults()) {
- mAdditionalDisplayInputProperties.remove(displayId);
- }
- if (mOverriddenPointerDisplayId == displayId) {
- updatePointerAccelerationLocked(acceleration);
- }
- }
- }
-
- @GuardedBy("mAdditionalDisplayInputPropertiesLock")
- private void updatePointerAccelerationLocked(float acceleration) {
- mNative.setPointerAcceleration(acceleration);
+ updateAdditionalDisplayInputProperties(displayId,
+ properties -> properties.pointerAcceleration = acceleration);
}
private void setPointerIconVisible(boolean visible, int displayId) {
- synchronized (mAdditionalDisplayInputPropertiesLock) {
- AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(displayId);
- if (properties == null) {
- properties = new AdditionalDisplayInputProperties();
- mAdditionalDisplayInputProperties.put(displayId, properties);
- }
- properties.pointerIconVisible = visible;
- if (properties.allDefaults()) {
- mAdditionalDisplayInputProperties.remove(displayId);
- }
- if (mOverriddenPointerDisplayId == displayId) {
- updatePointerIconVisibleLocked(visible);
- }
- }
- }
-
- @GuardedBy("mAdditionalDisplayInputPropertiesLock")
- private void updatePointerIconVisibleLocked(boolean visible) {
- if (visible) {
- if (mIconType == PointerIcon.TYPE_CUSTOM) {
- mNative.setCustomPointerIcon(mIcon);
- } else {
- mNative.setPointerIconType(mIconType);
- }
- } else {
- mNative.setPointerIconType(PointerIcon.TYPE_NULL);
- }
+ updateAdditionalDisplayInputProperties(displayId,
+ properties -> properties.pointerIconVisible = visible);
}
private void registerPointerSpeedSettingObserver() {
@@ -2023,22 +1975,18 @@ public class InputManagerService extends IInputManager.Stub
/**
* Update the display on which the mouse pointer is shown.
- * If there is an overridden display for the mouse pointer, use that. Otherwise, query
- * WindowManager for the pointer display.
*
* @return true if the pointer displayId changed, false otherwise.
*/
- private boolean updatePointerDisplayId() {
- synchronized (mAdditionalDisplayInputPropertiesLock) {
- final int pointerDisplayId = mOverriddenPointerDisplayId != Display.INVALID_DISPLAY
- ? mOverriddenPointerDisplayId : mWindowManagerCallbacks.getPointerDisplayId();
- if (mRequestedPointerDisplayId == pointerDisplayId) {
- return false;
- }
- mRequestedPointerDisplayId = pointerDisplayId;
- mNative.setPointerDisplayId(pointerDisplayId);
- return true;
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private boolean updatePointerDisplayIdLocked(int pointerDisplayId) {
+ if (mRequestedPointerDisplayId == pointerDisplayId) {
+ return false;
}
+ mRequestedPointerDisplayId = pointerDisplayId;
+ mNative.setPointerDisplayId(pointerDisplayId);
+ applyAdditionalDisplayInputProperties();
+ return true;
}
private void handlePointerDisplayIdChanged(PointerDisplayIdChangedArgs args) {
@@ -2051,25 +1999,23 @@ public class InputManagerService extends IInputManager.Stub
args.mPointerDisplayId, args.mXPosition, args.mYPosition);
}
- private boolean setVirtualMousePointerDisplayIdBlocking(int displayId) {
- // Indicates whether this request is for removing the override.
- final boolean removingOverride = displayId == Display.INVALID_DISPLAY;
+ private boolean setVirtualMousePointerDisplayIdBlocking(int overrideDisplayId) {
+ final boolean isRemovingOverride = overrideDisplayId == Display.INVALID_DISPLAY;
+
+ // Take care to not make calls to window manager while holding internal locks.
+ final int resolvedDisplayId = isRemovingOverride
+ ? mWindowManagerCallbacks.getPointerDisplayId()
+ : overrideDisplayId;
synchronized (mAdditionalDisplayInputPropertiesLock) {
- mOverriddenPointerDisplayId = displayId;
- if (!removingOverride) {
- final AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(displayId);
- if (properties != null) {
- updatePointerAccelerationLocked(properties.pointerAcceleration);
- updatePointerIconVisibleLocked(properties.pointerIconVisible);
- }
- }
- if (!updatePointerDisplayId() && mAcknowledgedPointerDisplayId == displayId) {
+ mOverriddenPointerDisplayId = overrideDisplayId;
+
+ if (!updatePointerDisplayIdLocked(resolvedDisplayId)
+ && mAcknowledgedPointerDisplayId == resolvedDisplayId) {
// The requested pointer display is already set.
return true;
}
- if (removingOverride && mAcknowledgedPointerDisplayId == Display.INVALID_DISPLAY) {
+ if (isRemovingOverride && mAcknowledgedPointerDisplayId == Display.INVALID_DISPLAY) {
// The pointer display override is being removed, but the current pointer display
// is already invalid. This can happen when the PointerController is destroyed as a
// result of the removal of all input devices that can control the pointer.
@@ -2087,7 +2033,7 @@ public class InputManagerService extends IInputManager.Stub
// reported new displayId is the one we requested. This check ensures that if two
// competing overrides are requested in succession, the caller can be notified if one
// of them fails.
- return removingOverride || mAcknowledgedPointerDisplayId == displayId;
+ return isRemovingOverride || mAcknowledgedPointerDisplayId == overrideDisplayId;
}
}
@@ -2393,15 +2339,10 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mAdditionalDisplayInputPropertiesLock) {
mIcon = null;
mIconType = iconType;
- if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
- final AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
- if (properties == null || properties.pointerIconVisible) {
- mNative.setPointerIconType(mIconType);
- }
- } else {
- mNative.setPointerIconType(mIconType);
- }
+
+ if (!mCurrentDisplayProperties.pointerIconVisible) return;
+
+ mNative.setPointerIconType(mIconType);
}
}
@@ -2412,17 +2353,10 @@ public class InputManagerService extends IInputManager.Stub
synchronized (mAdditionalDisplayInputPropertiesLock) {
mIconType = PointerIcon.TYPE_CUSTOM;
mIcon = icon;
- if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
- final AdditionalDisplayInputProperties properties =
- mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
- if (properties == null || properties.pointerIconVisible) {
- // Only set the icon if it is not currently hidden; otherwise, it will be set
- // once it's no longer hidden.
- mNative.setCustomPointerIcon(mIcon);
- }
- } else {
- mNative.setCustomPointerIcon(mIcon);
- }
+
+ if (!mCurrentDisplayProperties.pointerIconVisible) return;
+
+ mNative.setCustomPointerIcon(mIcon);
}
}
@@ -3852,14 +3786,79 @@ public class InputManagerService extends IInputManager.Stub
(float) IInputConstants.DEFAULT_POINTER_ACCELERATION;
// The pointer acceleration for this display.
- public float pointerAcceleration = DEFAULT_POINTER_ACCELERATION;
+ public float pointerAcceleration;
// Whether the pointer icon should be visible or hidden on this display.
- public boolean pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
+ public boolean pointerIconVisible;
+
+ AdditionalDisplayInputProperties() {
+ reset();
+ }
public boolean allDefaults() {
return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0
&& pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE;
}
+
+ public void reset() {
+ pointerAcceleration = DEFAULT_POINTER_ACCELERATION;
+ pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
+ }
+ }
+
+ private void applyAdditionalDisplayInputProperties() {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(mRequestedPointerDisplayId);
+ if (properties == null) properties = DEFAULT_ADDITIONAL_DISPLAY_INPUT_PROPERTIES;
+ applyAdditionalDisplayInputPropertiesLocked(properties);
+ }
+ }
+
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private void applyAdditionalDisplayInputPropertiesLocked(
+ AdditionalDisplayInputProperties properties) {
+ // Handle changes to each of the individual properties.
+ if (properties.pointerIconVisible != mCurrentDisplayProperties.pointerIconVisible) {
+ mCurrentDisplayProperties.pointerIconVisible = properties.pointerIconVisible;
+ if (properties.pointerIconVisible) {
+ if (mIconType == PointerIcon.TYPE_CUSTOM) {
+ Objects.requireNonNull(mIcon);
+ mNative.setCustomPointerIcon(mIcon);
+ } else {
+ mNative.setPointerIconType(mIconType);
+ }
+ } else {
+ mNative.setPointerIconType(PointerIcon.TYPE_NULL);
+ }
+ }
+
+ if (properties.pointerAcceleration != mCurrentDisplayProperties.pointerAcceleration) {
+ mCurrentDisplayProperties.pointerAcceleration = properties.pointerAcceleration;
+ mNative.setPointerAcceleration(properties.pointerAcceleration);
+ }
+ }
+
+ private void updateAdditionalDisplayInputProperties(int displayId,
+ Consumer<AdditionalDisplayInputProperties> updater) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(displayId);
+ if (properties == null) {
+ properties = new AdditionalDisplayInputProperties();
+ mAdditionalDisplayInputProperties.put(displayId, properties);
+ }
+ updater.accept(properties);
+ if (properties.allDefaults()) {
+ mAdditionalDisplayInputProperties.remove(displayId);
+ }
+ if (displayId != mRequestedPointerDisplayId) {
+ Log.i(TAG, "Not applying additional properties for display " + displayId
+ + " because the pointer is currently targeting display "
+ + mRequestedPointerDisplayId + ".");
+ return;
+ }
+ applyAdditionalDisplayInputPropertiesLocked(properties);
+ }
}
}
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index 31d5136c80a5..e5eed9928411 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -17,6 +17,7 @@
package com.android.server.location;
import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.app.compat.CompatChanges.isChangeEnabled;
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_AWARE;
@@ -1030,8 +1031,10 @@ public class LocationManagerService extends ILocationManager.Stub implements
@Override
public void addProviderRequestListener(IProviderRequestListener listener) {
- for (LocationProviderManager manager : mProviderManagers) {
- manager.addProviderRequestListener(listener);
+ if (mContext.checkCallingOrSelfPermission(INTERACT_ACROSS_USERS) == PERMISSION_GRANTED) {
+ for (LocationProviderManager manager : mProviderManagers) {
+ manager.addProviderRequestListener(listener);
+ }
}
}
diff --git a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
index 1937852fa333..098e8f74749c 100644
--- a/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
+++ b/services/core/java/com/android/server/media/projection/MediaProjectionManagerService.java
@@ -45,14 +45,15 @@ import android.os.RemoteException;
import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.Slog;
+import android.view.ContentRecordingSession;
import android.window.WindowContainerToken;
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.DumpUtils;
import com.android.server.LocalServices;
import com.android.server.SystemService;
-import com.android.server.SystemService.TargetUser;
import com.android.server.Watchdog;
+import com.android.server.wm.WindowManagerInternal;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -381,6 +382,27 @@ public final class MediaProjectionManagerService extends SystemService
}
}
+ /**
+ * Updates the current content mirroring session.
+ */
+ @Override
+ public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession,
+ @NonNull IMediaProjection projection) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mLock) {
+ if (!isValidMediaProjection(projection)) {
+ throw new SecurityException("Invalid media projection");
+ }
+ LocalServices.getService(
+ WindowManagerInternal.class).setContentRecordingSession(
+ incomingSession);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
@Override // Binder call
public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index 8552bba5d608..d34682df3413 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -2516,7 +2516,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
final int[] userIds = filterUserId == UserHandle.USER_ALL ? getAllUserIds()
: new int[] { filterUserId };
- boolean installPermissionsChanged = false;
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
@@ -2635,7 +2634,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
UidPermissionState origState = uidState;
- boolean installPermissionsChangedForUser = false;
+ boolean changedInstallPermission = false;
if (replace) {
userState.setInstallPermissionsFixed(ps.getPackageName(), false);
@@ -2801,7 +2800,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
&& origState.isPermissionGranted(permName))))) {
// Grant an install permission.
if (uidState.grantPermission(bp)) {
- installPermissionsChangedForUser = true;
+ changedInstallPermission = true;
}
} else if (bp.isRuntime()) {
boolean hardRestricted = bp.isHardRestricted();
@@ -2941,12 +2940,12 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
}
}
if (uidState.removePermissionState(bp.getName())) {
- installPermissionsChangedForUser = true;
+ changedInstallPermission = true;
}
}
}
- if ((installPermissionsChangedForUser || replace)
+ if ((changedInstallPermission || replace)
&& !userState.areInstallPermissionsFixed(ps.getPackageName())
&& !ps.isSystem() || ps.getTransientState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
@@ -2955,12 +2954,6 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
userState.setInstallPermissionsFixed(ps.getPackageName(), true);
}
- if (installPermissionsChangedForUser) {
- installPermissionsChanged = true;
- if (replace) {
- updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
- }
- }
updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState,
pkg.getPackageName(), uidImplicitPermissions, uidTargetSdkVersion, userId,
updatedUserIds);
@@ -2977,12 +2970,8 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
// Persist the runtime permissions state for users with changes. If permissions
// were revoked because no app in the shared user declares them we have to
// write synchronously to avoid losing runtime permissions state.
- // Also write synchronously if we changed any install permission for an updated app, because
- // the install permission state is likely already fixed before update, and if we lose the
- // changes here the app won't be reconsidered for newly-added install permissions.
if (callback != null) {
- callback.onPermissionUpdated(updatedUserIds,
- (replace && installPermissionsChanged) || runtimePermissionsRevoked);
+ callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
}
for (int userId : updatedUserIds) {
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 87523f44d4b6..5d2d5826f227 100644
--- a/services/core/java/com/android/server/wm/ContentRecorder.java
+++ b/services/core/java/com/android/server/wm/ContentRecorder.java
@@ -207,7 +207,8 @@ final class ContentRecorder {
// Update the cached session state first, since updating the service will result in always
// returning to this instance to update recording state.
mContentRecordingSession = null;
- mDisplayContent.mWmService.setContentRecordingSession(null);
+ mDisplayContent.mWmService.mContentRecordingController.setContentRecordingSessionLocked(
+ null, mDisplayContent.mWmService);
}
/**
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index c0d7d1362ac3..9eee7ba871a2 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -30,6 +30,7 @@ import android.hardware.display.DisplayManagerInternal;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Pair;
+import android.view.ContentRecordingSession;
import android.view.Display;
import android.view.IInputFilter;
import android.view.IRemoteAnimationFinishedCallback;
@@ -869,4 +870,16 @@ public abstract class WindowManagerInternal {
*/
public abstract boolean isPointInsideWindow(
@NonNull IBinder windowToken, int displayId, float displayX, float displayY);
+
+ /**
+ * Updates the content recording session. If a different session is already in progress, then
+ * the pre-existing session is stopped, and the new incoming session takes over.
+ *
+ * The DisplayContent for the new session will begin recording when
+ * {@link RootWindowContainer#onDisplayChanged} is invoked for the new {@link VirtualDisplay}.
+ * Must be invoked for a valid MediaProjection session.
+ *
+ * @param incomingSession the nullable incoming content recording session
+ */
+ public abstract void setContentRecordingSession(ContentRecordingSession incomingSession);
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 7a5480401de8..902218621cd0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -2956,16 +2956,6 @@ public class WindowManagerService extends IWindowManager.Stub
}
}
- /**
- * Updates the current content mirroring session.
- */
- @Override
- public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
- synchronized (mGlobalLock) {
- mContentRecordingController.setContentRecordingSessionLocked(incomingSession, this);
- }
- }
-
// TODO(multi-display): remove when no default display use case.
void prepareAppTransitionNone() {
if (!checkCallingPermission(MANAGE_APP_TOKENS, "prepareAppTransition()")) {
@@ -8242,6 +8232,14 @@ public class WindowManagerService extends IWindowManager.Stub
return w.getBounds().contains((int) displayX, (int) displayY);
}
}
+
+ @Override
+ public void setContentRecordingSession(@Nullable ContentRecordingSession incomingSession) {
+ synchronized (mGlobalLock) {
+ mContentRecordingController.setContentRecordingSessionLocked(incomingSession,
+ WindowManagerService.this);
+ }
+ }
}
void registerAppFreezeListener(AppFreezeListener listener) {
diff --git a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
index e78f0c77d6b3..844f5d4cd3eb 100644
--- a/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
+++ b/services/tests/servicestests/src/com/android/server/input/InputManagerServiceTests.kt
@@ -36,6 +36,7 @@ import org.mockito.ArgumentMatchers.anyInt
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mock
import org.mockito.Mockito.`when`
+import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.doAnswer
import org.mockito.Mockito.never
import org.mockito.Mockito.spy
@@ -226,7 +227,8 @@ class InputManagerServiceTests {
@Test
fun onDisplayRemoved_resetAllAdditionalInputProperties() {
- localService.setVirtualMousePointerDisplayId(10)
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
localService.setPointerIconVisible(false, 10)
verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
localService.setPointerAcceleration(5f, 10)
@@ -237,9 +239,66 @@ class InputManagerServiceTests {
verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
verify(native).setPointerAcceleration(
eq(IInputConstants.DEFAULT_POINTER_ACCELERATION.toFloat()))
+ verifyNoMoreInteractions(native)
+ // This call should not block because the virtual mouse pointer override was never removed.
localService.setVirtualMousePointerDisplayId(10)
+
verify(native).setPointerDisplayId(eq(10))
verifyNoMoreInteractions(native)
}
+
+ @Test
+ fun updateAdditionalInputPropertiesForOverrideDisplay() {
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
+ localService.setPointerIconVisible(false, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ localService.setPointerAcceleration(5f, 10)
+ verify(native).setPointerAcceleration(eq(5f))
+
+ localService.setPointerIconVisible(true, 10)
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NOT_SPECIFIED))
+ localService.setPointerAcceleration(1f, 10)
+ verify(native).setPointerAcceleration(eq(1f))
+
+ // Verify that setting properties on a different display is not propagated until the
+ // pointer is moved to that display.
+ localService.setPointerIconVisible(false, 20)
+ localService.setPointerAcceleration(6f, 20)
+ verifyNoMoreInteractions(native)
+
+ clearInvocations(native)
+ setVirtualMousePointerDisplayIdAndVerify(20)
+
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ verify(native).setPointerAcceleration(eq(6f))
+ }
+
+ @Test
+ fun setAdditionalInputPropertiesBeforeOverride() {
+ localService.setPointerIconVisible(false, 10)
+ localService.setPointerAcceleration(5f, 10)
+
+ verifyNoMoreInteractions(native)
+
+ setVirtualMousePointerDisplayIdAndVerify(10)
+
+ verify(native).setPointerIconType(eq(PointerIcon.TYPE_NULL))
+ verify(native).setPointerAcceleration(eq(5f))
+ }
+
+ private fun setVirtualMousePointerDisplayIdAndVerify(overrideDisplayId: Int) {
+ val thread = Thread { localService.setVirtualMousePointerDisplayId(overrideDisplayId) }
+ thread.start()
+
+ // Allow some time for the set override call to park while waiting for the native callback.
+ Thread.sleep(100 /*millis*/)
+ verify(native).setPointerDisplayId(overrideDisplayId)
+
+ service.onPointerDisplayIdChanged(overrideDisplayId, 0f, 0f)
+ testLooper.dispatchNext()
+ verify(wmCallbacks).notifyPointerDisplayIdChanged(overrideDisplayId, 0f, 0f)
+ thread.join(100 /*millis*/)
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index c5f785ea7680..32f3bfe5166c 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -2441,7 +2441,7 @@ public class DisplayContentTests extends WindowTestsBase {
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
tokenToMirror);
session.setDisplayId(displayId);
- mWm.setContentRecordingSession(session);
+ mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
actualDC.updateRecording();
// THEN mirroring is not started, since a null surface indicates the VirtualDisplay is off.
@@ -2470,7 +2470,7 @@ public class DisplayContentTests extends WindowTestsBase {
ContentRecordingSession session = ContentRecordingSession.createDisplaySession(
tokenToMirror);
session.setDisplayId(displayId);
- mWm.setContentRecordingSession(session);
+ mWm.mContentRecordingController.setContentRecordingSessionLocked(session, mWm);
mWm.mRoot.onDisplayAdded(displayId);
// WHEN getting the DisplayContent for the new virtual display.
diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING
index 523d5f95c6f3..1c0c71b65fd7 100644
--- a/services/usage/java/com/android/server/usage/TEST_MAPPING
+++ b/services/usage/java/com/android/server/usage/TEST_MAPPING
@@ -20,6 +20,28 @@
]
}
],
+ "presubmit-large": [
+ {
+ "name": "CtsUsageStatsTestCases",
+ "options": [
+ {
+ "include-filter": "android.app.usage.cts.BroadcastResponseStatsTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.FlakyTest"
+ },
+ {
+ "exclude-annotation": "android.platform.test.annotations.FlakyTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.MediumTest"
+ },
+ {
+ "exclude-annotation": "androidx.test.filters.LargeTest"
+ }
+ ]
+ }
+ ],
"postsubmit": [
{
"name": "CtsUsageStatsTestCases",
diff --git a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
index dc96c66bff29..df9fc6601ac5 100644
--- a/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbDirectMidiDevice.java
@@ -80,9 +80,13 @@ public final class UsbDirectMidiDevice implements Closeable {
// of cycles and not being permanently stuck.
private static final int BULK_TRANSFER_TIMEOUT_MILLISECONDS = 10;
+ // Arbitrary number for timeout when closing a thread
+ private static final int THREAD_JOIN_TIMEOUT_MILLISECONDS = 50;
+
private ArrayList<UsbDeviceConnection> mUsbDeviceConnections;
private ArrayList<ArrayList<UsbEndpoint>> mInputUsbEndpoints;
private ArrayList<ArrayList<UsbEndpoint>> mOutputUsbEndpoints;
+ private ArrayList<Thread> mThreads;
private UsbMidiBlockParser mMidiBlockParser = new UsbMidiBlockParser();
private int mDefaultMidiProtocol = MidiDeviceInfo.PROTOCOL_UMP_MIDI_1_0_UP_TO_64_BITS;
@@ -273,9 +277,10 @@ public final class UsbDirectMidiDevice implements Closeable {
// to USB MIDI for each USB output.
mUsbMidiPacketConverter = new UsbMidiPacketConverter(mNumOutputs);
- mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>(mUsbInterfaces.size());
- mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
- mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>(mUsbInterfaces.size());
+ mUsbDeviceConnections = new ArrayList<UsbDeviceConnection>();
+ mInputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
+ mOutputUsbEndpoints = new ArrayList<ArrayList<UsbEndpoint>>();
+ mThreads = new ArrayList<Thread>();
for (int interfaceIndex = 0; interfaceIndex < mUsbInterfaces.size(); interfaceIndex++) {
ArrayList<UsbEndpoint> inputEndpoints = new ArrayList<UsbEndpoint>();
@@ -327,7 +332,7 @@ public final class UsbDirectMidiDevice implements Closeable {
mInputUsbEndpoints.get(connectionIndex).get(endpointIndex);
final int portFinal = portNumber;
- new Thread("UsbDirectMidiDevice input thread " + portFinal) {
+ Thread newThread = new Thread("UsbDirectMidiDevice input thread " + portFinal) {
@Override
public void run() {
final UsbRequest request = new UsbRequest();
@@ -335,9 +340,12 @@ public final class UsbDirectMidiDevice implements Closeable {
request.initialize(connectionFinal, endpointFinal);
byte[] inputBuffer = new byte[endpointFinal.getMaxPacketSize()];
while (true) {
+ if (Thread.currentThread().interrupted()) {
+ Log.w(TAG, "input thread interrupted");
+ break;
+ }
// Record time of event immediately after waking.
long timestamp = System.nanoTime();
- if (!mIsOpen) break;
final ByteBuffer byteBuffer = ByteBuffer.wrap(inputBuffer);
if (!request.queue(byteBuffer)) {
Log.w(TAG, "Cannot queue request");
@@ -382,8 +390,9 @@ public final class UsbDirectMidiDevice implements Closeable {
}
Log.d(TAG, "input thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -402,18 +411,23 @@ public final class UsbDirectMidiDevice implements Closeable {
final int portFinal = portNumber;
final MidiEventScheduler eventSchedulerFinal = mEventSchedulers[portFinal];
- new Thread("UsbDirectMidiDevice output thread " + portFinal) {
+ Thread newThread = new Thread("UsbDirectMidiDevice output thread " + portFinal) {
@Override
public void run() {
while (true) {
+ if (Thread.currentThread().interrupted()) {
+ Log.w(TAG, "output thread interrupted");
+ break;
+ }
MidiEvent event;
try {
event = (MidiEvent) eventSchedulerFinal.waitNextEvent();
} catch (InterruptedException e) {
- // try again
- continue;
+ Log.w(TAG, "event scheduler interrupted");
+ break;
}
if (event == null) {
+ Log.w(TAG, "event is null");
break;
}
@@ -446,8 +460,9 @@ public final class UsbDirectMidiDevice implements Closeable {
}
Log.d(TAG, "output thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -523,6 +538,27 @@ public final class UsbDirectMidiDevice implements Closeable {
private void closeLocked() {
Log.d(TAG, "closeLocked()");
+
+ // Send an interrupt signal to threads.
+ for (Thread thread : mThreads) {
+ if (thread != null) {
+ thread.interrupt();
+ }
+ }
+
+ // Wait for threads to actually stop.
+ for (Thread thread : mThreads) {
+ if (thread != null) {
+ try {
+ thread.join(THREAD_JOIN_TIMEOUT_MILLISECONDS);
+ } catch (InterruptedException e) {
+ Log.w(TAG, "thread join interrupted");
+ break;
+ }
+ }
+ }
+ mThreads = null;
+
for (int i = 0; i < mEventSchedulers.length; i++) {
mMidiInputPortReceivers[i].setReceiver(null);
mEventSchedulers[i].close();
diff --git a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
index 54b3c400af4f..f924b2e9b932 100644
--- a/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
+++ b/tests/vcn/java/com/android/server/VcnManagementServiceTest.java
@@ -23,6 +23,7 @@ import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR;
import static android.net.NetworkCapabilities.TRANSPORT_WIFI;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_ACTIVE;
import static android.net.vcn.VcnManager.VCN_STATUS_CODE_SAFE_MODE;
+import static android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_NO_ACCESS;
@@ -276,7 +277,6 @@ public class VcnManagementServiceTest {
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
@@ -494,8 +494,10 @@ public class VcnManagementServiceTest {
mVcnMgmtSvc.addVcnUnderlyingNetworkPolicyListener(mMockPolicyListener);
triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
- mTestLooper.dispatchAll();
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
verify(vcn).teardownAsynchronously();
verify(mMockPolicyListener).onPolicyChanged();
}
@@ -521,6 +523,92 @@ public class VcnManagementServiceTest {
assertEquals(0, mVcnMgmtSvc.getAllVcns().size());
}
+ /**
+ * Tests an intermediate state where carrier privileges are marked as lost before active data
+ * subId changes during a SIM ejection.
+ *
+ * <p>The expected outcome is that the VCN is torn down after a delay, as opposed to
+ * immediately.
+ */
+ @Test
+ public void testTelephonyNetworkTrackerCallbackLostCarrierPrivilegesBeforeActiveDataSubChanges()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate privileges lost
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ TEST_SUBSCRIPTION_ID,
+ TEST_UUID_2,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Verify teardown after delay
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackSimSwitchesDoNotKillVcnInstances()
+ throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn vcn = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(
+ INVALID_SUBSCRIPTION_ID,
+ null /* activeDataSubscriptionGroup */,
+ Collections.emptySet(),
+ Collections.emptyMap(),
+ false /* hasCarrierPrivileges */);
+
+ // Simulate new SIM loaded right during teardown delay.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+
+ // Verify that even after the full timeout duration, the VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(vcn, never()).teardownAsynchronously();
+ }
+
+ @Test
+ public void testTelephonyNetworkTrackerCallbackDoesNotKillNewVcnInstances() throws Exception {
+ setupActiveSubscription(TEST_UUID_2);
+
+ final TelephonySubscriptionTrackerCallback cb = getTelephonySubscriptionTrackerCallback();
+ final Vcn oldInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Simulate SIM unloaded
+ triggerSubscriptionTrackerCbAndGetSnapshot(null, Collections.emptySet());
+
+ // Config cleared, SIM reloaded & config re-added right before teardown delay, staring new
+ // vcnInstance.
+ mTestLooper.moveTimeForward(
+ VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS / 2);
+ mTestLooper.dispatchAll();
+ mVcnMgmtSvc.clearVcnConfig(TEST_UUID_2, TEST_PACKAGE_NAME);
+ triggerSubscriptionTrackerCbAndGetSnapshot(TEST_UUID_2, Collections.singleton(TEST_UUID_2));
+ final Vcn newInstance = startAndGetVcnInstance(TEST_UUID_2);
+
+ // Verify that new instance was different, and the old one was torn down
+ assertTrue(oldInstance != newInstance);
+ verify(oldInstance).teardownAsynchronously();
+
+ // Verify that even after the full timeout duration, the new VCN instance is not torn down
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
+ mTestLooper.dispatchAll();
+ verify(newInstance, never()).teardownAsynchronously();
+ }
+
@Test
public void testPackageChangeListenerRegistered() throws Exception {
verify(mMockContext).registerReceiver(any(BroadcastReceiver.class), argThat(filter -> {
@@ -910,8 +998,6 @@ public class VcnManagementServiceTest {
private void setupSubscriptionAndStartVcn(
int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
-
triggerSubscriptionTrackerCbAndGetSnapshot(
subGrp,
Collections.singleton(subGrp),
@@ -1007,7 +1093,6 @@ public class VcnManagementServiceTest {
private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
final ArgumentCaptor<NetworkCallback> captor =
ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1252,14 +1337,15 @@ public class VcnManagementServiceTest {
true /* isActive */,
true /* hasCarrierPrivileges */);
- // VCN is currently active. Lose carrier privileges for TEST_PACKAGE so the VCN goes
- // inactive.
+ // VCN is currently active. Lose carrier privileges for TEST_PACKAGE and hit teardown
+ // timeout so the VCN goes inactive.
final TelephonySubscriptionSnapshot snapshot =
triggerSubscriptionTrackerCbAndGetSnapshot(
TEST_UUID_1,
Collections.singleton(TEST_UUID_1),
Collections.singletonMap(TEST_SUBSCRIPTION_ID, TEST_UUID_1),
false /* hasCarrierPrivileges */);
+ mTestLooper.moveTimeForward(VcnManagementService.CARRIER_PRIVILEGES_LOST_TEARDOWN_DELAY_MS);
mTestLooper.dispatchAll();
// Giving TEST_PACKAGE privileges again will restart the VCN (which will indicate ACTIVE