Merge "SpatializerHelper: Deliver metrics to MediaMetrics" into tm-dev
diff --git a/apex/jobscheduler/framework/java/android/app/job/JobInfo.java b/apex/jobscheduler/framework/java/android/app/job/JobInfo.java
index 4710322..f49cdbf 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 @@
* {@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 388bbf1..632ecb2c 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 @@
/**
* 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 7ed4b62..d184d44 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 @@
* {@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/DeviceIdleController.java b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
index e9ce87f..2eb86c1 100644
--- a/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
+++ b/apex/jobscheduler/service/java/com/android/server/DeviceIdleController.java
@@ -693,6 +693,7 @@
synchronized (DeviceIdleController.this) {
if (mStationaryListeners.size() > 0) {
startMonitoringMotionLocked();
+ scheduleMotionTimeoutAlarmLocked();
}
}
};
@@ -3859,7 +3860,7 @@
void handleMotionDetectedLocked(long timeout, String type) {
if (mStationaryListeners.size() > 0) {
postStationaryStatusUpdated();
- scheduleMotionTimeoutAlarmLocked();
+ cancelMotionTimeoutAlarmLocked();
// We need to re-register the motion listener, but we don't want the sensors to be
// constantly active or to churn the CPU by registering too early, register after some
// delay.
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 d5a7f28..28116a8 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 @@
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 @@
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 @@
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 cd70e88..60afdc7 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 @@
// 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 @@
// 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 @@
* 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 @@
}
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 @@
startTrackingJobLocked(incomingJob, cancelled);
}
reportActiveLocked();
- return wasRunning;
}
void updateUidState(int uid, int procState) {
diff --git a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
index 9c7d702..2da00c7 100644
--- a/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
+++ b/apex/jobscheduler/service/java/com/android/server/tare/InternalResourceService.java
@@ -382,7 +382,7 @@
if (increased) {
mAgent.distributeBasicIncomeLocked(newBatteryLevel);
} else if (newBatteryLevel == mCurrentBatteryLevel) {
- Slog.wtf(TAG, "Battery level stayed the same");
+ // The broadcast is also sent when the plug type changes...
return;
}
mCurrentBatteryLevel = newBatteryLevel;
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 c90291e..fb342b9 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 @@
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 1c4ec85..c9afdad 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 @@
| 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 @@
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 @@
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 @@
}
}
+ 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 @@
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 @@
"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 @@
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 @@
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 e407e31..a6a3aaf 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 fe99c71..48277fb 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2923,7 +2923,7 @@
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 7c83d58..bbe3ce3 100644
--- a/core/java/android/app/LocaleConfig.java
+++ b/core/java/android/app/LocaleConfig.java
@@ -99,7 +99,7 @@
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/WindowConfiguration.java b/core/java/android/app/WindowConfiguration.java
index 5ef3fc0..e96a986 100644
--- a/core/java/android/app/WindowConfiguration.java
+++ b/core/java/android/app/WindowConfiguration.java
@@ -345,6 +345,14 @@
mAlwaysOnTop = alwaysOnTop ? ALWAYS_ON_TOP_ON : ALWAYS_ON_TOP_OFF;
}
+ /**
+ * Unsets always-on-top to undefined.
+ * @hide
+ */
+ public void unsetAlwaysOnTop() {
+ mAlwaysOnTop = ALWAYS_ON_TOP_UNDEFINED;
+ }
+
private void setAlwaysOnTop(@AlwaysOnTop int alwaysOnTop) {
mAlwaysOnTop = alwaysOnTop;
}
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 93f91e5..b7f1136 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -2515,7 +2515,8 @@
* 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 @@
* 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/app/admin/ProvisioningIntentHelper.java b/core/java/android/app/admin/ProvisioningIntentHelper.java
index 1c38559..894f706 100644
--- a/core/java/android/app/admin/ProvisioningIntentHelper.java
+++ b/core/java/android/app/admin/ProvisioningIntentHelper.java
@@ -18,10 +18,26 @@
import static android.app.admin.DevicePolicyManager.ACTION_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ALLOW_OFFLINE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_DEVICE_ADMIN_PACKAGE_NAME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_KEEP_SCREEN_ON;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_LOCAL_TIME;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_ENCRYPTION;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_SUPPORTED_MODES;
import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_TRIGGER;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_USE_MOBILE_DATA;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_HIDDEN;
+import static android.app.admin.DevicePolicyManager.EXTRA_PROVISIONING_WIFI_PROXY_PORT;
import static android.app.admin.DevicePolicyManager.MIME_TYPE_PROVISIONING_NFC;
import static android.app.admin.DevicePolicyManager.PROVISIONING_TRIGGER_NFC;
import static android.nfc.NfcAdapter.EXTRA_NDEF_MESSAGES;
@@ -44,6 +60,8 @@
import java.io.IOException;
import java.io.StringReader;
import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
import java.util.Properties;
import java.util.Set;
@@ -52,6 +70,8 @@
*/
final class ProvisioningIntentHelper {
+ private static final Map<String, Class> EXTRAS_TO_CLASS_MAP = createExtrasToClassMap();
+
private static final String TAG = "ProvisioningIntentHelper";
/**
@@ -124,12 +144,11 @@
private static void addPropertyToBundle(
String propertyName, Properties properties, Bundle bundle) {
- if(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME.equals(propertyName)) {
+ if (EXTRAS_TO_CLASS_MAP.get(propertyName) == ComponentName.class) {
ComponentName componentName = ComponentName.unflattenFromString(
properties.getProperty(propertyName));
bundle.putParcelable(propertyName, componentName);
- } else if (EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE.equals(propertyName)
- || EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE.equals(propertyName)) {
+ } else if (EXTRAS_TO_CLASS_MAP.get(propertyName) == PersistableBundle.class) {
try {
bundle.putParcelable(propertyName,
deserializeExtrasBundle(properties, propertyName));
@@ -137,6 +156,13 @@
Log.e(TAG,
"Failed to parse " + propertyName + ".", e);
}
+ } else if (EXTRAS_TO_CLASS_MAP.get(propertyName) == Boolean.class) {
+ bundle.putBoolean(propertyName,
+ Boolean.parseBoolean(properties.getProperty(propertyName)));
+ } else if (EXTRAS_TO_CLASS_MAP.get(propertyName) == Long.class) {
+ bundle.putLong(propertyName, Long.parseLong(properties.getProperty(propertyName)));
+ } else if (EXTRAS_TO_CLASS_MAP.get(propertyName) == Integer.class) {
+ bundle.putInt(propertyName, Integer.parseInt(properties.getProperty(propertyName)));
}
else {
bundle.putString(propertyName, properties.getProperty(propertyName));
@@ -213,4 +239,61 @@
Log.i(TAG, "No compatible records found on nfcIntent");
return null;
}
+
+ private static Map<String, Class> createExtrasToClassMap() {
+ Map<String, Class> map = new HashMap<>();
+ for (String extra : getBooleanExtras()) {
+ map.put(extra, Boolean.class);
+ }
+ for (String extra : getLongExtras()) {
+ map.put(extra, Long.class);
+ }
+ for (String extra : getIntExtras()) {
+ map.put(extra, Integer.class);
+ }
+ for (String extra : getComponentNameExtras()) {
+ map.put(extra, ComponentName.class);
+ }
+ for (String extra : getPersistableBundleExtras()) {
+ map.put(extra, PersistableBundle.class);
+ }
+ return map;
+ }
+
+ private static Set<String> getPersistableBundleExtras() {
+ return Set.of(
+ EXTRA_PROVISIONING_ADMIN_EXTRAS_BUNDLE,
+ EXTRA_PROVISIONING_ROLE_HOLDER_EXTRAS_BUNDLE);
+ }
+
+ private static Set<String> getComponentNameExtras() {
+ return Set.of(EXTRA_PROVISIONING_DEVICE_ADMIN_COMPONENT_NAME);
+ }
+
+ private static Set<String> getIntExtras() {
+ return Set.of(
+ EXTRA_PROVISIONING_WIFI_PROXY_PORT,
+ EXTRA_PROVISIONING_DEVICE_ADMIN_MINIMUM_VERSION_CODE,
+ EXTRA_PROVISIONING_SUPPORTED_MODES);
+ }
+
+ private static Set<String> getLongExtras() {
+ return Set.of(EXTRA_PROVISIONING_LOCAL_TIME);
+ }
+
+ private static Set<String> getBooleanExtras() {
+ return Set.of(
+ EXTRA_PROVISIONING_ALLOW_OFFLINE,
+ EXTRA_PROVISIONING_SHOULD_LAUNCH_RESULT_INTENT,
+ EXTRA_PROVISIONING_KEEP_ACCOUNT_ON_MIGRATION,
+ EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED,
+ EXTRA_PROVISIONING_WIFI_HIDDEN,
+ EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT,
+ EXTRA_PROVISIONING_SKIP_ENCRYPTION,
+ EXTRA_PROVISIONING_SKIP_EDUCATION_SCREENS,
+ EXTRA_PROVISIONING_USE_MOBILE_DATA,
+ EXTRA_PROVISIONING_SKIP_OWNERSHIP_DISCLAIMER,
+ EXTRA_PROVISIONING_RETURN_BEFORE_POLICY_COMPLIANCE,
+ EXTRA_PROVISIONING_KEEP_SCREEN_ON);
+ }
}
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index a50ff3841..7a88a057 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3259,7 +3259,7 @@
/**
- * Broadcast Action: Sent when the current battery level changes.
+ * Broadcast Action: Sent when the current battery level or plug type changes.
*
* It has {@link android.os.BatteryManager#EXTRA_EVENTS} that carries a list of {@link Bundle}
* instances representing individual battery level changes with associated
diff --git a/core/java/android/service/dreams/DreamActivity.java b/core/java/android/service/dreams/DreamActivity.java
index cf4e6a6..bf64d06 100644
--- a/core/java/android/service/dreams/DreamActivity.java
+++ b/core/java/android/service/dreams/DreamActivity.java
@@ -58,8 +58,9 @@
setTitle(title);
}
- DreamService.DreamServiceWrapper callback =
- (DreamService.DreamServiceWrapper) getIntent().getIBinderExtra(EXTRA_CALLBACK);
+ final Bundle extras = getIntent().getExtras();
+ final DreamService.DreamActivityCallback callback =
+ (DreamService.DreamActivityCallback) extras.getBinder(EXTRA_CALLBACK);
if (callback != null) {
callback.onActivityCreated(this);
diff --git a/core/java/android/service/dreams/DreamService.java b/core/java/android/service/dreams/DreamService.java
index 47fc120..5217b28 100644
--- a/core/java/android/service/dreams/DreamService.java
+++ b/core/java/android/service/dreams/DreamService.java
@@ -36,6 +36,7 @@
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.drawable.Drawable;
+import android.os.Binder;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
@@ -1272,7 +1273,7 @@
Intent i = new Intent(this, DreamActivity.class);
i.setPackage(getApplicationContext().getPackageName());
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- i.putExtra(DreamActivity.EXTRA_CALLBACK, mDreamServiceWrapper);
+ i.putExtra(DreamActivity.EXTRA_CALLBACK, new DreamActivityCallback(mDreamToken));
final ServiceInfo serviceInfo = fetchServiceInfo(this,
new ComponentName(this, getClass()));
i.putExtra(DreamActivity.EXTRA_DREAM_TITLE, fetchDreamLabel(this, serviceInfo));
@@ -1452,11 +1453,36 @@
public void wakeUp() {
mHandler.post(() -> DreamService.this.wakeUp(true /*fromSystem*/));
}
+ }
- /** @hide */
- void onActivityCreated(DreamActivity a) {
- mActivity = a;
- onWindowCreated(a.getWindow());
+ /** @hide */
+ final class DreamActivityCallback extends Binder {
+ private final IBinder mActivityDreamToken;
+
+ DreamActivityCallback(IBinder token) {
+ mActivityDreamToken = token;
+ }
+
+ void onActivityCreated(DreamActivity activity) {
+ if (mActivityDreamToken != mDreamToken || mFinished) {
+ Slog.d(TAG, "DreamActivity was created after the dream was finished or "
+ + "a new dream started, finishing DreamActivity");
+ if (!activity.isFinishing()) {
+ activity.finishAndRemoveTask();
+ }
+ return;
+ }
+ if (mActivity != null) {
+ Slog.w(TAG, "A DreamActivity has already been started, "
+ + "finishing latest DreamActivity");
+ if (!activity.isFinishing()) {
+ activity.finishAndRemoveTask();
+ }
+ return;
+ }
+
+ mActivity = activity;
+ onWindowCreated(activity.getWindow());
}
}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
index c83869c..fb562d8 100644
--- a/core/java/android/view/IWindowManager.aidl
+++ b/core/java/android/view/IWindowManager.aidl
@@ -32,7 +32,6 @@
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 @@
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 38ca248..90497e7 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -4781,9 +4781,6 @@
@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 @@
@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,31 +11983,10 @@
}
private void updatePreferKeepClearForFocus() {
- if (mMarkPreferKeepClearForFocus != null) {
- removeCallbacks(mMarkPreferKeepClearForFocus);
- mMarkPreferKeepClearForFocus = null;
+ if (ViewConfiguration.get(mContext).isPreferKeepClearForFocusEnabled()) {
+ updatePositionUpdateListener();
+ post(this::updateKeepClearRects);
}
-
- 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);
- }
- }
-
- private void cancelMarkPreferKeepClearForFocus() {
- if (mMarkPreferKeepClearForFocus != null) {
- removeCallbacks(mMarkPreferKeepClearForFocus);
- mMarkPreferKeepClearForFocus = null;
- }
- mPreferKeepClearForFocus = false;
}
/**
@@ -13754,7 +13731,6 @@
}
invalidate();
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
- updatePreferKeepClearForFocus();
return true;
}
return false;
@@ -21154,7 +21130,6 @@
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 ebc409e..638b8f9 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -347,7 +347,7 @@
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 @@
mMinScalingSpan = 0;
mSmartSelectionInitializedTimeout = SMART_SELECTION_INITIALIZED_TIMEOUT_IN_MILLISECOND;
mSmartSelectionInitializingTimeout = SMART_SELECTION_INITIALIZING_TIMEOUT_IN_MILLISECOND;
- mPreferKeepClearForFocusDelay = -1;
+ mPreferKeepClearForFocusEnabled = false;
}
/**
@@ -508,8 +508,8 @@
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 @@
}
/**
- * @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 eabc13a..127c7b7 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -222,7 +222,6 @@
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 @@
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 @@
}
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 fd9cc19..152729b 100644
--- a/core/java/android/view/ViewRootRectTracker.java
+++ b/core/java/android/view/ViewRootRectTracker.java
@@ -73,10 +73,25 @@
}
/**
- * @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 @@
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 2dcc585..65e7abf 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_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_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,8 @@
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_BIOMETRIC_PROMPT_TRANSITION = 56;
+ public static final int CUJ_SETTINGS_TOGGLE = 57;
private static final int NO_STATSD_LOGGING = -1;
@@ -262,6 +266,8 @@
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 +344,8 @@
CUJ_SETTINGS_SLIDER,
CUJ_TAKE_SCREENSHOT,
CUJ_VOLUME_CONTROL,
+ CUJ_BIOMETRIC_PROMPT_TRANSITION,
+ CUJ_SETTINGS_TOGGLE,
})
@Retention(RetentionPolicy.SOURCE)
public @interface CujType {
@@ -768,6 +776,10 @@
return "TAKE_SCREENSHOT";
case CUJ_VOLUME_CONTROL:
return "VOLUME_CONTROL";
+ case CUJ_BIOMETRIC_PROMPT_TRANSITION:
+ return "BIOMETRIC_PROMPT_TRANSITION";
+ 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 09c9cfc..04f72c7 100644
--- a/core/java/com/android/internal/os/BatteryUsageStatsStore.java
+++ b/core/java/com/android/internal/os/BatteryUsageStatsStore.java
@@ -320,4 +320,19 @@
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 c4ff49c..00127c1 100644
--- a/core/proto/android/server/jobscheduler.proto
+++ b/core/proto/android/server/jobscheduler.proto
@@ -1073,6 +1073,7 @@
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 5cc9559..66fcb04 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 f7465e0..63dd3b013 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/autofill_fill_dialog.xml b/core/res/res/layout/autofill_fill_dialog.xml
index e638764..ba27d90 100644
--- a/core/res/res/layout/autofill_fill_dialog.xml
+++ b/core/res/res/layout/autofill_fill_dialog.xml
@@ -62,18 +62,24 @@
android:layout_marginEnd="24dp"
android:background="@drawable/autofill_dataset_picker_background"/>
- <ListView
- android:id="@+id/autofill_dialog_list"
- android:layout_weight="1"
+ <LinearLayout
android:layout_width="fill_parent"
- android:layout_height="0dp"
- android:drawSelectorOnTop="true"
- android:clickable="true"
- android:divider="?android:attr/listDivider"
+ android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
- android:background="@drawable/autofill_dataset_picker_background"
- android:visibility="gone"/>
+ android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogList"
+ android:orientation="vertical">
+ <ListView
+ android:id="@+id/autofill_dialog_list"
+ android:layout_weight="1"
+ android:layout_width="fill_parent"
+ android:layout_height="0dp"
+ android:drawSelectorOnTop="true"
+ android:clickable="true"
+ android:divider="@drawable/list_divider_material"
+ android:background="@drawable/autofill_dataset_picker_background"
+ android:visibility="gone"/>
+ </LinearLayout>
<com.android.internal.widget.ButtonBarLayout
android:layout_width="match_parent"
diff --git a/core/res/res/layout/autofill_save.xml b/core/res/res/layout/autofill_save.xml
index 8a1a9c7..49539cf 100644
--- a/core/res/res/layout/autofill_save.xml
+++ b/core/res/res/layout/autofill_save.xml
@@ -23,45 +23,38 @@
android:orientation="vertical">
<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/autofill_save"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/autofill_save_outer_top_margin"
- android:paddingTop="@dimen/autofill_save_outer_top_padding"
+ android:layout_marginStart="24dp"
+ android:layout_marginEnd="24dp"
android:elevation="@dimen/autofill_elevation"
android:background="?android:attr/colorSurface"
+ android:gravity="center_horizontal"
android:orientation="vertical">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:paddingStart="@dimen/autofill_save_inner_padding"
- android:paddingEnd="@dimen/autofill_save_inner_padding"
android:orientation="vertical">
- <LinearLayout
+ <ImageView
+ android:id="@+id/autofill_save_icon"
+ android:scaleType="fitStart"
+ android:layout_gravity="center"
+ android:layout_width="@dimen/autofill_save_icon_size"
+ android:layout_height="@dimen/autofill_save_icon_size"/>
+
+ <TextView
+ android:id="@+id/autofill_save_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/autofill_save_icon"
- android:scaleType="fitStart"
- android:layout_width="@dimen/autofill_save_icon_size"
- android:layout_height="@dimen/autofill_save_icon_size"/>
-
- <TextView
- android:id="@+id/autofill_save_title"
- android:paddingStart="@dimen/autofill_save_title_start_padding"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- android:text="@string/autofill_save_title"
- android:textAppearance="@style/TextAppearance.DeviceDefault.Subhead"
- android:layout_weight="1">
- </TextView>
-
- </LinearLayout>
+ android:text="@string/autofill_save_title"
+ android:layout_marginTop="16dp"
+ android:gravity="center"
+ android:textAppearance="@style/AutofillSaveUiTitle">
+ </TextView>
<com.android.server.autofill.ui.CustomScrollView
android:id="@+id/autofill_save_custom_subtitle"
@@ -79,8 +72,6 @@
android:clipToPadding="false"
android:layout_marginTop="32dp"
android:layout_marginBottom="18dp"
- android:layout_marginStart="24dp"
- android:layout_marginEnd="24dp"
android:theme="@style/Theme.DeviceDefault.AutofillHalfScreenDialogButton"
android:orientation="horizontal">
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 c88bc92..1a395b9 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" />
+ 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="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" />
-
+ <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 0c462fd..39034dc 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-night/themes_device_defaults.xml b/core/res/res/values-night/themes_device_defaults.xml
index 366e6f9..7cfdba7 100644
--- a/core/res/res/values-night/themes_device_defaults.xml
+++ b/core/res/res/values-night/themes_device_defaults.xml
@@ -98,5 +98,10 @@
<item name="colorAccentPrimary">@color/system_accent1_700</item>
<item name="textColorPrimary">@color/system_neutral1_100</item>
<item name="textColorSecondary">@color/system_neutral2_100</item>
+ <item name="colorListDivider">@color/white</item>
+ </style>
+
+ <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
+ <item name="colorForeground">@android:color/white</item>
</style>
</resources>
diff --git a/core/res/res/values-television/styles.xml b/core/res/res/values-television/styles.xml
new file mode 100644
index 0000000..ad9140c
--- /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 d357f01..ea6e1f1 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 054695e..70766cc 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/dimens.xml b/core/res/res/values/dimens.xml
index 211bf7f..43ca4f0 100644
--- a/core/res/res/values/dimens.xml
+++ b/core/res/res/values/dimens.xml
@@ -865,13 +865,13 @@
<dimen name="autofill_dataset_picker_max_height">90%</dimen>
<!-- Autofill save dialog padding -->
- <dimen name="autofill_save_outer_top_margin">32dp</dimen>
+ <dimen name="autofill_save_outer_top_margin">24dp</dimen>
<dimen name="autofill_save_outer_top_padding">16dp</dimen>
<dimen name="autofill_elevation">32dp</dimen>
<dimen name="autofill_save_inner_padding">16dp</dimen>
- <dimen name="autofill_save_icon_size">24dp</dimen>
+ <dimen name="autofill_save_icon_size">32dp</dimen>
<dimen name="autofill_save_title_start_padding">8dp</dimen>
- <dimen name="autofill_save_scroll_view_top_margin">4dp</dimen>
+ <dimen name="autofill_save_scroll_view_top_margin">16dp</dimen>
<dimen name="autofill_save_button_bar_padding">16dp</dimen>
<dimen name="autofill_dialog_corner_radius">28dp</dimen>
diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml
index 0c35f93..5d17047 100644
--- a/core/res/res/values/styles.xml
+++ b/core/res/res/values/styles.xml
@@ -1495,7 +1495,14 @@
<item name="background">@drawable/autofill_dataset_picker_background</item>
</style>
- <!-- @hide -->
+ <!-- @hide Autofill save dialog title -->
+ <style name="AutofillSaveUiTitle" parent="TextAppearance.DeviceDefault.Subhead">
+ <item name="textColor">?attr/textColorPrimary</item>
+ <item name="fontFamily">google-sans</item>
+ <item name="textSize">24sp</item>
+ </style>
+
+ <!--@hide -->
<style name="AutofillHalfScreenAnimation">
<item name="android:windowEnterAnimation">@anim/slide_in_up</item>
<item name="android:windowExitAnimation">@anim/slide_out_down</item>
@@ -1526,26 +1533,27 @@
<!-- 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 +1563,6 @@
<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 5bfc568..e111ee1 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/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml
index 5000b58..c603c83 100644
--- a/core/res/res/values/themes_device_defaults.xml
+++ b/core/res/res/values/themes_device_defaults.xml
@@ -2704,4 +2704,8 @@
<item name="textColorPrimary">@color/system_neutral1_900</item>
<item name="textColorSecondary">@color/system_neutral2_700</item>
</style>
+ <style name="Theme.DeviceDefault.AutofillHalfScreenDialogList" parent="Theme.DeviceDefault.DayNight">
+ <item name="colorListDivider">@color/list_divider_opacity_device_default_light</item>
+ <item name="opacityListDivider">@color/list_divider_opacity_device_default_light</item>
+ </style>
</resources>
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 21f6e7c..c9729fa 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 @@
}
@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/libs/WindowManager/Shell/res/values/strings_tv.xml b/libs/WindowManager/Shell/res/values/strings_tv.xml
index 09ed9b8..2b7a13e 100644
--- a/libs/WindowManager/Shell/res/values/strings_tv.xml
+++ b/libs/WindowManager/Shell/res/values/strings_tv.xml
@@ -26,23 +26,38 @@
<!-- Picture-in-Picture (PIP) menu -->
<eat-comment />
<!-- Button to close picture-in-picture (PIP) in PIP menu [CHAR LIMIT=30] -->
- <string name="pip_close">Close PIP</string>
+ <string name="pip_close">Close</string>
<!-- Button to move picture-in-picture (PIP) screen to the fullscreen in PIP menu [CHAR LIMIT=30] -->
<string name="pip_fullscreen">Full screen</string>
<!-- Button to move picture-in-picture (PIP) via DPAD in the PIP menu [CHAR LIMIT=30] -->
- <string name="pip_move">Move PIP</string>
+ <string name="pip_move">Move</string>
<!-- Button to expand the picture-in-picture (PIP) window [CHAR LIMIT=30] -->
- <string name="pip_expand">Expand PIP</string>
+ <string name="pip_expand">Expand</string>
<!-- Button to collapse/shrink the picture-in-picture (PIP) window [CHAR LIMIT=30] -->
- <string name="pip_collapse">Collapse PIP</string>
+ <string name="pip_collapse">Collapse</string>
<!-- Educative text instructing the user to double press the HOME button to access the pip
controls menu [CHAR LIMIT=50] -->
<string name="pip_edu_text"> Double press <annotation icon="home_icon"> HOME </annotation> for
controls </string>
+
+ <!-- Accessibility announcement when opening the PiP menu. [CHAR LIMIT=NONE] -->
+ <string name="a11y_pip_menu_entered">Picture-in-Picture menu.</string>
+
+ <!-- Accessibility action: move the PiP window to the left [CHAR LIMIT=30] -->
+ <string name="a11y_action_pip_move_left">Move left</string>
+ <!-- Accessibility action: move the PiP window to the right [CHAR LIMIT=30] -->
+ <string name="a11y_action_pip_move_right">Move right</string>
+ <!-- Accessibility action: move the PiP window up [CHAR LIMIT=30] -->
+ <string name="a11y_action_pip_move_up">Move up</string>
+ <!-- Accessibility action: move the PiP window down [CHAR LIMIT=30] -->
+ <string name="a11y_action_pip_move_down">Move down</string>
+ <!-- Accessibility action: done with moving the PiP [CHAR LIMIT=30] -->
+ <string name="a11y_action_pip_move_done">Done</string>
+
</resources>
diff --git a/media/java/android/media/projection/IMediaProjectionManager.aidl b/media/java/android/media/projection/IMediaProjectionManager.aidl
index d190fce..1d58a40 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.IMediaProjectionWatcherCallback;
import android.media.projection.MediaProjectionInfo;
import android.os.IBinder;
+import android.view.ContentRecordingSession;
/** {@hide} */
interface IMediaProjectionManager {
@@ -33,4 +34,15 @@
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 b5f9593..ba7bf3f 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.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 @@
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 @@
@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 @@
}
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 @@
}
}
+ 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/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
index 6766cdd..8cda376 100644
--- a/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
+++ b/packages/SettingsLib/FooterPreference/src/com/android/settingslib/widget/FooterPreference.java
@@ -44,7 +44,6 @@
int mIconVisibility = View.VISIBLE;
private CharSequence mContentDescription;
private CharSequence mLearnMoreText;
- private CharSequence mLearnMoreContentDescription;
private FooterLearnMoreSpan mLearnMoreSpan;
public FooterPreference(Context context, AttributeSet attrs) {
@@ -80,9 +79,6 @@
learnMoreText.setSpan(mLearnMoreSpan, 0,
learnMoreText.length(), 0);
learnMore.setText(learnMoreText);
- if (!TextUtils.isEmpty(mLearnMoreContentDescription)) {
- learnMore.setContentDescription(mLearnMoreContentDescription);
- }
} else {
learnMore.setVisibility(View.GONE);
}
@@ -140,27 +136,6 @@
}
/**
- * To set content description of the learn more text. This can use for talkback
- * environment if developer wants to have a customization content.
- *
- * @param learnMoreContentDescription The resource id of the content description.
- */
- public void setLearnMoreContentDescription(CharSequence learnMoreContentDescription) {
- if (!TextUtils.equals(mContentDescription, learnMoreContentDescription)) {
- mLearnMoreContentDescription = learnMoreContentDescription;
- notifyChanged();
- }
- }
-
- /**
- * Return the content description of learn more link.
- */
- @VisibleForTesting
- CharSequence getLearnMoreContentDescription() {
- return mLearnMoreContentDescription;
- }
-
- /**
* Assign an action for the learn more link.
*/
public void setLearnMoreAction(View.OnClickListener listener) {
@@ -201,7 +176,7 @@
private String mKey;
private CharSequence mTitle;
private CharSequence mContentDescription;
- private CharSequence mLearnMoreContentDescription;
+ private CharSequence mLearnMoreText;
public Builder(@NonNull Context context) {
mContext = context;
@@ -260,25 +235,24 @@
}
/**
- * To set content description of the learn more text. This can use for talkback
+ * To set learn more string of the learn more text. This can use for talkback
* environment if developer wants to have a customization content.
*
- * @param learnMoreContentDescription The resource id of the content description.
+ * @param learnMoreText The resource id of the learn more string.
*/
- public Builder setLearnMoreContentDescription(CharSequence learnMoreContentDescription) {
- mLearnMoreContentDescription = learnMoreContentDescription;
+ public Builder setLearnMoreText(CharSequence learnMoreText) {
+ mLearnMoreText = learnMoreText;
return this;
}
/**
- * To set content description of the {@link FooterPreference}. This can use for talkback
+ * To set learn more string of the {@link FooterPreference}. This can use for talkback
* environment if developer wants to have a customization content.
*
- * @param learnMoreContentDescriptionResId The resource id of the content description.
+ * @param learnMoreTextResId The resource id of the learn more string.
*/
- public Builder setLearnMoreContentDescription(
- @StringRes int learnMoreContentDescriptionResId) {
- mLearnMoreContentDescription = mContext.getText(learnMoreContentDescriptionResId);
+ public Builder setLearnMoreText(@StringRes int learnMoreTextResId) {
+ mLearnMoreText = mContext.getText(learnMoreTextResId);
return this;
}
@@ -301,8 +275,8 @@
footerPreference.setContentDescription(mContentDescription);
}
- if (!TextUtils.isEmpty(mLearnMoreContentDescription)) {
- footerPreference.setLearnMoreContentDescription(mLearnMoreContentDescription);
+ if (!TextUtils.isEmpty(mLearnMoreText)) {
+ footerPreference.setLearnMoreText(mLearnMoreText);
}
return footerPreference;
}
diff --git a/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml b/packages/SettingsLib/SettingsTheme/res/values-night-v31/colors.xml
index 5e3907c..5411591 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 b43b444..fb06976 100644
--- a/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/PrimarySwitchPreference.java
@@ -19,8 +19,6 @@
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.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 @@
@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 2f30baa..cb8e7e8 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedPreferenceHelper.java
@@ -198,10 +198,9 @@
if (mDisabledByAdmin != disabled) {
mDisabledByAdmin = disabled;
changed = true;
+ updateDisabledState();
}
- updateDisabledState();
-
return changed;
}
@@ -210,10 +209,9 @@
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 e6160bb..b5e4fa3 100644
--- a/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
+++ b/packages/SettingsLib/src/com/android/settingslib/RestrictedSwitchPreference.java
@@ -254,9 +254,11 @@
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 0000000..a5f69ff
--- /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 2d1a516..5c55a43 100644
--- a/packages/SettingsLib/tests/robotests/Android.bp
+++ b/packages/SettingsLib/tests/robotests/Android.bp
@@ -63,6 +63,7 @@
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 9c16740..74c2fc8 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.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 0000000..d67d44b
--- /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/src/com/android/settingslib/widget/FooterPreferenceTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
index 9abb27e..55125c5 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/widget/FooterPreferenceTest.java
@@ -74,13 +74,6 @@
}
@Test
- public void setLearnMoreContentDescription_contentSet_shouldGetSameContentDescription() {
- mFooterPreference.setLearnMoreContentDescription("test");
-
- assertThat(mFooterPreference.getLearnMoreContentDescription()).isEqualTo("test");
- }
-
- @Test
public void setLearnMoreAction_actionSet_shouldGetAction() {
mFooterPreference.setLearnMoreAction(v -> {
});
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 0000000..855da16
--- /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 d146fc9..b01fd6d 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 1c09e81f..18f870d 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 7010a28..1351ec1 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/ScreenDecorations.java b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
index 685c585..088dfb4 100644
--- a/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
+++ b/packages/SystemUI/src/com/android/systemui/ScreenDecorations.java
@@ -159,6 +159,8 @@
@VisibleForTesting
protected DisplayDecorationSupport mHwcScreenDecorationSupport;
private Display.Mode mDisplayMode;
+ @VisibleForTesting
+ protected DisplayInfo mDisplayInfo = new DisplayInfo();
private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
new CameraAvailabilityListener.CameraTransitionCallback() {
@@ -325,9 +327,10 @@
private void startOnScreenDecorationsThread() {
mWindowManager = mContext.getSystemService(WindowManager.class);
mDisplayManager = mContext.getSystemService(DisplayManager.class);
- mRotation = mContext.getDisplay().getRotation();
- mDisplayMode = mContext.getDisplay().getMode();
- mDisplayUniqueId = mContext.getDisplay().getUniqueId();
+ mContext.getDisplay().getDisplayInfo(mDisplayInfo);
+ mRotation = mDisplayInfo.rotation;
+ mDisplayMode = mDisplayInfo.getMode();
+ mDisplayUniqueId = mDisplayInfo.uniqueId;
mRoundedCornerResDelegate = new RoundedCornerResDelegate(mContext.getResources(),
mDisplayUniqueId);
mRoundedCornerResDelegate.setPhysicalPixelDisplaySizeRatio(
@@ -351,8 +354,9 @@
@Override
public void onDisplayChanged(int displayId) {
- final int newRotation = mContext.getDisplay().getRotation();
- final Display.Mode newDisplayMode = mContext.getDisplay().getMode();
+ mContext.getDisplay().getDisplayInfo(mDisplayInfo);
+ final int newRotation = mDisplayInfo.rotation;
+ final Display.Mode newDisplayMode = mDisplayInfo.getMode();
if ((mOverlays != null || mScreenDecorHwcWindow != null)
&& (mRotation != newRotation
|| displayModeChanged(mDisplayMode, newDisplayMode))) {
@@ -398,7 +402,7 @@
}
}
- final String newUniqueId = mContext.getDisplay().getUniqueId();
+ final String newUniqueId = mDisplayInfo.uniqueId;
if (!Objects.equals(newUniqueId, mDisplayUniqueId)) {
mDisplayUniqueId = newUniqueId;
final DisplayDecorationSupport newScreenDecorationSupport =
@@ -923,11 +927,10 @@
@VisibleForTesting
float getPhysicalPixelDisplaySizeRatio() {
final Point stableDisplaySize = mDisplayManager.getStableDisplaySize();
- final DisplayInfo displayInfo = new DisplayInfo();
- mContext.getDisplay().getDisplayInfo(displayInfo);
+ mContext.getDisplay().getDisplayInfo(mDisplayInfo);
return DisplayUtils.getPhysicalPixelDisplaySizeRatio(
- stableDisplaySize.x, stableDisplaySize.y, displayInfo.getNaturalWidth(),
- displayInfo.getNaturalHeight());
+ stableDisplaySize.x, stableDisplaySize.y, mDisplayInfo.getNaturalWidth(),
+ mDisplayInfo.getNaturalHeight());
}
@Override
@@ -1004,11 +1007,12 @@
"must call on " + mHandler.getLooper().getThread()
+ ", but was " + Thread.currentThread());
- int newRotation = mContext.getDisplay().getRotation();
+ mContext.getDisplay().getDisplayInfo(mDisplayInfo);
+ final int newRotation = mDisplayInfo.rotation;
if (mRotation != newRotation) {
mDotViewController.setNewRotation(newRotation);
}
- final Display.Mode newMod = mContext.getDisplay().getMode();
+ final Display.Mode newMod = mDisplayInfo.getMode();
if (!mPendingConfigChange
&& (newRotation != mRotation || displayModeChanged(mDisplayMode, newMod))) {
@@ -1220,7 +1224,7 @@
@Override
public void updateRotation(int rotation) {
mRotation = rotation;
- updateCutout();
+ super.updateRotation(rotation);
}
@VisibleForTesting(otherwise = VisibleForTesting.PROTECTED)
@@ -1431,9 +1435,10 @@
@Override
public boolean onPreDraw() {
- final int displayRotation = mContext.getDisplay().getRotation();
- final Display.Mode displayMode = mContext.getDisplay().getMode();
- if (displayRotation != mRotation && displayModeChanged(mDisplayMode, displayMode)
+ mContext.getDisplay().getDisplayInfo(mDisplayInfo);
+ final int displayRotation = mDisplayInfo.rotation;
+ final Display.Mode displayMode = mDisplayInfo.getMode();
+ if ((displayRotation != mRotation || displayModeChanged(mDisplayMode, displayMode))
&& !mPendingConfigChange) {
if (DEBUG) {
if (displayRotation != mRotation) {
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 44879aa..b8a0013 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 @@
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 80589a2..edd1c68 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 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 @@
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 @@
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 0f868718..e9caaaf 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 @@
}
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 @@
}
override fun onUiModeChanged() {
- recreatePlayers()
+ updatePlayers(recreateMedia = false)
inflateSettingsButton()
}
}
@@ -539,7 +540,7 @@
}
}
- private fun recreatePlayers() {
+ private fun updatePlayers(recreateMedia: Boolean) {
pageIndicator.tintList = ColorStateList.valueOf(
context.getColor(R.color.media_paging_indicator)
)
@@ -554,7 +555,9 @@
}
} 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 @@
.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 429f2df..731e177 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 @@
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 @@
.setContentText(contentText)
.setContentTitle(title)
.setOnlyAlertOnce(true)
- .setOngoing(true)
.setStyle(new Notification.BigTextStyle().bigText(contentText))
.setVisibility(Notification.VISIBILITY_PUBLIC);
if (hasBatterySettings()) {
@@ -340,8 +337,7 @@
}
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 dcdd784..67dae9e 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 @@
}
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 90a3d45..f1fdae7 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 @@
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 @@
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 22a6b0a..929dda6 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 @@
@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/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 62bb85a..81270c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -4712,6 +4712,11 @@
duration = StackStateAnimator.ANIMATION_DURATION_STANDARD;
}
mKeyguardStatusBarViewController.animateKeyguardStatusBarOut(startDelay, duration);
+ if (mSplitShadeEnabled) {
+ // temporary workaround for QS height not being updated during lockscreen to
+ // shade transition
+ setQsExpanded(true);
+ }
updateQSMinHeight();
} else if (oldState == StatusBarState.SHADE_LOCKED
&& statusBarState == KEYGUARD) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
index 0e77866..5e7dc11 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java
@@ -906,9 +906,19 @@
}
if (mQsExpansion > 0) {
behindAlpha = MathUtils.lerp(behindAlpha, mDefaultScrimAlpha, mQsExpansion);
+ float tintProgress = mQsExpansion;
+ if (mStatusBarKeyguardViewManager.isBouncerInTransit()) {
+ // this is case of - on lockscreen - going from expanded QS to bouncer.
+ // Because mQsExpansion is already interpolated and transition between tints
+ // is too slow, we want to speed it up and make it more aligned to bouncer
+ // showing up progress. This issue is visible on large screens, both portrait and
+ // split shade because then transition is between very different tints
+ tintProgress = BouncerPanelExpansionCalculator
+ .showBouncerProgress(mPanelExpansionFraction);
+ }
int stateTint = mClipsQsScrim ? ScrimState.SHADE_LOCKED.getNotifTint()
: ScrimState.SHADE_LOCKED.getBehindTint();
- behindTint = ColorUtils.blendARGB(behindTint, stateTint, mQsExpansion);
+ behindTint = ColorUtils.blendARGB(behindTint, stateTint, tintProgress);
}
// If the keyguard is going away, we should not be opaque.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
index 57253af..92e4947 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/ScreenDecorationsTest.java
@@ -63,6 +63,7 @@
import android.util.Size;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayInfo;
import android.view.Surface;
import android.view.View;
import android.view.ViewGroup;
@@ -136,6 +137,8 @@
private CornerDecorProvider mPrivacyDotBottomRightDecorProvider;
@Mock
private Display.Mode mDisplayMode;
+ @Mock
+ private DisplayInfo mDisplayInfo;
private PrivacyDotViewController.ShowingListener mPrivacyDotShowingListener;
@Before
@@ -159,7 +162,7 @@
when(mContext.getDisplay()).thenReturn(mDisplay);
// Not support hwc layer by default
doReturn(null).when(mDisplay).getDisplayDecorationSupport();
- doReturn(mDisplayMode).when(mDisplay).getMode();
+ doReturn(mDisplayMode).when(mDisplayInfo).getMode();
when(mMockTypedArray.length()).thenReturn(0);
mPrivacyDotTopLeftDecorProvider = spy(new PrivacyDotCornerDecorProviderImpl(
@@ -214,6 +217,7 @@
mExecutor.runAllReady();
}
});
+ mScreenDecorations.mDisplayInfo = mDisplayInfo;
doReturn(1f).when(mScreenDecorations).getPhysicalPixelDisplaySizeRatio();
reset(mTunerService);
@@ -977,7 +981,7 @@
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded4px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */);
- doReturn(Surface.ROTATION_0).when(mDisplay).getRotation();
+ mDisplayInfo.rotation = Surface.ROTATION_0;
mScreenDecorations.start();
@@ -991,7 +995,7 @@
getTestsDrawable(com.android.systemui.tests.R.drawable.rounded5px)
/* roundedBottomDrawable */,
0 /* roundedPadding */, false /* fillCutout */, true /* privacyDot */);
- doReturn(Surface.ROTATION_270).when(mDisplay).getRotation();
+ mDisplayInfo.rotation = Surface.ROTATION_270;
mScreenDecorations.onConfigurationChanged(null);
@@ -1274,7 +1278,6 @@
final ScreenDecorHwcLayer hwcLayer = mScreenDecorations.mScreenDecorHwcLayer;
spyOn(hwcLayer);
doReturn(mDisplay).when(hwcLayer).getDisplay();
- doReturn(mDisplayMode).when(mDisplay).getMode();
mScreenDecorations.mDisplayListener.onDisplayChanged(1);
@@ -1298,7 +1301,6 @@
mScreenDecorations.mCutoutViews[BOUNDS_POSITION_TOP];
spyOn(cutoutView);
doReturn(mDisplay).when(cutoutView).getDisplay();
- doReturn(mDisplayMode).when(mDisplay).getMode();
mScreenDecorations.mDisplayListener.onDisplayChanged(1);
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 0166fa2..6a6a65a 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 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 @@
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 @@
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 ceb811b..219b3c8 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 @@
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 e42ae1c..9a7b129 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.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 @@
.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 4a8cb0b..7b1e5c9 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.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.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 @@
private View mDialogLaunchView;
@Mock
private WifiStateWorker mWifiStateWorker;
+ @Mock
+ private SignalStrength mSignalStrength;
private TestableResources mTestableResources;
private InternetDialogController mInternetDialogController;
@@ -152,6 +162,8 @@
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 @@
@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 @@
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/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
index 5f8dda3..09009c6 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/ScrimControllerTest.java
@@ -19,6 +19,8 @@
import static com.android.systemui.statusbar.phone.ScrimController.OPAQUE;
import static com.android.systemui.statusbar.phone.ScrimController.SEMI_TRANSPARENT;
import static com.android.systemui.statusbar.phone.ScrimController.TRANSPARENT;
+import static com.android.systemui.statusbar.phone.ScrimState.BOUNCER;
+import static com.android.systemui.statusbar.phone.ScrimState.SHADE_LOCKED;
import static com.google.common.truth.Truth.assertThat;
@@ -275,7 +277,7 @@
@Test
public void transitionToShadeLocked() {
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setQsPosition(1f, 0);
finishAnimationsImmediately();
@@ -293,7 +295,7 @@
@Test
public void transitionToShadeLocked_clippingQs() {
mScrimController.setClipsQsScrim(true);
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setQsPosition(1f, 0);
finishAnimationsImmediately();
@@ -542,7 +544,7 @@
@Test
public void transitionToKeyguardBouncer() {
- mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.transitionTo(BOUNCER);
finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be visible without tint
@@ -561,7 +563,7 @@
@Test
public void transitionToKeyguardBouncer_clippingQs() {
mScrimController.setClipsQsScrim(true);
- mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.transitionTo(BOUNCER);
finishAnimationsImmediately();
// Front scrim should be transparent
// Back scrim should be clipping QS
@@ -581,7 +583,7 @@
@Test
public void disableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
mScrimController.setClipsQsScrim(true);
- mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.transitionTo(BOUNCER);
mScrimController.setClipsQsScrim(false);
@@ -602,7 +604,7 @@
@Test
public void enableClipQsScrimWithoutStateTransition_updatesTintAndAlpha() {
mScrimController.setClipsQsScrim(false);
- mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.transitionTo(BOUNCER);
mScrimController.setClipsQsScrim(true);
@@ -672,9 +674,9 @@
finishAnimationsImmediately();
assertEquals(mScrimState, ScrimState.UNLOCKED);
- mScrimController.transitionTo(ScrimState.BOUNCER);
+ mScrimController.transitionTo(BOUNCER);
finishAnimationsImmediately();
- assertEquals(mScrimState, ScrimState.BOUNCER);
+ assertEquals(mScrimState, BOUNCER);
mScrimController.transitionTo(ScrimState.BOUNCER_SCRIMMED);
finishAnimationsImmediately();
@@ -1081,9 +1083,9 @@
HashSet<ScrimState> lowPowerModeStates = new HashSet<>(Arrays.asList(
ScrimState.OFF, ScrimState.AOD, ScrimState.PULSING));
HashSet<ScrimState> regularStates = new HashSet<>(Arrays.asList(
- ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, ScrimState.BOUNCER,
+ ScrimState.UNINITIALIZED, ScrimState.KEYGUARD, BOUNCER,
ScrimState.DREAMING, ScrimState.BOUNCER_SCRIMMED, ScrimState.BRIGHTNESS_MIRROR,
- ScrimState.UNLOCKED, ScrimState.SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
+ ScrimState.UNLOCKED, SHADE_LOCKED, ScrimState.AUTH_SCRIMMED,
ScrimState.AUTH_SCRIMMED_SHADE));
for (ScrimState state : ScrimState.values()) {
@@ -1228,7 +1230,7 @@
@Test
public void testNotificationScrimVisible_afterOpeningShadeFromLockscreen() {
mScrimController.setRawPanelExpansionFraction(1);
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
@@ -1237,10 +1239,26 @@
}
@Test
+ public void qsExpansion_BehindTint_shadeLocked_bouncerActive_usesBouncerProgress() {
+ when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
+ // clipping doesn't change tested logic but allows to assert scrims more in line with
+ // their expected large screen behaviour
+ mScrimController.setClipsQsScrim(false);
+ mScrimController.transitionTo(SHADE_LOCKED);
+
+ mScrimController.setQsPosition(1f, 100 /* value doesn't matter */);
+ assertTintAfterExpansion(mScrimBehind, SHADE_LOCKED.getBehindTint(), /* expansion= */ 1f);
+
+ mScrimController.setQsPosition(0.8f, 100 /* value doesn't matter */);
+ // panel expansion of 0.6 means its fully transitioned with bouncer progress interpolation
+ assertTintAfterExpansion(mScrimBehind, BOUNCER.getBehindTint(), /* expansion= */ 0.6f);
+ }
+
+ @Test
public void expansionNotificationAlpha_shadeLocked_bouncerActive_usesBouncerInterpolator() {
when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(true);
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
float expansion = 0.8f;
float expectedAlpha =
@@ -1256,7 +1274,7 @@
public void expansionNotificationAlpha_shadeLocked_bouncerNotActive_usesShadeInterpolator() {
when(mStatusBarKeyguardViewManager.isBouncerInTransit()).thenReturn(false);
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
float expansion = 0.8f;
float expectedAlpha = ShadeInterpolation.getNotificationScrimAlpha(expansion);
@@ -1352,7 +1370,7 @@
@Test
public void testNotificationTransparency_followsTransitionToFullShade() {
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
float shadeLockedAlpha = mNotificationsScrim.getViewAlpha();
@@ -1379,7 +1397,7 @@
@Test
public void notificationTransparency_followsNotificationScrimProgress() {
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
mScrimController.setRawPanelExpansionFraction(1.0f);
finishAnimationsImmediately();
mScrimController.transitionTo(ScrimState.KEYGUARD);
@@ -1473,7 +1491,7 @@
mScrimBehind, TRANSPARENT,
mNotificationsScrim, TRANSPARENT));
- mScrimController.transitionTo(ScrimState.SHADE_LOCKED);
+ mScrimController.transitionTo(SHADE_LOCKED);
finishAnimationsImmediately();
assertScrimAlpha(Map.of(
mScrimInFront, TRANSPARENT,
@@ -1504,6 +1522,15 @@
assertEquals(expectedAlpha, scrim.getViewAlpha(), 0.2);
}
+ private void assertTintAfterExpansion(ScrimView scrim, int expectedTint, float expansion) {
+ String message = "Tint test failed with expected scrim tint: "
+ + Integer.toHexString(expectedTint) + " and actual tint: "
+ + Integer.toHexString(scrim.getTint()) + " for scrim: " + getScrimName(scrim);
+ mScrimController.setRawPanelExpansionFraction(expansion);
+ finishAnimationsImmediately();
+ assertEquals(message, expectedTint, scrim.getTint(), 0.1);
+ }
+
private void assertScrimTinted(Map<ScrimView, Boolean> scrimToTint) {
scrimToTint.forEach((scrim, hasTint) -> assertScrimTint(scrim, hasTint));
}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 826a98a..078a1d9 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -348,7 +348,9 @@
window.setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY);
window.addFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM
| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_DIM_BEHIND
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
+ window.setDimAmount(0.6f);
window.addPrivateFlags(WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS);
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
window.setGravity(Gravity.BOTTOM | Gravity.CENTER);
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index f26d9f9..76cac93 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_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 @@
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 @@
/** 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 @@
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 @@
final ParcelUuid uuidToTeardown = subGrp;
final Vcn instanceToTeardown = entry.getValue();
- stopVcnLocked(uuidToTeardown);
+ // 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);
+ // 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/ActiveServices.java b/services/core/java/com/android/server/am/ActiveServices.java
index 48b3d0e..e9eebbf 100644
--- a/services/core/java/com/android/server/am/ActiveServices.java
+++ b/services/core/java/com/android/server/am/ActiveServices.java
@@ -76,6 +76,7 @@
import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
import android.Manifest;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UptimeMillisLong;
@@ -174,6 +175,8 @@
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
@@ -211,6 +214,24 @@
| ServiceInfo.FOREGROUND_SERVICE_TYPE_CONNECTED_DEVICE
| ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION;
+ // Foreground service is stopped for unknown reason.
+ static final int FGS_STOP_REASON_UNKNOWN = 0;
+ // Foreground service is stopped by app calling Service.stopForeground().
+ static final int FGS_STOP_REASON_STOP_FOREGROUND = 1;
+ // Foreground service is stopped because service is brought down either by app calling
+ // stopService() or unbindService(), or service process is killed by the system.
+ static final int FGS_STOP_REASON_STOP_SERVICE = 2;
+ /**
+ * The list of FGS stop reasons.
+ */
+ @IntDef(flag = true, prefix = { "FGS_STOP_REASON_" }, value = {
+ FGS_STOP_REASON_UNKNOWN,
+ FGS_STOP_REASON_STOP_FOREGROUND,
+ FGS_STOP_REASON_STOP_SERVICE,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ @interface FgsStopReason {}
+
final ActivityManagerService mAm;
// Maximum number of services that we allow to start in the background
@@ -721,7 +742,7 @@
showFgsBgRestrictedNotificationLocked(r);
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0);
+ 0, FGS_STOP_REASON_UNKNOWN);
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID, callingUid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
}
@@ -1909,7 +1930,7 @@
ignoreForeground = true;
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED,
- 0);
+ 0, FGS_STOP_REASON_UNKNOWN);
if (CompatChanges.isChangeEnabled(FGS_START_EXCEPTION_CHANGE_ID,
r.appInfo.uid)) {
throw new ForegroundServiceStartNotAllowedException(msg);
@@ -1984,7 +2005,7 @@
mAm.updateForegroundServiceUsageStats(r.name, r.userId, true);
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER,
- 0);
+ 0, FGS_STOP_REASON_UNKNOWN);
}
// Even if the service is already a FGS, we need to update the notification,
// so we need to call it again.
@@ -2066,7 +2087,8 @@
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
- ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
+ FGS_STOP_REASON_STOP_FOREGROUND);
r.mFgsNotificationWasDeferred = false;
signalForegroundServiceObserversLocked(r);
resetFgsRestrictionLocked(r);
@@ -4652,7 +4674,8 @@
logFGSStateChangeLocked(r,
FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT,
r.mFgsExitTime > r.mFgsEnterTime
- ? (int)(r.mFgsExitTime - r.mFgsEnterTime) : 0);
+ ? (int) (r.mFgsExitTime - r.mFgsEnterTime) : 0,
+ FGS_STOP_REASON_STOP_SERVICE);
mAm.updateForegroundServiceUsageStats(r.name, r.userId, false);
}
@@ -6846,7 +6869,8 @@
* @param state one of ENTER/EXIT/DENIED event.
* @param durationMs Only meaningful for EXIT event, the duration from ENTER and EXIT state.
*/
- private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs) {
+ private void logFGSStateChangeLocked(ServiceRecord r, int state, int durationMs,
+ @FgsStopReason int fgsStopReason) {
if (!ActivityManagerUtils.shouldSamplePackageForAtom(
r.packageName, mAm.mConstants.mFgsAtomSampleRate)) {
return;
@@ -6861,6 +6885,8 @@
allowWhileInUsePermissionInFgs = r.mAllowWhileInUsePermissionInFgs;
fgsStartReasonCode = r.mAllowStartForeground;
}
+ final int callerTargetSdkVersion = r.mRecentCallerApplicationInfo != null
+ ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0;
FrameworkStatsLog.write(FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED,
r.appInfo.uid,
r.shortInstanceName,
@@ -6869,8 +6895,7 @@
fgsStartReasonCode,
r.appInfo.targetSdkVersion,
r.mRecentCallingUid,
- r.mRecentCallerApplicationInfo != null
- ? r.mRecentCallerApplicationInfo.targetSdkVersion : 0,
+ callerTargetSdkVersion,
r.mInfoTempFgsAllowListReason != null
? r.mInfoTempFgsAllowListReason.mCallingUid : INVALID_UID,
r.mFgsNotificationWasDeferred,
@@ -6879,6 +6904,30 @@
r.mStartForegroundCount,
ActivityManagerUtils.hashComponentNameForAtom(r.shortInstanceName),
r.mFgsHasNotificationPermission);
+
+ int event = 0;
+ if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__ENTER) {
+ event = EventLogTags.AM_FOREGROUND_SERVICE_START;
+ } else if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__EXIT) {
+ event = EventLogTags.AM_FOREGROUND_SERVICE_STOP;
+ } else if (state == FrameworkStatsLog.FOREGROUND_SERVICE_STATE_CHANGED__STATE__DENIED) {
+ event = EventLogTags.AM_FOREGROUND_SERVICE_DENIED;
+ } else {
+ // Unknown event.
+ return;
+ }
+ EventLog.writeEvent(event,
+ r.userId,
+ r.shortInstanceName,
+ allowWhileInUsePermissionInFgs ? 1 : 0,
+ reasonCodeToString(fgsStartReasonCode),
+ r.appInfo.targetSdkVersion,
+ callerTargetSdkVersion,
+ r.mFgsNotificationWasDeferred ? 1 : 0,
+ r.mFgsNotificationShown ? 1 : 0,
+ durationMs,
+ r.mStartForegroundCount,
+ fgsStopReasonToString(fgsStopReason));
}
boolean canAllowWhileInUsePermissionInFgsLocked(int callingPid, int callingUid,
@@ -6903,4 +6952,15 @@
return mAm.getPackageManagerInternal().isSameApp(packageName, uid,
UserHandle.getUserId(uid));
}
+
+ private static String fgsStopReasonToString(@FgsStopReason int stopReason) {
+ switch (stopReason) {
+ case FGS_STOP_REASON_STOP_SERVICE:
+ return "STOP_SERVICE";
+ case FGS_STOP_REASON_STOP_FOREGROUND:
+ return "STOP_FOREGROUND";
+ default:
+ return "UNKNOWN";
+ }
+ }
}
diff --git a/services/core/java/com/android/server/am/AppBatteryTracker.java b/services/core/java/com/android/server/am/AppBatteryTracker.java
index 1a566a9..6619025 100644
--- a/services/core/java/com/android/server/am/AppBatteryTracker.java
+++ b/services/core/java/com/android/server/am/AppBatteryTracker.java
@@ -839,6 +839,8 @@
@Override
void dumpAsProto(ProtoOutputStream proto, int uid) {
+ // Force an update.
+ updateBatteryUsageStatsIfNecessary(mInjector.currentTimeMillis(), true);
synchronized (mLock) {
final SparseArray<ImmutableBatteryUsage> uidConsumers = mUidBatteryUsageInWindow;
if (uid != android.os.Process.INVALID_UID) {
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 7253b494..7fe3c1f 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 @@
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 @@
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 @@
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/EventLogTags.logtags b/services/core/java/com/android/server/am/EventLogTags.logtags
index b250a0c..d080036 100644
--- a/services/core/java/com/android/server/am/EventLogTags.logtags
+++ b/services/core/java/com/android/server/am/EventLogTags.logtags
@@ -116,3 +116,11 @@
30086 ssm_user_stopping (userId|1|5)
30087 ssm_user_stopped (userId|1|5)
30088 ssm_user_completed_event (userId|1|5),(eventFlag|1|5)
+
+# Foreground service start/stop events.
+30100 am_foreground_service_start (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
+30101 am_foreground_service_denied (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
+30102 am_foreground_service_stop (User|1|5),(Component Name|3),(allowWhileInUse|1),(startReasonCode|3),(targetSdk|1|1),(callerTargetSdk|1|1),(notificationWasDeferred|1),(notificationShown|1),(durationMs|1|3),(startForegroundCount|1|1),(stopReason|3)
+
+
+
diff --git a/services/core/java/com/android/server/am/TEST_MAPPING b/services/core/java/com/android/server/am/TEST_MAPPING
index 03eddc9..e4f624d 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/audio/PlaybackActivityMonitor.java b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
index 565783f..a4468a3 100644
--- a/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
+++ b/services/core/java/com/android/server/audio/PlaybackActivityMonitor.java
@@ -494,7 +494,7 @@
}
pw.println("\n");
// muted players:
- pw.print("\n muted players (piids) awaiting device connection: BL3 ####");
+ pw.print("\n muted players (piids) awaiting device connection:");
for (int piid : mMutedPlayersAwaitingConnection) {
pw.print(" " + piid);
}
@@ -1235,9 +1235,7 @@
@GuardedBy("mPlayerLock")
private void unmutePlayersExpectingDevice() {
- if (mMutedPlayersAwaitingConnection.isEmpty()) {
- return;
- }
+ mMutedUsagesAwaitingConnection = null;
for (int piid : mMutedPlayersAwaitingConnection) {
final AudioPlaybackConfiguration apc = mPlayers.get(piid);
if (apc == null) {
@@ -1254,7 +1252,6 @@
}
}
mMutedPlayersAwaitingConnection.clear();
- mMutedUsagesAwaitingConnection = null;
}
//=================================================================
@@ -1278,8 +1275,8 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_L_TIMEOUT_MUTE_AWAIT_CONNECTION:
- Log.i(TAG, "Timeout for muting waiting for "
- + (AudioDeviceAttributes) msg.obj + ", unmuting");
+ sEventLogger.loglogi("Timeout for muting waiting for "
+ + (AudioDeviceAttributes) msg.obj + ", unmuting", TAG);
synchronized (mPlayerLock) {
unmutePlayersExpectingDevice();
}
diff --git a/services/core/java/com/android/server/content/ContentService.java b/services/core/java/com/android/server/content/ContentService.java
index f93e06d..2564ae8 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.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.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 @@
}
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 @@
}
}
+ 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 b624d43..72612a0 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.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 @@
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 @@
// 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 @@
@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 @@
}
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 @@
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 @@
}
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 @@
/**
* 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 @@
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 @@
// 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 @@
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 @@
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 @@
(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 31d5136..e5eed99 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 @@
@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/location/gnss/GnssNetworkConnectivityHandler.java b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
index 70b8689..2e1aaf8 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNetworkConnectivityHandler.java
@@ -16,6 +16,8 @@
package com.android.server.location.gnss;
+import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED;
+
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.LinkAddress;
@@ -47,7 +49,6 @@
import java.util.List;
import java.util.Map;
-
/**
* Handles network connection requests and network state change updates for AGPS data download.
*/
@@ -582,6 +583,7 @@
if (mNiHandler.getInEmergency() && mActiveSubId >= 0) {
if (DEBUG) Log.d(TAG, "Adding Network Specifier: " + Integer.toString(mActiveSubId));
networkRequestBuilder.setNetworkSpecifier(Integer.toString(mActiveSubId));
+ networkRequestBuilder.removeCapability(NET_CAPABILITY_NOT_RESTRICTED);
}
NetworkRequest networkRequest = networkRequestBuilder.build();
// Make sure we only have a single request.
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 1937852..098e8f7 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.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 @@
}
}
+ /**
+ * 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/ApkChecksums.java b/services/core/java/com/android/server/pm/ApkChecksums.java
index 3911994..c2f2b0a 100644
--- a/services/core/java/com/android/server/pm/ApkChecksums.java
+++ b/services/core/java/com/android/server/pm/ApkChecksums.java
@@ -77,6 +77,7 @@
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
import java.nio.file.Files;
import java.security.DigestException;
import java.security.InvalidParameterException;
@@ -584,7 +585,7 @@
});
checksums.put(TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
- generatedRootHash));
+ verityHashForFile(file, generatedRootHash)));
} catch (IOException | NoSuchAlgorithmException | DigestException e) {
Slog.e(TAG, "Error calculating WHOLE_MERKLE_ROOT_4K_SHA256", e);
}
@@ -649,19 +650,20 @@
// Skip /product folder.
// TODO(b/231354111): remove this hack once we are allowed to change SELinux rules.
if (!containsFile(Environment.getProductDirectory(), filePath)) {
- byte[] hash = VerityUtils.getFsverityRootHash(filePath);
- if (hash != null) {
- return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+ byte[] verityHash = VerityUtils.getFsverityRootHash(filePath);
+ if (verityHash != null) {
+ return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, verityHash);
}
}
// v4 next
try {
ApkSignatureSchemeV4Verifier.VerifiedSigner signer =
ApkSignatureSchemeV4Verifier.extractCertificates(filePath);
- byte[] hash = signer.contentDigests.getOrDefault(CONTENT_DIGEST_VERITY_CHUNKED_SHA256,
- null);
- if (hash != null) {
- return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256, hash);
+ byte[] rootHash = signer.contentDigests.getOrDefault(
+ CONTENT_DIGEST_VERITY_CHUNKED_SHA256, null);
+ if (rootHash != null) {
+ return new ApkChecksum(split, TYPE_WHOLE_MERKLE_ROOT_4K_SHA256,
+ verityHashForFile(new File(filePath), rootHash));
}
} catch (SignatureNotFoundException e) {
// Nothing
@@ -671,6 +673,41 @@
return null;
}
+ /**
+ * Returns fs-verity digest as described in
+ * https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#fs-verity-descriptor
+ * @param file the Merkle tree is built over
+ * @param rootHash Merkle tree root hash
+ */
+ static byte[] verityHashForFile(File file, byte[] rootHash) {
+ try {
+ ByteBuffer buffer = ByteBuffer.allocate(256); // sizeof(fsverity_descriptor)
+ buffer.order(ByteOrder.LITTLE_ENDIAN);
+ buffer.put((byte) 1); // __u8 version, must be 1
+ buffer.put((byte) 1); // __u8 hash_algorithm, FS_VERITY_HASH_ALG_SHA256
+ buffer.put((byte) 12); // __u8, FS_VERITY_LOG_BLOCKSIZE
+ buffer.put((byte) 0); // __u8, size of salt in bytes; 0 if none
+ buffer.putInt(0); // __le32 __reserved_0x04, must be 0
+ buffer.putLong(file.length()); // __le64 data_size
+ buffer.put(rootHash); // root_hash, first 32 bytes
+ final int padding = 32 + 32 + 144; // root_hash, last 32 bytes, we are using sha256.
+ // salt, 32 bytes
+ // reserved, 144 bytes
+ for (int i = 0; i < padding; ++i) {
+ buffer.put((byte) 0);
+ }
+
+ buffer.flip();
+
+ final MessageDigest md = MessageDigest.getInstance(ALGO_SHA256);
+ md.update(buffer);
+ return md.digest();
+ } catch (NoSuchAlgorithmException e) {
+ Slog.e(TAG, "Device does not support MessageDigest algorithm", e);
+ return null;
+ }
+ }
+
private static Map<Integer, ApkChecksum> extractHashFromV2V3Signature(
String split, String filePath, int types) {
Map<Integer, byte[]> contentDigests = null;
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
index 703be16..719c3b7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceUtils.java
@@ -1069,7 +1069,7 @@
if (ArrayUtils.isEmpty(hashInfo.rawRootHash)) {
throw new IOException("Root has not present");
}
- return hashInfo.rawRootHash;
+ return ApkChecksums.verityHashForFile(new File(filename), hashInfo.rawRootHash);
} catch (IOException ignore) {
Slog.e(TAG, "ERROR: could not load root hash from incremental install");
}
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 6796065..cc1c943 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -603,15 +603,10 @@
final String setupWizardPackage = ArrayUtils.firstOrNull(getKnownPackages(
KnownPackages.PACKAGE_SETUP_WIZARD, userId));
grantPermissionsToSystemPackage(pm, setupWizardPackage, userId, PHONE_PERMISSIONS,
- CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS);
+ CONTACTS_PERMISSIONS, ALWAYS_LOCATION_PERMISSIONS, CAMERA_PERMISSIONS,
+ NEARBY_DEVICES_PERMISSIONS);
grantSystemFixedPermissionsToSystemPackage(pm, setupWizardPackage, userId,
NOTIFICATION_PERMISSIONS);
- if (mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH, 0)
- || mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE,
- 0)) {
- grantPermissionsToSystemPackage(
- pm, setupWizardPackage, userId, NEARBY_DEVICES_PERMISSIONS);
- }
// SearchSelector
grantPermissionsToSystemPackage(pm, getDefaultSearchSelectorPackage(), userId,
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 8552bba..d34682d 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 @@
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 @@
UidPermissionState origState = uidState;
- boolean installPermissionsChangedForUser = false;
+ boolean changedInstallPermission = false;
if (replace) {
userState.setInstallPermissionsFixed(ps.getPackageName(), false);
@@ -2801,7 +2800,7 @@
&& 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 @@
}
}
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 @@
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 @@
// 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/ConfigurationContainer.java b/services/core/java/com/android/server/wm/ConfigurationContainer.java
index e7c03aa..fcdf175 100644
--- a/services/core/java/com/android/server/wm/ConfigurationContainer.java
+++ b/services/core/java/com/android/server/wm/ConfigurationContainer.java
@@ -126,6 +126,10 @@
mResolvedTmpConfig.setTo(mResolvedOverrideConfiguration);
resolveOverrideConfiguration(newParentConfig);
mFullConfiguration.setTo(newParentConfig);
+ // Do not inherit always-on-top property from parent, otherwise the always-on-top
+ // property is propagated to all children. In that case, newly added child is
+ // always being positioned at bottom (behind the always-on-top siblings).
+ mFullConfiguration.windowConfiguration.unsetAlwaysOnTop();
mFullConfiguration.updateFrom(mResolvedOverrideConfiguration);
onMergedOverrideConfigurationChanged();
if (!mResolvedTmpConfig.equals(mResolvedOverrideConfiguration)) {
@@ -228,6 +232,10 @@
final ConfigurationContainer parent = getParent();
if (parent != null) {
mMergedOverrideConfiguration.setTo(parent.getMergedOverrideConfiguration());
+ // Do not inherit always-on-top property from parent, otherwise the always-on-top
+ // property is propagated to all children. In that case, newly added child is
+ // always being positioned at bottom (behind the always-on-top siblings).
+ mMergedOverrideConfiguration.windowConfiguration.unsetAlwaysOnTop();
mMergedOverrideConfiguration.updateFrom(mResolvedOverrideConfiguration);
} else {
mMergedOverrideConfiguration.setTo(mResolvedOverrideConfiguration);
diff --git a/services/core/java/com/android/server/wm/ContentRecorder.java b/services/core/java/com/android/server/wm/ContentRecorder.java
index 87523f4..5d2d582 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 @@
// 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 c0d7d13..9eee7ba 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.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 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 7a54804..9022186 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 @@
}
}
- /**
- * 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 @@
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/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
index ac115a2..cb14864 100644
--- a/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/DeviceIdleControllerTest.java
@@ -284,7 +284,7 @@
@Override
public void onDeviceStationaryChanged(boolean isStationary) {
if (isStationary == motionExpected) {
- fail("Unexpected device stationary status: " + isStationary);
+ fail("Got unexpected device stationary status: " + isStationary);
}
this.isStationary = isStationary;
}
@@ -2096,6 +2096,70 @@
eq(SensorManager.SENSOR_DELAY_NORMAL));
}
+ @Test
+ public void testStationaryDetection_NoDoze_AfterMotion() {
+ // Short timeout for testing.
+ mConstants.MOTION_INACTIVE_TIMEOUT = 6000L;
+ doReturn(Sensor.REPORTING_MODE_CONTINUOUS).when(mMotionSensor).getReportingMode();
+ setAlarmSoon(true);
+
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> regAlarmListener = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ final ArgumentCaptor<AlarmManager.OnAlarmListener> motionAlarmListener = ArgumentCaptor
+ .forClass(AlarmManager.OnAlarmListener.class);
+ doNothing().when(mAlarmManager).setWindow(
+ anyInt(), anyLong(), anyLong(), eq("DeviceIdleController.motion"),
+ motionAlarmListener.capture(), any());
+ doNothing().when(mAlarmManager).setWindow(anyInt(), anyLong(), anyLong(),
+ eq("DeviceIdleController.motion_registration"),
+ regAlarmListener.capture(), any());
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+
+ StationaryListenerForTest stationaryListener = new StationaryListenerForTest();
+ spyOn(stationaryListener);
+ InOrder inOrder = inOrder(stationaryListener, mSensorManager, mAlarmManager);
+
+ stationaryListener.motionExpected = true;
+ mDeviceIdleController.registerStationaryListener(stationaryListener);
+ inOrder.verify(stationaryListener, timeout(1000L).times(1))
+ .onDeviceStationaryChanged(eq(false));
+ assertFalse(stationaryListener.isStationary);
+ inOrder.verify(mSensorManager)
+ .registerListener(listenerCaptor.capture(), eq(mMotionSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL));
+ inOrder.verify(mAlarmManager).setWindow(
+ anyInt(), eq(mInjector.nowElapsed + mConstants.MOTION_INACTIVE_TIMEOUT), anyLong(),
+ eq("DeviceIdleController.motion"), any(), any());
+ final SensorEventListener listener = listenerCaptor.getValue();
+
+ // Trigger motion
+ listener.onSensorChanged(mock(SensorEvent.class));
+ inOrder.verify(stationaryListener, timeout(1000L).times(1))
+ .onDeviceStationaryChanged(eq(false));
+ final ArgumentCaptor<Long> registrationTimeCaptor = ArgumentCaptor.forClass(Long.class);
+ inOrder.verify(mAlarmManager).setWindow(
+ anyInt(), registrationTimeCaptor.capture(), anyLong(),
+ eq("DeviceIdleController.motion_registration"), any(), any());
+
+ // Make sure the listener is re-registered.
+ mInjector.nowElapsed = registrationTimeCaptor.getValue();
+ regAlarmListener.getValue().onAlarm();
+ inOrder.verify(mSensorManager)
+ .registerListener(eq(listener), eq(mMotionSensor),
+ eq(SensorManager.SENSOR_DELAY_NORMAL));
+ final ArgumentCaptor<Long> timeoutCaptor = ArgumentCaptor.forClass(Long.class);
+ inOrder.verify(mAlarmManager).setWindow(anyInt(), timeoutCaptor.capture(), anyLong(),
+ eq("DeviceIdleController.motion"), any(), any());
+
+ // No motion before timeout
+ stationaryListener.motionExpected = false;
+ mInjector.nowElapsed = timeoutCaptor.getValue();
+ motionAlarmListener.getValue().onAlarm();
+ inOrder.verify(stationaryListener, timeout(1000L).times(1))
+ .onDeviceStationaryChanged(eq(true));
+ }
+
private void enterDeepState(int state) {
switch (state) {
case STATE_ACTIVE:
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 e78f0c7..844f5d4 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.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 @@
@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 @@
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/ConfigurationContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
index 59b12e4..5d824e9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ConfigurationContainerTests.java
@@ -21,6 +21,7 @@
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
+import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
@@ -210,6 +211,20 @@
}
@Test
+ public void testSetAlwaysOnTop() {
+ final TestConfigurationContainer root = new TestConfigurationContainer();
+ final TestConfigurationContainer child1 = root.addChild();
+ final TestConfigurationContainer child2 = root.addChild();
+ root.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW);
+ root.setAlwaysOnTop(true);
+ final TestConfigurationContainer child3 = root.addChild();
+ assertEquals(true, root.isAlwaysOnTop());
+ assertEquals(false, child1.isAlwaysOnTop());
+ assertEquals(false, child2.isAlwaysOnTop());
+ assertEquals(false, child3.isAlwaysOnTop());
+ }
+
+ @Test
public void testSetWindowingMode() {
final TestConfigurationContainer root = new TestConfigurationContainer();
root.setWindowingMode(WINDOWING_MODE_UNDEFINED);
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 c5f785e..32f3bfe 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 @@
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 @@
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/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
index e5e0145..e8f1d23 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TaskDisplayAreaTests.java
@@ -674,8 +674,6 @@
taskDisplayArea.positionChildAt(POSITION_TOP, alwaysOnTopRootTask,
false /* includingParents */);
assertTrue(alwaysOnTopRootTask.isAlwaysOnTop());
- // Ensure always on top state is synced to the children of the root task.
- assertTrue(alwaysOnTopRootTask.getTopNonFinishingActivity().isAlwaysOnTop());
assertEquals(alwaysOnTopRootTask, taskDisplayArea.getTopRootTask());
final Task pinnedRootTask = taskDisplayArea.createRootTask(
diff --git a/services/usage/java/com/android/server/usage/TEST_MAPPING b/services/usage/java/com/android/server/usage/TEST_MAPPING
index 523d5f9..1c0c71b 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 dc96c66..df9fc66 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 @@
// 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 @@
// 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 @@
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 @@
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 @@
}
Log.d(TAG, "input thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -402,18 +411,23 @@
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 @@
}
Log.d(TAG, "output thread exit");
}
- }.start();
-
+ };
+ newThread.start();
+ mThreads.add(newThread);
portNumber++;
}
}
@@ -523,6 +538,27 @@
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 54b3c40..f924b2e 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_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 @@
@Test
public void testSystemReady() throws Exception {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
verify(mConnMgr).registerNetworkProvider(any(VcnNetworkProvider.class));
verify(mSubscriptionTracker).register();
@@ -494,8 +494,10 @@
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 @@
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 @@
private void setupSubscriptionAndStartVcn(
int subId, ParcelUuid subGrp, boolean isVcnActive, boolean hasCarrierPrivileges) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
-
triggerSubscriptionTrackerCbAndGetSnapshot(
subGrp,
Collections.singleton(subGrp),
@@ -1007,7 +1093,6 @@
private void setupTrackedCarrierWifiNetwork(NetworkCapabilities caps) {
mVcnMgmtSvc.systemReady();
- mTestLooper.dispatchAll();
final ArgumentCaptor<NetworkCallback> captor =
ArgumentCaptor.forClass(NetworkCallback.class);
@@ -1252,14 +1337,15 @@
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