Merge "Adding permission for Ethernet Network Management"
diff --git a/Android.bp b/Android.bp
index e03f844..25238e8 100644
--- a/Android.bp
+++ b/Android.bp
@@ -308,6 +308,8 @@
include_dirs: [
"frameworks/av/aidl",
"frameworks/native/libs/permission/aidl",
+ // TODO: remove when moved to the below package
+ "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
"packages/modules/Connectivity/framework/aidl-export",
],
},
@@ -557,6 +559,9 @@
include_dirs: [
"frameworks/av/aidl",
"frameworks/native/libs/permission/aidl",
+ // TODO: remove when moved to the below package
+ "frameworks/base/packages/ConnectivityT/framework-t/aidl-export",
+ "packages/modules/Connectivity/framework/aidl-export",
],
},
// These are libs from framework-internal-utils that are required (i.e. being referenced)
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index afad29c..c4795f55 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -364,6 +364,11 @@
* @hide
*/
public static final int REASON_SYSTEM_MODULE = 320;
+ /**
+ * Carrier privileged app.
+ * @hide
+ */
+ public static final int REASON_CARRIER_PRIVILEGED_APP = 321;
/** @hide The app requests out-out. */
public static final int REASON_OPT_OUT_REQUESTED = 1000;
@@ -440,6 +445,7 @@
REASON_ROLE_DIALER,
REASON_ROLE_EMERGENCY,
REASON_SYSTEM_MODULE,
+ REASON_CARRIER_PRIVILEGED_APP,
REASON_OPT_OUT_REQUESTED,
})
@Retention(RetentionPolicy.SOURCE)
@@ -749,6 +755,8 @@
return "ROLE_EMERGENCY";
case REASON_SYSTEM_MODULE:
return "SYSTEM_MODULE";
+ case REASON_CARRIER_PRIVILEGED_APP:
+ return "CARRIER_PRIVILEGED_APP";
case REASON_OPT_OUT_REQUESTED:
return "REASON_OPT_OUT_REQUESTED";
default:
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 b2ae8ee..cea1945 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -357,6 +357,9 @@
// Putting RESTRICTED_INDEX after NEVER_INDEX to make it easier for proto dumping
// (ScheduledJobStateChanged and JobStatusDumpProto).
public static final int RESTRICTED_INDEX = 5;
+ // Putting EXEMPTED_INDEX after RESTRICTED_INDEX to make it easier for proto dumping
+ // (ScheduledJobStateChanged and JobStatusDumpProto).
+ public static final int EXEMPTED_INDEX = 6;
private class ConstantsObserver implements DeviceConfig.OnPropertiesChangedListener,
EconomyManagerInternal.TareStateChangeListener {
@@ -2492,6 +2495,7 @@
shouldForceBatchJob =
mConstants.MIN_READY_NON_ACTIVE_JOBS_COUNT > 1
&& job.getEffectiveStandbyBucket() != ACTIVE_INDEX
+ && job.getEffectiveStandbyBucket() != EXEMPTED_INDEX
&& !batchDelayExpired;
}
@@ -2764,7 +2768,7 @@
return job.getEffectiveStandbyBucket() != RESTRICTED_INDEX
? mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS
: Math.min(mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS, 5 * MINUTE_IN_MILLIS);
- } else if (job.getEffectivePriority() == JobInfo.PRIORITY_HIGH) {
+ } else if (job.getEffectivePriority() >= JobInfo.PRIORITY_HIGH) {
return mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS;
} else {
return mConstants.RUNTIME_MIN_GUARANTEE_MS;
@@ -3086,8 +3090,10 @@
return FREQUENT_INDEX;
} else if (bucket > UsageStatsManager.STANDBY_BUCKET_ACTIVE) {
return WORKING_INDEX;
- } else {
+ } else if (bucket > UsageStatsManager.STANDBY_BUCKET_EXEMPTED) {
return ACTIVE_INDEX;
+ } else {
+ return EXEMPTED_INDEX;
}
}
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
index 0eea701..0456a9b 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/JobStatus.java
@@ -17,6 +17,7 @@
package com.android.server.job.controllers;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.WORKING_INDEX;
@@ -844,12 +845,15 @@
* exemptions.
*/
public int getEffectiveStandbyBucket() {
+ final int actualBucket = getStandbyBucket();
+ if (actualBucket == EXEMPTED_INDEX) {
+ return actualBucket;
+ }
if (uidActive || getJob().isExemptedFromAppStandby()) {
// Treat these cases as if they're in the ACTIVE bucket so that they get throttled
// like other ACTIVE apps.
return ACTIVE_INDEX;
}
- final int actualBucket = getStandbyBucket();
if (actualBucket != RESTRICTED_INDEX && actualBucket != NEVER_INDEX
&& mHasMediaBackupExemption) {
// Cap it at WORKING_INDEX as media back up jobs are important to the user, and the
diff --git a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
index dd5246a..c1728a3 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/controllers/QuotaController.java
@@ -21,6 +21,7 @@
import static android.text.format.DateUtils.SECOND_IN_MILLIS;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
@@ -132,6 +133,7 @@
*/
public long expirationTimeElapsed;
+ public long allowedTimePerPeriodMs;
public long windowSizeMs;
public int jobCountLimit;
public int sessionCountLimit;
@@ -213,6 +215,7 @@
@Override
public String toString() {
return "expirationTime=" + expirationTimeElapsed + ", "
+ + "allowedTimePerPeriodMs=" + allowedTimePerPeriodMs + ", "
+ "windowSizeMs=" + windowSizeMs + ", "
+ "jobCountLimit=" + jobCountLimit + ", "
+ "sessionCountLimit=" + sessionCountLimit + ", "
@@ -236,6 +239,7 @@
if (obj instanceof ExecutionStats) {
ExecutionStats other = (ExecutionStats) obj;
return this.expirationTimeElapsed == other.expirationTimeElapsed
+ && this.allowedTimePerPeriodMs == other.allowedTimePerPeriodMs
&& this.windowSizeMs == other.windowSizeMs
&& this.jobCountLimit == other.jobCountLimit
&& this.sessionCountLimit == other.sessionCountLimit
@@ -261,6 +265,7 @@
public int hashCode() {
int result = 0;
result = 31 * result + hashLong(expirationTimeElapsed);
+ result = 31 * result + hashLong(allowedTimePerPeriodMs);
result = 31 * result + hashLong(windowSizeMs);
result = 31 * result + hashLong(jobCountLimit);
result = 31 * result + hashLong(sessionCountLimit);
@@ -350,7 +355,15 @@
private boolean mIsEnabled;
/** How much time each app will have to run jobs within their standby bucket window. */
- private long mAllowedTimePerPeriodMs = QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
+ private final long[] mAllowedTimePerPeriodMs = new long[]{
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS,
+ 0, // NEVER
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ QcConstants.DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS
+ };
/**
* The maximum amount of time an app can have its jobs running within a {@link #MAX_PERIOD_MS}
@@ -365,12 +378,6 @@
private long mQuotaBufferMs = QcConstants.DEFAULT_IN_QUOTA_BUFFER_MS;
/**
- * {@link #mAllowedTimePerPeriodMs} - {@link #mQuotaBufferMs}. This can be used to determine
- * when an app will have enough quota to transition from out-of-quota to in-quota.
- */
- private long mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
-
- /**
* {@link #mMaxExecutionTimeMs} - {@link #mQuotaBufferMs}. This can be used to determine when an
* app will have enough quota to transition from out-of-quota to in-quota.
*/
@@ -450,7 +457,8 @@
QcConstants.DEFAULT_WINDOW_SIZE_FREQUENT_MS,
QcConstants.DEFAULT_WINDOW_SIZE_RARE_MS,
0, // NEVER
- QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS
+ QcConstants.DEFAULT_WINDOW_SIZE_RESTRICTED_MS,
+ QcConstants.DEFAULT_WINDOW_SIZE_EXEMPTED_MS
};
/** The maximum period any bucket can have. */
@@ -469,7 +477,8 @@
QcConstants.DEFAULT_MAX_JOB_COUNT_FREQUENT,
QcConstants.DEFAULT_MAX_JOB_COUNT_RARE,
0, // NEVER
- QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED
+ QcConstants.DEFAULT_MAX_JOB_COUNT_RESTRICTED,
+ QcConstants.DEFAULT_MAX_JOB_COUNT_EXEMPTED
};
/**
@@ -487,6 +496,7 @@
QcConstants.DEFAULT_MAX_SESSION_COUNT_RARE,
0, // NEVER
QcConstants.DEFAULT_MAX_SESSION_COUNT_RESTRICTED,
+ QcConstants.DEFAULT_MAX_SESSION_COUNT_EXEMPTED,
};
/**
@@ -506,7 +516,8 @@
QcConstants.DEFAULT_EJ_LIMIT_FREQUENT_MS,
QcConstants.DEFAULT_EJ_LIMIT_RARE_MS,
0, // NEVER
- QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS
+ QcConstants.DEFAULT_EJ_LIMIT_RESTRICTED_MS,
+ QcConstants.DEFAULT_EJ_LIMIT_EXEMPTED_MS
};
private long mEjLimitAdditionInstallerMs = QcConstants.DEFAULT_EJ_LIMIT_ADDITION_INSTALLER_MS;
@@ -823,6 +834,11 @@
if (mService.isBatteryCharging()) {
return mConstants.RUNTIME_FREE_QUOTA_MAX_LIMIT_MS;
}
+ if (jobStatus.getEffectiveStandbyBucket() == EXEMPTED_INDEX) {
+ return Math.max(mEJLimitsMs[EXEMPTED_INDEX] / 2,
+ getTimeUntilEJQuotaConsumedLocked(
+ jobStatus.getSourceUserId(), jobStatus.getSourcePackageName()));
+ }
if (mTopAppCache.get(jobStatus.getSourceUid()) || isTopStartedJobLocked(jobStatus)) {
return Math.max(mEJLimitsMs[ACTIVE_INDEX] / 2,
getTimeUntilEJQuotaConsumedLocked(
@@ -929,9 +945,11 @@
final long minSurplus;
if (priority <= JobInfo.PRIORITY_MIN) {
- minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityMin);
+ minSurplus = (long)
+ (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityMin);
} else if (priority <= JobInfo.PRIORITY_LOW) {
- minSurplus = (long) (mAllowedTimePerPeriodMs * mAllowedTimeSurplusPriorityLow);
+ minSurplus = (long)
+ (mAllowedTimePerPeriodMs[standbyBucket] * mAllowedTimeSurplusPriorityLow);
} else {
minSurplus = 0;
}
@@ -989,7 +1007,7 @@
}
private long getRemainingExecutionTimeLocked(@NonNull ExecutionStats stats) {
- return Math.min(mAllowedTimePerPeriodMs - stats.executionTimeInWindowMs,
+ return Math.min(stats.allowedTimePerPeriodMs - stats.executionTimeInWindowMs,
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs);
}
@@ -1068,15 +1086,15 @@
if (sessions == null || sessions.size() == 0) {
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
- if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+ if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
return mMaxExecutionTimeMs;
}
- return mAllowedTimePerPeriodMs;
+ return mAllowedTimePerPeriodMs[standbyBucket];
}
final long startWindowElapsed = nowElapsed - stats.windowSizeMs;
final long startMaxElapsed = nowElapsed - MAX_PERIOD_MS;
- final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(jobPriority);
+ final long allowedTimePerPeriodMs = getAllowedTimePerPeriodMs(standbyBucket, jobPriority);
final long allowedTimeRemainingMs = allowedTimePerPeriodMs - stats.executionTimeInWindowMs;
final long maxExecutionTimeRemainingMs =
mMaxExecutionTimeMs - stats.executionTimeInMaxPeriodMs;
@@ -1087,7 +1105,7 @@
// Regular ACTIVE case. Since the bucket size equals the allowed time, the app jobs can
// essentially run until they reach the maximum limit.
- if (stats.windowSizeMs == mAllowedTimePerPeriodMs) {
+ if (stats.windowSizeMs == mAllowedTimePerPeriodMs[standbyBucket]) {
return calculateTimeUntilQuotaConsumedLocked(
sessions, startMaxElapsed, maxExecutionTimeRemainingMs);
}
@@ -1103,14 +1121,19 @@
sessions, startWindowElapsed, allowedTimeRemainingMs));
}
- private long getAllowedTimePerPeriodMs(@JobInfo.Priority int jobPriority) {
+ private long getAllowedTimePerPeriodMs(int standbyBucket, @JobInfo.Priority int jobPriority) {
+ return getAllowedTimePerPeriodMs(mAllowedTimePerPeriodMs[standbyBucket], jobPriority);
+ }
+
+ private long getAllowedTimePerPeriodMs(long initialAllowedTime,
+ @JobInfo.Priority int jobPriority) {
if (jobPriority <= JobInfo.PRIORITY_MIN) {
- return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityMin));
+ return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityMin));
}
if (jobPriority <= JobInfo.PRIORITY_LOW) {
- return (long) (mAllowedTimePerPeriodMs * (1 - mAllowedTimeSurplusPriorityLow));
+ return (long) (initialAllowedTime * (1 - mAllowedTimeSurplusPriorityLow));
}
- return mAllowedTimePerPeriodMs;
+ return initialAllowedTime;
}
/**
@@ -1237,16 +1260,19 @@
appStats[standbyBucket] = stats;
}
if (refreshStatsIfOld) {
+ final long bucketAllowedTimeMs = mAllowedTimePerPeriodMs[standbyBucket];
final long bucketWindowSizeMs = mBucketPeriodsMs[standbyBucket];
final int jobCountLimit = mMaxBucketJobCounts[standbyBucket];
final int sessionCountLimit = mMaxBucketSessionCounts[standbyBucket];
Timer timer = mPkgTimers.get(userId, packageName);
if ((timer != null && timer.isActive())
|| stats.expirationTimeElapsed <= sElapsedRealtimeClock.millis()
+ || stats.allowedTimePerPeriodMs != bucketAllowedTimeMs
|| stats.windowSizeMs != bucketWindowSizeMs
|| stats.jobCountLimit != jobCountLimit
|| stats.sessionCountLimit != sessionCountLimit) {
// The stats are no longer valid.
+ stats.allowedTimePerPeriodMs = bucketAllowedTimeMs;
stats.windowSizeMs = bucketWindowSizeMs;
stats.jobCountLimit = jobCountLimit;
stats.sessionCountLimit = sessionCountLimit;
@@ -1272,8 +1298,11 @@
} else {
stats.inQuotaTimeElapsed = 0;
}
- final long allowedTimeMinMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_MIN);
- final long allowedTimeLowMs = getAllowedTimePerPeriodMs(JobInfo.PRIORITY_LOW);
+ final long allowedTimeMinMs =
+ getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_MIN);
+ final long allowedTimeLowMs =
+ getAllowedTimePerPeriodMs(stats.allowedTimePerPeriodMs, JobInfo.PRIORITY_LOW);
+ final long allowedTimeIntoQuotaMs = stats.allowedTimePerPeriodMs - mQuotaBufferMs;
Timer timer = mPkgTimers.get(userId, packageName);
final long nowElapsed = sElapsedRealtimeClock.millis();
@@ -1287,9 +1316,9 @@
// If the timer is active, the value will be stale at the next method call, so
// invalidate now.
stats.expirationTimeElapsed = nowElapsed;
- if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+ if (stats.executionTimeInWindowMs >= allowedTimeIntoQuotaMs) {
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- nowElapsed - mAllowedTimeIntoQuotaMs + stats.windowSizeMs);
+ nowElapsed - allowedTimeIntoQuotaMs + stats.windowSizeMs);
}
if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
stats.inQuotaTimeLowElapsed = Math.max(stats.inQuotaTimeLowElapsed,
@@ -1346,9 +1375,9 @@
stats.executionTimeInWindowMs += session.endTimeElapsed - start;
stats.bgJobCountInWindow += session.bgJobCount;
- if (stats.executionTimeInWindowMs >= mAllowedTimeIntoQuotaMs) {
+ if (stats.executionTimeInWindowMs >= allowedTimeIntoQuotaMs) {
stats.inQuotaTimeElapsed = Math.max(stats.inQuotaTimeElapsed,
- start + stats.executionTimeInWindowMs - mAllowedTimeIntoQuotaMs
+ start + stats.executionTimeInWindowMs - allowedTimeIntoQuotaMs
+ stats.windowSizeMs);
}
if (stats.executionTimeInWindowMs >= allowedTimeLowMs) {
@@ -1697,7 +1726,7 @@
if (js.setQuotaConstraintSatisfied(nowElapsed, true)) {
changedJobs.add(js);
}
- } else if (realStandbyBucket != ACTIVE_INDEX
+ } else if (realStandbyBucket != EXEMPTED_INDEX && realStandbyBucket != ACTIVE_INDEX
&& realStandbyBucket == js.getEffectiveStandbyBucket()
&& js.getEffectivePriority() >= JobInfo.PRIORITY_DEFAULT) {
// An app in the ACTIVE bucket may be out of quota while the job could be in quota
@@ -1842,7 +1871,8 @@
}
}
final boolean inRegularQuota =
- stats.executionTimeInWindowMs < getAllowedTimePerPeriodMs(minPriority)
+ stats.executionTimeInWindowMs
+ < getAllowedTimePerPeriodMs(standbyBucket, minPriority)
&& stats.executionTimeInMaxPeriodMs < mMaxExecutionTimeMs
&& isUnderJobCountQuota
&& isUnderTimingSessionCountQuota;
@@ -2921,9 +2951,29 @@
/** Prefix to use with all constant keys in order to "sub-namespace" the keys. */
private static final String QC_CONSTANT_PREFIX = "qc_";
+ /**
+ * Previously used keys:
+ * * allowed_time_per_period_ms -- No longer used after splitting by bucket
+ */
+
@VisibleForTesting
- static final String KEY_ALLOWED_TIME_PER_PERIOD_MS =
- QC_CONSTANT_PREFIX + "allowed_time_per_period_ms";
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_exempted_ms";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_active_ms";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_working_ms";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_frequent_ms";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_rare_ms";
+ @VisibleForTesting
+ static final String KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+ QC_CONSTANT_PREFIX + "allowed_time_per_period_restricted_ms";
@VisibleForTesting
static final String KEY_IN_QUOTA_BUFFER_MS =
QC_CONSTANT_PREFIX + "in_quota_buffer_ms";
@@ -2934,6 +2984,9 @@
static final String KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN =
QC_CONSTANT_PREFIX + "allowed_time_surplus_priority_min";
@VisibleForTesting
+ static final String KEY_WINDOW_SIZE_EXEMPTED_MS =
+ QC_CONSTANT_PREFIX + "window_size_exempted_ms";
+ @VisibleForTesting
static final String KEY_WINDOW_SIZE_ACTIVE_MS =
QC_CONSTANT_PREFIX + "window_size_active_ms";
@VisibleForTesting
@@ -2952,6 +3005,9 @@
static final String KEY_MAX_EXECUTION_TIME_MS =
QC_CONSTANT_PREFIX + "max_execution_time_ms";
@VisibleForTesting
+ static final String KEY_MAX_JOB_COUNT_EXEMPTED =
+ QC_CONSTANT_PREFIX + "max_job_count_exempted";
+ @VisibleForTesting
static final String KEY_MAX_JOB_COUNT_ACTIVE =
QC_CONSTANT_PREFIX + "max_job_count_active";
@VisibleForTesting
@@ -2973,6 +3029,9 @@
static final String KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW =
QC_CONSTANT_PREFIX + "max_job_count_per_rate_limiting_window";
@VisibleForTesting
+ static final String KEY_MAX_SESSION_COUNT_EXEMPTED =
+ QC_CONSTANT_PREFIX + "max_session_count_exempted";
+ @VisibleForTesting
static final String KEY_MAX_SESSION_COUNT_ACTIVE =
QC_CONSTANT_PREFIX + "max_session_count_active";
@VisibleForTesting
@@ -2997,6 +3056,9 @@
static final String KEY_MIN_QUOTA_CHECK_DELAY_MS =
QC_CONSTANT_PREFIX + "min_quota_check_delay_ms";
@VisibleForTesting
+ static final String KEY_EJ_LIMIT_EXEMPTED_MS =
+ QC_CONSTANT_PREFIX + "ej_limit_exempted_ms";
+ @VisibleForTesting
static final String KEY_EJ_LIMIT_ACTIVE_MS =
QC_CONSTANT_PREFIX + "ej_limit_active_ms";
@VisibleForTesting
@@ -3039,14 +3101,26 @@
static final String KEY_EJ_GRACE_PERIOD_TOP_APP_MS =
QC_CONSTANT_PREFIX + "ej_grace_period_top_app_ms";
- private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_MS =
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS =
+ 10 * 60 * 1000L; // 10 minutes
+ private static final long DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
10 * 60 * 1000L; // 10 minutes
private static final long DEFAULT_IN_QUOTA_BUFFER_MS =
30 * 1000L; // 30 seconds
private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW = .25f;
private static final float DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_MIN = .5f;
+ private static final long DEFAULT_WINDOW_SIZE_EXEMPTED_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS; // EXEMPT apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_ACTIVE_MS =
- DEFAULT_ALLOWED_TIME_PER_PERIOD_MS; // ACTIVE apps can run jobs at any time
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS; // ACTIVE apps can run jobs at any time
private static final long DEFAULT_WINDOW_SIZE_WORKING_MS =
2 * 60 * 60 * 1000L; // 2 hours
private static final long DEFAULT_WINDOW_SIZE_FREQUENT_MS =
@@ -3060,8 +3134,9 @@
private static final long DEFAULT_RATE_LIMITING_WINDOW_MS =
MINUTE_IN_MILLIS;
private static final int DEFAULT_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW = 20;
- private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE =
+ private static final int DEFAULT_MAX_JOB_COUNT_EXEMPTED =
75; // 75/window = 450/hr = 1/session
+ private static final int DEFAULT_MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
private static final int DEFAULT_MAX_JOB_COUNT_WORKING = // 120/window = 60/hr = 12/session
(int) (60.0 * DEFAULT_WINDOW_SIZE_WORKING_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_FREQUENT = // 200/window = 25/hr = 25/session
@@ -3069,8 +3144,10 @@
private static final int DEFAULT_MAX_JOB_COUNT_RARE = // 48/window = 2/hr = 16/session
(int) (2.0 * DEFAULT_WINDOW_SIZE_RARE_MS / HOUR_IN_MILLIS);
private static final int DEFAULT_MAX_JOB_COUNT_RESTRICTED = 10;
- private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
+ private static final int DEFAULT_MAX_SESSION_COUNT_EXEMPTED =
75; // 450/hr
+ private static final int DEFAULT_MAX_SESSION_COUNT_ACTIVE =
+ DEFAULT_MAX_SESSION_COUNT_EXEMPTED;
private static final int DEFAULT_MAX_SESSION_COUNT_WORKING =
10; // 5/hr
private static final int DEFAULT_MAX_SESSION_COUNT_FREQUENT =
@@ -3081,6 +3158,7 @@
private static final int DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW = 20;
private static final long DEFAULT_TIMING_SESSION_COALESCING_DURATION_MS = 5000; // 5 seconds
private static final long DEFAULT_MIN_QUOTA_CHECK_DELAY_MS = MINUTE_IN_MILLIS;
+ private static final long DEFAULT_EJ_LIMIT_EXEMPTED_MS = 45 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_ACTIVE_MS = 30 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_LIMIT_WORKING_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
private static final long DEFAULT_EJ_LIMIT_FREQUENT_MS = 10 * MINUTE_IN_MILLIS;
@@ -3096,8 +3174,39 @@
private static final long DEFAULT_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS = 3 * MINUTE_IN_MILLIS;
private static final long DEFAULT_EJ_GRACE_PERIOD_TOP_APP_MS = 1 * MINUTE_IN_MILLIS;
- /** How much time each app will have to run jobs within their standby bucket window. */
- public long ALLOWED_TIME_PER_PERIOD_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_MS;
+ /**
+ * How much time each app in the exempted bucket will have to run jobs within their standby
+ * bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS;
+ /**
+ * How much time each app in the active bucket will have to run jobs within their standby
+ * bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_ACTIVE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS;
+ /**
+ * How much time each app in the working set bucket will have to run jobs within their
+ * standby bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_WORKING_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS;
+ /**
+ * How much time each app in the frequent bucket will have to run jobs within their standby
+ * bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS;
+ /**
+ * How much time each app in the rare bucket will have to run jobs within their standby
+ * bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_RARE_MS = DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS;
+ /**
+ * How much time each app in the restricted bucket will have to run jobs within their
+ * standby bucket window.
+ */
+ public long ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS;
/**
* How much time the package should have before transitioning from out-of-quota to in-quota.
@@ -3106,7 +3215,7 @@
public long IN_QUOTA_BUFFER_MS = DEFAULT_IN_QUOTA_BUFFER_MS;
/**
- * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+ * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
* {@link JobInfo#PRIORITY_LOW low priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running low priority
* jobs.
@@ -3114,7 +3223,7 @@
public float ALLOWED_TIME_SURPLUS_PRIORITY_LOW = DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW;
/**
- * The percentage of {@link #ALLOWED_TIME_PER_PERIOD_MS} that should not be used by
+ * The percentage of ALLOWED_TIME_PER_PERIOD_*_MS that should not be used by
* {@link JobInfo#PRIORITY_MIN low priority} jobs. In other words, there must be a minimum
* surplus of this amount of remaining allowed time before we start running min priority
* jobs.
@@ -3123,35 +3232,42 @@
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS} within the past
+ * WINDOW_SIZE_MS.
+ */
+ public long WINDOW_SIZE_EXEMPTED_MS = DEFAULT_WINDOW_SIZE_EXEMPTED_MS;
+
+ /**
+ * The quota window size of the particular standby bucket. Apps in this standby bucket are
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_ACTIVE_MS} within the past
* WINDOW_SIZE_MS.
*/
public long WINDOW_SIZE_ACTIVE_MS = DEFAULT_WINDOW_SIZE_ACTIVE_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_WORKING_MS} within the past
* WINDOW_SIZE_MS.
*/
public long WINDOW_SIZE_WORKING_MS = DEFAULT_WINDOW_SIZE_WORKING_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_FREQUENT_MS} within the past
* WINDOW_SIZE_MS.
*/
public long WINDOW_SIZE_FREQUENT_MS = DEFAULT_WINDOW_SIZE_FREQUENT_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_RARE_MS} within the past
* WINDOW_SIZE_MS.
*/
public long WINDOW_SIZE_RARE_MS = DEFAULT_WINDOW_SIZE_RARE_MS;
/**
* The quota window size of the particular standby bucket. Apps in this standby bucket are
- * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_MS} within the past
+ * expected to run only {@link #ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS} within the past
* WINDOW_SIZE_MS.
*/
public long WINDOW_SIZE_RESTRICTED_MS = DEFAULT_WINDOW_SIZE_RESTRICTED_MS;
@@ -3165,6 +3281,12 @@
* The maximum number of jobs an app can run within this particular standby bucket's
* window size.
*/
+ public int MAX_JOB_COUNT_EXEMPTED = DEFAULT_MAX_JOB_COUNT_EXEMPTED;
+
+ /**
+ * The maximum number of jobs an app can run within this particular standby bucket's
+ * window size.
+ */
public int MAX_JOB_COUNT_ACTIVE = DEFAULT_MAX_JOB_COUNT_ACTIVE;
/**
@@ -3204,6 +3326,12 @@
* The maximum number of {@link TimingSession TimingSessions} an app can run within this
* particular standby bucket's window size.
*/
+ public int MAX_SESSION_COUNT_EXEMPTED = DEFAULT_MAX_SESSION_COUNT_EXEMPTED;
+
+ /**
+ * The maximum number of {@link TimingSession TimingSessions} an app can run within this
+ * particular standby bucket's window size.
+ */
public int MAX_SESSION_COUNT_ACTIVE = DEFAULT_MAX_SESSION_COUNT_ACTIVE;
/**
@@ -3232,7 +3360,7 @@
/**
* The maximum number of {@link TimingSession TimingSessions} that can run within the past
- * {@link #ALLOWED_TIME_PER_PERIOD_MS}.
+ * {@link #RATE_LIMITING_WINDOW_MS}.
*/
public int MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW =
DEFAULT_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW;
@@ -3275,6 +3403,13 @@
* standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
* in any rewards or free EJs).
*/
+ public long EJ_LIMIT_EXEMPTED_MS = DEFAULT_EJ_LIMIT_EXEMPTED_MS;
+
+ /**
+ * The total expedited job session limit of the particular standby bucket. Apps in this
+ * standby bucket can only have expedited job sessions totalling EJ_LIMIT (without factoring
+ * in any rewards or free EJs).
+ */
public long EJ_LIMIT_ACTIVE_MS = DEFAULT_EJ_LIMIT_ACTIVE_MS;
/**
@@ -3358,7 +3493,12 @@
public void processConstantLocked(@NonNull DeviceConfig.Properties properties,
@NonNull String key) {
switch (key) {
- case KEY_ALLOWED_TIME_PER_PERIOD_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS:
+ case KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS:
case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW:
case KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN:
case KEY_IN_QUOTA_BUFFER_MS:
@@ -3388,6 +3528,15 @@
updateEJLimitConstantsLocked();
break;
+ case KEY_MAX_JOB_COUNT_EXEMPTED:
+ MAX_JOB_COUNT_EXEMPTED = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_EXEMPTED);
+ int newExemptedMaxJobCount =
+ Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_EXEMPTED);
+ if (mMaxBucketJobCounts[EXEMPTED_INDEX] != newExemptedMaxJobCount) {
+ mMaxBucketJobCounts[EXEMPTED_INDEX] = newExemptedMaxJobCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_MAX_JOB_COUNT_ACTIVE:
MAX_JOB_COUNT_ACTIVE = properties.getInt(key, DEFAULT_MAX_JOB_COUNT_ACTIVE);
int newActiveMaxJobCount = Math.max(MIN_BUCKET_JOB_COUNT, MAX_JOB_COUNT_ACTIVE);
@@ -3432,6 +3581,16 @@
mShouldReevaluateConstraints = true;
}
break;
+ case KEY_MAX_SESSION_COUNT_EXEMPTED:
+ MAX_SESSION_COUNT_EXEMPTED =
+ properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_EXEMPTED);
+ int newExemptedMaxSessionCount =
+ Math.max(MIN_BUCKET_SESSION_COUNT, MAX_SESSION_COUNT_EXEMPTED);
+ if (mMaxBucketSessionCounts[EXEMPTED_INDEX] != newExemptedMaxSessionCount) {
+ mMaxBucketSessionCounts[EXEMPTED_INDEX] = newExemptedMaxSessionCount;
+ mShouldReevaluateConstraints = true;
+ }
+ break;
case KEY_MAX_SESSION_COUNT_ACTIVE:
MAX_SESSION_COUNT_ACTIVE =
properties.getInt(key, DEFAULT_MAX_SESSION_COUNT_ACTIVE);
@@ -3579,15 +3738,34 @@
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_ALLOWED_TIME_PER_PERIOD_MS, KEY_IN_QUOTA_BUFFER_MS,
+ KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ KEY_IN_QUOTA_BUFFER_MS,
KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN,
- KEY_MAX_EXECUTION_TIME_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
+ KEY_MAX_EXECUTION_TIME_MS,
+ KEY_WINDOW_SIZE_EXEMPTED_MS, KEY_WINDOW_SIZE_ACTIVE_MS,
KEY_WINDOW_SIZE_WORKING_MS,
KEY_WINDOW_SIZE_FREQUENT_MS, KEY_WINDOW_SIZE_RARE_MS,
KEY_WINDOW_SIZE_RESTRICTED_MS);
- ALLOWED_TIME_PER_PERIOD_MS =
- properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_MS,
- DEFAULT_ALLOWED_TIME_PER_PERIOD_MS);
+ ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS);
+ ALLOWED_TIME_PER_PERIOD_ACTIVE_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS);
+ ALLOWED_TIME_PER_PERIOD_WORKING_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_WORKING_MS);
+ ALLOWED_TIME_PER_PERIOD_FREQUENT_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS);
+ ALLOWED_TIME_PER_PERIOD_RARE_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_RARE_MS);
+ ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS =
+ properties.getLong(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ DEFAULT_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS);
ALLOWED_TIME_SURPLUS_PRIORITY_LOW =
properties.getFloat(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW,
DEFAULT_ALLOWED_TIME_SURPLUS_PRIORITY_LOW);
@@ -3598,6 +3776,8 @@
DEFAULT_IN_QUOTA_BUFFER_MS);
MAX_EXECUTION_TIME_MS = properties.getLong(KEY_MAX_EXECUTION_TIME_MS,
DEFAULT_MAX_EXECUTION_TIME_MS);
+ WINDOW_SIZE_EXEMPTED_MS = properties.getLong(KEY_WINDOW_SIZE_EXEMPTED_MS,
+ DEFAULT_WINDOW_SIZE_EXEMPTED_MS);
WINDOW_SIZE_ACTIVE_MS = properties.getLong(KEY_WINDOW_SIZE_ACTIVE_MS,
DEFAULT_WINDOW_SIZE_ACTIVE_MS);
WINDOW_SIZE_WORKING_MS =
@@ -3618,20 +3798,55 @@
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
- long newAllowedTimeMs = Math.min(mMaxExecutionTimeMs,
- Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_MS));
- if (mAllowedTimePerPeriodMs != newAllowedTimeMs) {
- mAllowedTimePerPeriodMs = newAllowedTimeMs;
- mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
+ long minAllowedTimeMs = Long.MAX_VALUE;
+ long newAllowedTimeExemptedMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeExemptedMs);
+ if (mAllowedTimePerPeriodMs[EXEMPTED_INDEX] != newAllowedTimeExemptedMs) {
+ mAllowedTimePerPeriodMs[EXEMPTED_INDEX] = newAllowedTimeExemptedMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeActiveMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeActiveMs);
+ if (mAllowedTimePerPeriodMs[ACTIVE_INDEX] != newAllowedTimeActiveMs) {
+ mAllowedTimePerPeriodMs[ACTIVE_INDEX] = newAllowedTimeActiveMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeWorkingMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_WORKING_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeWorkingMs);
+ if (mAllowedTimePerPeriodMs[WORKING_INDEX] != newAllowedTimeWorkingMs) {
+ mAllowedTimePerPeriodMs[WORKING_INDEX] = newAllowedTimeWorkingMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeFrequentMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_FREQUENT_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeFrequentMs);
+ if (mAllowedTimePerPeriodMs[FREQUENT_INDEX] != newAllowedTimeFrequentMs) {
+ mAllowedTimePerPeriodMs[FREQUENT_INDEX] = newAllowedTimeFrequentMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeRareMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_RARE_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeRareMs);
+ if (mAllowedTimePerPeriodMs[RARE_INDEX] != newAllowedTimeRareMs) {
+ mAllowedTimePerPeriodMs[RARE_INDEX] = newAllowedTimeRareMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newAllowedTimeRestrictedMs = Math.min(mMaxExecutionTimeMs,
+ Math.max(MINUTE_IN_MILLIS, ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS));
+ minAllowedTimeMs = Math.min(minAllowedTimeMs, newAllowedTimeRestrictedMs);
+ if (mAllowedTimePerPeriodMs[RESTRICTED_INDEX] != newAllowedTimeRestrictedMs) {
+ mAllowedTimePerPeriodMs[RESTRICTED_INDEX] = newAllowedTimeRestrictedMs;
mShouldReevaluateConstraints = true;
}
// Make sure quota buffer is non-negative, not greater than allowed time per period,
// and no more than 5 minutes.
- long newQuotaBufferMs = Math.max(0, Math.min(mAllowedTimePerPeriodMs,
+ long newQuotaBufferMs = Math.max(0, Math.min(minAllowedTimeMs,
Math.min(5 * MINUTE_IN_MILLIS, IN_QUOTA_BUFFER_MS)));
if (mQuotaBufferMs != newQuotaBufferMs) {
mQuotaBufferMs = newQuotaBufferMs;
- mAllowedTimeIntoQuotaMs = mAllowedTimePerPeriodMs - mQuotaBufferMs;
mMaxExecutionTimeIntoQuotaMs = mMaxExecutionTimeMs - mQuotaBufferMs;
mShouldReevaluateConstraints = true;
}
@@ -3652,32 +3867,38 @@
mAllowedTimeSurplusPriorityMin = newAllowedTimeSurplusPriorityMin;
mShouldReevaluateConstraints = true;
}
- long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ long newExemptedPeriodMs = Math.max(mAllowedTimePerPeriodMs[EXEMPTED_INDEX],
+ Math.min(MAX_PERIOD_MS, WINDOW_SIZE_EXEMPTED_MS));
+ if (mBucketPeriodsMs[EXEMPTED_INDEX] != newExemptedPeriodMs) {
+ mBucketPeriodsMs[EXEMPTED_INDEX] = newExemptedPeriodMs;
+ mShouldReevaluateConstraints = true;
+ }
+ long newActivePeriodMs = Math.max(mAllowedTimePerPeriodMs[ACTIVE_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_ACTIVE_MS));
if (mBucketPeriodsMs[ACTIVE_INDEX] != newActivePeriodMs) {
mBucketPeriodsMs[ACTIVE_INDEX] = newActivePeriodMs;
mShouldReevaluateConstraints = true;
}
- long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ long newWorkingPeriodMs = Math.max(mAllowedTimePerPeriodMs[WORKING_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_WORKING_MS));
if (mBucketPeriodsMs[WORKING_INDEX] != newWorkingPeriodMs) {
mBucketPeriodsMs[WORKING_INDEX] = newWorkingPeriodMs;
mShouldReevaluateConstraints = true;
}
- long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ long newFrequentPeriodMs = Math.max(mAllowedTimePerPeriodMs[FREQUENT_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_FREQUENT_MS));
if (mBucketPeriodsMs[FREQUENT_INDEX] != newFrequentPeriodMs) {
mBucketPeriodsMs[FREQUENT_INDEX] = newFrequentPeriodMs;
mShouldReevaluateConstraints = true;
}
- long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ long newRarePeriodMs = Math.max(mAllowedTimePerPeriodMs[RARE_INDEX],
Math.min(MAX_PERIOD_MS, WINDOW_SIZE_RARE_MS));
if (mBucketPeriodsMs[RARE_INDEX] != newRarePeriodMs) {
mBucketPeriodsMs[RARE_INDEX] = newRarePeriodMs;
mShouldReevaluateConstraints = true;
}
// Fit in the range [allowed time (10 mins), 1 week].
- long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs,
+ long newRestrictedPeriodMs = Math.max(mAllowedTimePerPeriodMs[RESTRICTED_INDEX],
Math.min(7 * 24 * 60 * MINUTE_IN_MILLIS, WINDOW_SIZE_RESTRICTED_MS));
if (mBucketPeriodsMs[RESTRICTED_INDEX] != newRestrictedPeriodMs) {
mBucketPeriodsMs[RESTRICTED_INDEX] = newRestrictedPeriodMs;
@@ -3740,11 +3961,14 @@
// Query the values as an atomic set.
final DeviceConfig.Properties properties = DeviceConfig.getProperties(
DeviceConfig.NAMESPACE_JOB_SCHEDULER,
+ KEY_EJ_LIMIT_EXEMPTED_MS,
KEY_EJ_LIMIT_ACTIVE_MS, KEY_EJ_LIMIT_WORKING_MS,
KEY_EJ_LIMIT_FREQUENT_MS, KEY_EJ_LIMIT_RARE_MS,
KEY_EJ_LIMIT_RESTRICTED_MS, KEY_EJ_LIMIT_ADDITION_SPECIAL_MS,
KEY_EJ_LIMIT_ADDITION_INSTALLER_MS,
KEY_EJ_WINDOW_SIZE_MS);
+ EJ_LIMIT_EXEMPTED_MS = properties.getLong(
+ KEY_EJ_LIMIT_EXEMPTED_MS, DEFAULT_EJ_LIMIT_EXEMPTED_MS);
EJ_LIMIT_ACTIVE_MS = properties.getLong(
KEY_EJ_LIMIT_ACTIVE_MS, DEFAULT_EJ_LIMIT_ACTIVE_MS);
EJ_LIMIT_WORKING_MS = properties.getLong(
@@ -3770,8 +3994,15 @@
mShouldReevaluateConstraints = true;
}
// The limit must be in the range [15 minutes, window size].
+ long newExemptLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
+ Math.min(newWindowSizeMs, EJ_LIMIT_EXEMPTED_MS));
+ if (mEJLimitsMs[EXEMPTED_INDEX] != newExemptLimitMs) {
+ mEJLimitsMs[EXEMPTED_INDEX] = newExemptLimitMs;
+ mShouldReevaluateConstraints = true;
+ }
+ // The limit must be in the range [15 minutes, exempted limit].
long newActiveLimitMs = Math.max(15 * MINUTE_IN_MILLIS,
- Math.min(newWindowSizeMs, EJ_LIMIT_ACTIVE_MS));
+ Math.min(newExemptLimitMs, EJ_LIMIT_ACTIVE_MS));
if (mEJLimitsMs[ACTIVE_INDEX] != newActiveLimitMs) {
mEJLimitsMs[ACTIVE_INDEX] = newActiveLimitMs;
mShouldReevaluateConstraints = true;
@@ -3823,18 +4054,31 @@
pw.println();
pw.println("QuotaController:");
pw.increaseIndent();
- pw.print(KEY_ALLOWED_TIME_PER_PERIOD_MS, ALLOWED_TIME_PER_PERIOD_MS).println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, ALLOWED_TIME_PER_PERIOD_ACTIVE_MS)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, ALLOWED_TIME_PER_PERIOD_WORKING_MS)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, ALLOWED_TIME_PER_PERIOD_FREQUENT_MS)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, ALLOWED_TIME_PER_PERIOD_RARE_MS)
+ .println();
+ pw.print(KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS).println();
pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, ALLOWED_TIME_SURPLUS_PRIORITY_LOW)
.println();
pw.print(KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, ALLOWED_TIME_SURPLUS_PRIORITY_MIN)
.println();
pw.print(KEY_IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS).println();
+ pw.print(KEY_WINDOW_SIZE_EXEMPTED_MS, WINDOW_SIZE_EXEMPTED_MS).println();
pw.print(KEY_WINDOW_SIZE_ACTIVE_MS, WINDOW_SIZE_ACTIVE_MS).println();
pw.print(KEY_WINDOW_SIZE_WORKING_MS, WINDOW_SIZE_WORKING_MS).println();
pw.print(KEY_WINDOW_SIZE_FREQUENT_MS, WINDOW_SIZE_FREQUENT_MS).println();
pw.print(KEY_WINDOW_SIZE_RARE_MS, WINDOW_SIZE_RARE_MS).println();
pw.print(KEY_WINDOW_SIZE_RESTRICTED_MS, WINDOW_SIZE_RESTRICTED_MS).println();
pw.print(KEY_MAX_EXECUTION_TIME_MS, MAX_EXECUTION_TIME_MS).println();
+ pw.print(KEY_MAX_JOB_COUNT_EXEMPTED, MAX_JOB_COUNT_EXEMPTED).println();
pw.print(KEY_MAX_JOB_COUNT_ACTIVE, MAX_JOB_COUNT_ACTIVE).println();
pw.print(KEY_MAX_JOB_COUNT_WORKING, MAX_JOB_COUNT_WORKING).println();
pw.print(KEY_MAX_JOB_COUNT_FREQUENT, MAX_JOB_COUNT_FREQUENT).println();
@@ -3843,6 +4087,7 @@
pw.print(KEY_RATE_LIMITING_WINDOW_MS, RATE_LIMITING_WINDOW_MS).println();
pw.print(KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW,
MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW).println();
+ pw.print(KEY_MAX_SESSION_COUNT_EXEMPTED, MAX_SESSION_COUNT_EXEMPTED).println();
pw.print(KEY_MAX_SESSION_COUNT_ACTIVE, MAX_SESSION_COUNT_ACTIVE).println();
pw.print(KEY_MAX_SESSION_COUNT_WORKING, MAX_SESSION_COUNT_WORKING).println();
pw.print(KEY_MAX_SESSION_COUNT_FREQUENT, MAX_SESSION_COUNT_FREQUENT).println();
@@ -3854,6 +4099,7 @@
TIMING_SESSION_COALESCING_DURATION_MS).println();
pw.print(KEY_MIN_QUOTA_CHECK_DELAY_MS, MIN_QUOTA_CHECK_DELAY_MS).println();
+ pw.print(KEY_EJ_LIMIT_EXEMPTED_MS, EJ_LIMIT_EXEMPTED_MS).println();
pw.print(KEY_EJ_LIMIT_ACTIVE_MS, EJ_LIMIT_ACTIVE_MS).println();
pw.print(KEY_EJ_LIMIT_WORKING_MS, EJ_LIMIT_WORKING_MS).println();
pw.print(KEY_EJ_LIMIT_FREQUENT_MS, EJ_LIMIT_FREQUENT_MS).println();
@@ -3875,8 +4121,6 @@
private void dump(ProtoOutputStream proto) {
final long qcToken = proto.start(ConstantsProto.QUOTA_CONTROLLER);
- proto.write(ConstantsProto.QuotaController.ALLOWED_TIME_PER_PERIOD_MS,
- ALLOWED_TIME_PER_PERIOD_MS);
proto.write(ConstantsProto.QuotaController.IN_QUOTA_BUFFER_MS, IN_QUOTA_BUFFER_MS);
proto.write(ConstantsProto.QuotaController.ACTIVE_WINDOW_SIZE_MS,
WINDOW_SIZE_ACTIVE_MS);
@@ -3946,7 +4190,7 @@
//////////////////////// TESTING HELPERS /////////////////////////////
@VisibleForTesting
- long getAllowedTimePerPeriodMs() {
+ long[] getAllowedTimePerPeriodMs() {
return mAllowedTimePerPeriodMs;
}
diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt
index 9153426..50e0a1b 100644
--- a/boot/hiddenapi/hiddenapi-max-target-o.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-o.txt
@@ -35446,51 +35446,6 @@
Landroid/net/IIpConnectivityMetrics;->addNetdEventCallback(ILandroid/net/INetdEventCallback;)Z
Landroid/net/IIpConnectivityMetrics;->logEvent(Landroid/net/ConnectivityMetricsEvent;)I
Landroid/net/IIpConnectivityMetrics;->removeNetdEventCallback(I)Z
-Landroid/net/IIpSecService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/net/IIpSecService$Stub$Proxy;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V
-Landroid/net/IIpSecService$Stub$Proxy;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse;
-Landroid/net/IIpSecService$Stub$Proxy;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V
-Landroid/net/IIpSecService$Stub$Proxy;->applyTunnelModeTransform(IIILjava/lang/String;)V
-Landroid/net/IIpSecService$Stub$Proxy;->closeUdpEncapsulationSocket(I)V
-Landroid/net/IIpSecService$Stub$Proxy;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse;
-Landroid/net/IIpSecService$Stub$Proxy;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse;
-Landroid/net/IIpSecService$Stub$Proxy;->deleteTransform(I)V
-Landroid/net/IIpSecService$Stub$Proxy;->deleteTunnelInterface(ILjava/lang/String;)V
-Landroid/net/IIpSecService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
-Landroid/net/IIpSecService$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/net/IIpSecService$Stub$Proxy;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse;
-Landroid/net/IIpSecService$Stub$Proxy;->releaseSecurityParameterIndex(I)V
-Landroid/net/IIpSecService$Stub$Proxy;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V
-Landroid/net/IIpSecService$Stub$Proxy;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V
-Landroid/net/IIpSecService$Stub;-><init>()V
-Landroid/net/IIpSecService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/IIpSecService;
-Landroid/net/IIpSecService$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/net/IIpSecService$Stub;->TRANSACTION_addAddressToTunnelInterface:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_allocateSecurityParameterIndex:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTransportModeTransform:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_applyTunnelModeTransform:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_closeUdpEncapsulationSocket:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_createTransform:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_createTunnelInterface:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTransform:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_deleteTunnelInterface:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_openUdpEncapsulationSocket:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_releaseSecurityParameterIndex:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_removeAddressFromTunnelInterface:I
-Landroid/net/IIpSecService$Stub;->TRANSACTION_removeTransportModeTransforms:I
-Landroid/net/IIpSecService;->addAddressToTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V
-Landroid/net/IIpSecService;->allocateSecurityParameterIndex(Ljava/lang/String;ILandroid/os/IBinder;)Landroid/net/IpSecSpiResponse;
-Landroid/net/IIpSecService;->applyTransportModeTransform(Landroid/os/ParcelFileDescriptor;II)V
-Landroid/net/IIpSecService;->applyTunnelModeTransform(IIILjava/lang/String;)V
-Landroid/net/IIpSecService;->closeUdpEncapsulationSocket(I)V
-Landroid/net/IIpSecService;->createTransform(Landroid/net/IpSecConfig;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTransformResponse;
-Landroid/net/IIpSecService;->createTunnelInterface(Ljava/lang/String;Ljava/lang/String;Landroid/net/Network;Landroid/os/IBinder;Ljava/lang/String;)Landroid/net/IpSecTunnelInterfaceResponse;
-Landroid/net/IIpSecService;->deleteTransform(I)V
-Landroid/net/IIpSecService;->deleteTunnelInterface(ILjava/lang/String;)V
-Landroid/net/IIpSecService;->openUdpEncapsulationSocket(ILandroid/os/IBinder;)Landroid/net/IpSecUdpEncapResponse;
-Landroid/net/IIpSecService;->releaseSecurityParameterIndex(I)V
-Landroid/net/IIpSecService;->removeAddressFromTunnelInterface(ILandroid/net/LinkAddress;Ljava/lang/String;)V
-Landroid/net/IIpSecService;->removeTransportModeTransforms(Landroid/os/ParcelFileDescriptor;)V
Landroid/net/INetd$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/INetd$Stub$Proxy;->addVirtualTunnelInterface(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;II)V
Landroid/net/INetd$Stub$Proxy;->bandwidthEnableDataSaver(Z)Z
@@ -35914,174 +35869,6 @@
Landroid/net/InterfaceConfiguration;->mHwAddr:Ljava/lang/String;
Landroid/net/InterfaceConfiguration;->setHardwareAddress(Ljava/lang/String;)V
Landroid/net/InterfaceConfiguration;->validateFlag(Ljava/lang/String;)V
-Landroid/net/IpSecAlgorithm;->checkValidOrThrow(Ljava/lang/String;II)V
-Landroid/net/IpSecAlgorithm;->CRYPT_NULL:Ljava/lang/String;
-Landroid/net/IpSecAlgorithm;->equals(Landroid/net/IpSecAlgorithm;Landroid/net/IpSecAlgorithm;)Z
-Landroid/net/IpSecAlgorithm;->isAead()Z
-Landroid/net/IpSecAlgorithm;->isAuthentication()Z
-Landroid/net/IpSecAlgorithm;->isEncryption()Z
-Landroid/net/IpSecAlgorithm;->isUnsafeBuild()Z
-Landroid/net/IpSecAlgorithm;->mKey:[B
-Landroid/net/IpSecAlgorithm;->mName:Ljava/lang/String;
-Landroid/net/IpSecAlgorithm;->mTruncLenBits:I
-Landroid/net/IpSecAlgorithm;->TAG:Ljava/lang/String;
-Landroid/net/IpSecConfig;-><init>()V
-Landroid/net/IpSecConfig;-><init>(Landroid/net/IpSecConfig;)V
-Landroid/net/IpSecConfig;-><init>(Landroid/os/Parcel;)V
-Landroid/net/IpSecConfig;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/IpSecConfig;->equals(Landroid/net/IpSecConfig;Landroid/net/IpSecConfig;)Z
-Landroid/net/IpSecConfig;->getAuthenticatedEncryption()Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->getAuthentication()Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->getDestinationAddress()Ljava/lang/String;
-Landroid/net/IpSecConfig;->getEncapRemotePort()I
-Landroid/net/IpSecConfig;->getEncapSocketResourceId()I
-Landroid/net/IpSecConfig;->getEncapType()I
-Landroid/net/IpSecConfig;->getEncryption()Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->getMarkMask()I
-Landroid/net/IpSecConfig;->getMarkValue()I
-Landroid/net/IpSecConfig;->getMode()I
-Landroid/net/IpSecConfig;->getNattKeepaliveInterval()I
-Landroid/net/IpSecConfig;->getNetwork()Landroid/net/Network;
-Landroid/net/IpSecConfig;->getSourceAddress()Ljava/lang/String;
-Landroid/net/IpSecConfig;->getSpiResourceId()I
-Landroid/net/IpSecConfig;->mAuthenticatedEncryption:Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->mAuthentication:Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->mDestinationAddress:Ljava/lang/String;
-Landroid/net/IpSecConfig;->mEncapRemotePort:I
-Landroid/net/IpSecConfig;->mEncapSocketResourceId:I
-Landroid/net/IpSecConfig;->mEncapType:I
-Landroid/net/IpSecConfig;->mEncryption:Landroid/net/IpSecAlgorithm;
-Landroid/net/IpSecConfig;->mMarkMask:I
-Landroid/net/IpSecConfig;->mMarkValue:I
-Landroid/net/IpSecConfig;->mMode:I
-Landroid/net/IpSecConfig;->mNattKeepaliveInterval:I
-Landroid/net/IpSecConfig;->mNetwork:Landroid/net/Network;
-Landroid/net/IpSecConfig;->mSourceAddress:Ljava/lang/String;
-Landroid/net/IpSecConfig;->mSpiResourceId:I
-Landroid/net/IpSecConfig;->setAuthenticatedEncryption(Landroid/net/IpSecAlgorithm;)V
-Landroid/net/IpSecConfig;->setAuthentication(Landroid/net/IpSecAlgorithm;)V
-Landroid/net/IpSecConfig;->setDestinationAddress(Ljava/lang/String;)V
-Landroid/net/IpSecConfig;->setEncapRemotePort(I)V
-Landroid/net/IpSecConfig;->setEncapSocketResourceId(I)V
-Landroid/net/IpSecConfig;->setEncapType(I)V
-Landroid/net/IpSecConfig;->setEncryption(Landroid/net/IpSecAlgorithm;)V
-Landroid/net/IpSecConfig;->setMarkMask(I)V
-Landroid/net/IpSecConfig;->setMarkValue(I)V
-Landroid/net/IpSecConfig;->setMode(I)V
-Landroid/net/IpSecConfig;->setNattKeepaliveInterval(I)V
-Landroid/net/IpSecConfig;->setNetwork(Landroid/net/Network;)V
-Landroid/net/IpSecConfig;->setSourceAddress(Ljava/lang/String;)V
-Landroid/net/IpSecConfig;->setSpiResourceId(I)V
-Landroid/net/IpSecConfig;->TAG:Ljava/lang/String;
-Landroid/net/IpSecManager$IpSecTunnelInterface;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)V
-Landroid/net/IpSecManager$IpSecTunnelInterface;->addAddress(Ljava/net/InetAddress;I)V
-Landroid/net/IpSecManager$IpSecTunnelInterface;->getInterfaceName()Ljava/lang/String;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->getResourceId()I
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mCloseGuard:Ldalvik/system/CloseGuard;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mInterfaceName:Ljava/lang/String;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mLocalAddress:Ljava/net/InetAddress;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mOpPackageName:Ljava/lang/String;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mRemoteAddress:Ljava/net/InetAddress;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mResourceId:I
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mService:Landroid/net/IIpSecService;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->mUnderlyingNetwork:Landroid/net/Network;
-Landroid/net/IpSecManager$IpSecTunnelInterface;->removeAddress(Ljava/net/InetAddress;I)V
-Landroid/net/IpSecManager$ResourceUnavailableException;-><init>(Ljava/lang/String;)V
-Landroid/net/IpSecManager$SecurityParameterIndex;-><init>(Landroid/net/IIpSecService;Ljava/net/InetAddress;I)V
-Landroid/net/IpSecManager$SecurityParameterIndex;->getResourceId()I
-Landroid/net/IpSecManager$SecurityParameterIndex;->mCloseGuard:Ldalvik/system/CloseGuard;
-Landroid/net/IpSecManager$SecurityParameterIndex;->mDestinationAddress:Ljava/net/InetAddress;
-Landroid/net/IpSecManager$SecurityParameterIndex;->mResourceId:I
-Landroid/net/IpSecManager$SecurityParameterIndex;->mService:Landroid/net/IIpSecService;
-Landroid/net/IpSecManager$SecurityParameterIndex;->mSpi:I
-Landroid/net/IpSecManager$SpiUnavailableException;-><init>(Ljava/lang/String;I)V
-Landroid/net/IpSecManager$SpiUnavailableException;->mSpi:I
-Landroid/net/IpSecManager$Status;->OK:I
-Landroid/net/IpSecManager$Status;->RESOURCE_UNAVAILABLE:I
-Landroid/net/IpSecManager$Status;->SPI_UNAVAILABLE:I
-Landroid/net/IpSecManager$UdpEncapsulationSocket;-><init>(Landroid/net/IIpSecService;I)V
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->getResourceId()I
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->mCloseGuard:Ldalvik/system/CloseGuard;
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPfd:Landroid/os/ParcelFileDescriptor;
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->mPort:I
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->mResourceId:I
-Landroid/net/IpSecManager$UdpEncapsulationSocket;->mService:Landroid/net/IIpSecService;
-Landroid/net/IpSecManager;-><init>(Landroid/content/Context;Landroid/net/IIpSecService;)V
-Landroid/net/IpSecManager;->applyTunnelModeTransform(Landroid/net/IpSecManager$IpSecTunnelInterface;ILandroid/net/IpSecTransform;)V
-Landroid/net/IpSecManager;->createIpSecTunnelInterface(Ljava/net/InetAddress;Ljava/net/InetAddress;Landroid/net/Network;)Landroid/net/IpSecManager$IpSecTunnelInterface;
-Landroid/net/IpSecManager;->INVALID_RESOURCE_ID:I
-Landroid/net/IpSecManager;->maybeHandleServiceSpecificException(Landroid/os/ServiceSpecificException;)V
-Landroid/net/IpSecManager;->mContext:Landroid/content/Context;
-Landroid/net/IpSecManager;->mService:Landroid/net/IIpSecService;
-Landroid/net/IpSecManager;->removeTunnelModeTransform(Landroid/net/Network;Landroid/net/IpSecTransform;)V
-Landroid/net/IpSecManager;->rethrowCheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/io/IOException;
-Landroid/net/IpSecManager;->rethrowUncheckedExceptionFromServiceSpecificException(Landroid/os/ServiceSpecificException;)Ljava/lang/RuntimeException;
-Landroid/net/IpSecManager;->TAG:Ljava/lang/String;
-Landroid/net/IpSecSpiResponse;-><init>(I)V
-Landroid/net/IpSecSpiResponse;-><init>(III)V
-Landroid/net/IpSecSpiResponse;-><init>(Landroid/os/Parcel;)V
-Landroid/net/IpSecSpiResponse;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/IpSecSpiResponse;->resourceId:I
-Landroid/net/IpSecSpiResponse;->spi:I
-Landroid/net/IpSecSpiResponse;->status:I
-Landroid/net/IpSecSpiResponse;->TAG:Ljava/lang/String;
-Landroid/net/IpSecTransform$Builder;->buildTunnelModeTransform(Ljava/net/InetAddress;Landroid/net/IpSecManager$SecurityParameterIndex;)Landroid/net/IpSecTransform;
-Landroid/net/IpSecTransform$Builder;->mConfig:Landroid/net/IpSecConfig;
-Landroid/net/IpSecTransform$Builder;->mContext:Landroid/content/Context;
-Landroid/net/IpSecTransform$NattKeepaliveCallback;-><init>()V
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_ERROR:I
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_HARDWARE_UNSUPPORTED:I
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->ERROR_INVALID_NETWORK:I
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->onError(I)V
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStarted()V
-Landroid/net/IpSecTransform$NattKeepaliveCallback;->onStopped()V
-Landroid/net/IpSecTransform;-><init>(Landroid/content/Context;Landroid/net/IpSecConfig;)V
-Landroid/net/IpSecTransform;->activate()Landroid/net/IpSecTransform;
-Landroid/net/IpSecTransform;->checkResultStatus(I)V
-Landroid/net/IpSecTransform;->ENCAP_ESPINUDP:I
-Landroid/net/IpSecTransform;->ENCAP_ESPINUDP_NON_IKE:I
-Landroid/net/IpSecTransform;->ENCAP_NONE:I
-Landroid/net/IpSecTransform;->equals(Landroid/net/IpSecTransform;Landroid/net/IpSecTransform;)Z
-Landroid/net/IpSecTransform;->getConfig()Landroid/net/IpSecConfig;
-Landroid/net/IpSecTransform;->getIpSecService()Landroid/net/IIpSecService;
-Landroid/net/IpSecTransform;->getResourceId()I
-Landroid/net/IpSecTransform;->mCallbackHandler:Landroid/os/Handler;
-Landroid/net/IpSecTransform;->mCloseGuard:Ldalvik/system/CloseGuard;
-Landroid/net/IpSecTransform;->mConfig:Landroid/net/IpSecConfig;
-Landroid/net/IpSecTransform;->mContext:Landroid/content/Context;
-Landroid/net/IpSecTransform;->mKeepalive:Landroid/net/ConnectivityManager$PacketKeepalive;
-Landroid/net/IpSecTransform;->mKeepaliveCallback:Landroid/net/ConnectivityManager$PacketKeepaliveCallback;
-Landroid/net/IpSecTransform;->MODE_TRANSPORT:I
-Landroid/net/IpSecTransform;->MODE_TUNNEL:I
-Landroid/net/IpSecTransform;->mResourceId:I
-Landroid/net/IpSecTransform;->mUserKeepaliveCallback:Landroid/net/IpSecTransform$NattKeepaliveCallback;
-Landroid/net/IpSecTransform;->startNattKeepalive(Landroid/net/IpSecTransform$NattKeepaliveCallback;ILandroid/os/Handler;)V
-Landroid/net/IpSecTransform;->stopNattKeepalive()V
-Landroid/net/IpSecTransform;->TAG:Ljava/lang/String;
-Landroid/net/IpSecTransformResponse;-><init>(I)V
-Landroid/net/IpSecTransformResponse;-><init>(II)V
-Landroid/net/IpSecTransformResponse;-><init>(Landroid/os/Parcel;)V
-Landroid/net/IpSecTransformResponse;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/IpSecTransformResponse;->resourceId:I
-Landroid/net/IpSecTransformResponse;->status:I
-Landroid/net/IpSecTransformResponse;->TAG:Ljava/lang/String;
-Landroid/net/IpSecTunnelInterfaceResponse;-><init>(I)V
-Landroid/net/IpSecTunnelInterfaceResponse;-><init>(IILjava/lang/String;)V
-Landroid/net/IpSecTunnelInterfaceResponse;-><init>(Landroid/os/Parcel;)V
-Landroid/net/IpSecTunnelInterfaceResponse;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/IpSecTunnelInterfaceResponse;->interfaceName:Ljava/lang/String;
-Landroid/net/IpSecTunnelInterfaceResponse;->resourceId:I
-Landroid/net/IpSecTunnelInterfaceResponse;->status:I
-Landroid/net/IpSecTunnelInterfaceResponse;->TAG:Ljava/lang/String;
-Landroid/net/IpSecUdpEncapResponse;-><init>(I)V
-Landroid/net/IpSecUdpEncapResponse;-><init>(IIILjava/io/FileDescriptor;)V
-Landroid/net/IpSecUdpEncapResponse;-><init>(Landroid/os/Parcel;)V
-Landroid/net/IpSecUdpEncapResponse;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/IpSecUdpEncapResponse;->fileDescriptor:Landroid/os/ParcelFileDescriptor;
-Landroid/net/IpSecUdpEncapResponse;->port:I
-Landroid/net/IpSecUdpEncapResponse;->resourceId:I
-Landroid/net/IpSecUdpEncapResponse;->status:I
-Landroid/net/IpSecUdpEncapResponse;->TAG:Ljava/lang/String;
Landroid/net/ITetheringStatsProvider$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
Landroid/net/ITetheringStatsProvider$Stub$Proxy;->getTetherStats(I)Landroid/net/NetworkStats;
diff --git a/core/api/current.txt b/core/api/current.txt
index 74594d0..dddf069 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -3074,6 +3074,7 @@
method public void onSystemActionsChanged();
method public final boolean performGlobalAction(int);
method public void setAccessibilityFocusAppearance(int, @ColorInt int);
+ method public void setAnimationScale(float);
method public boolean setCacheEnabled(boolean);
method public void setGestureDetectionPassthroughRegion(int, @NonNull android.graphics.Region);
method public final void setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo);
@@ -4069,7 +4070,7 @@
method public int getMaxNumPictureInPictureActions();
method public final android.media.session.MediaController getMediaController();
method @NonNull public android.view.MenuInflater getMenuInflater();
- method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+ method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method public final android.app.Activity getParent();
method @Nullable public android.content.Intent getParentActivityIntent();
method public android.content.SharedPreferences getPreferences(int);
@@ -4875,6 +4876,7 @@
field public static final int REASON_DEPENDENCY_DIED = 12; // 0xc
field public static final int REASON_EXCESSIVE_RESOURCE_USAGE = 9; // 0x9
field public static final int REASON_EXIT_SELF = 1; // 0x1
+ field public static final int REASON_FREEZER = 14; // 0xe
field public static final int REASON_INITIALIZATION_FAILURE = 7; // 0x7
field public static final int REASON_LOW_MEMORY = 3; // 0x3
field public static final int REASON_OTHER = 13; // 0xd
@@ -4970,7 +4972,7 @@
method @NonNull @UiContext public final android.content.Context getContext();
method @Nullable public android.view.View getCurrentFocus();
method @NonNull public android.view.LayoutInflater getLayoutInflater();
- method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+ method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method @Nullable public final android.app.Activity getOwnerActivity();
method @Nullable public final android.view.SearchEvent getSearchEvent();
method public final int getVolumeControlStream();
@@ -6949,6 +6951,7 @@
method public boolean performGlobalAction(int);
method public void revokeRuntimePermission(String, String);
method public void revokeRuntimePermissionAsUser(String, String, android.os.UserHandle);
+ method public void setAnimationScale(float);
method public void setOnAccessibilityEventListener(android.app.UiAutomation.OnAccessibilityEventListener);
method public boolean setRotation(int);
method public void setRunAsMonkey(boolean);
@@ -7611,7 +7614,7 @@
field public static final String EXTRA_PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED = "android.app.extra.PROVISIONING_LEAVE_ALL_SYSTEM_APPS_ENABLED";
field public static final String EXTRA_PROVISIONING_LOCALE = "android.app.extra.PROVISIONING_LOCALE";
field public static final String EXTRA_PROVISIONING_LOCAL_TIME = "android.app.extra.PROVISIONING_LOCAL_TIME";
- field public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
+ field @Deprecated public static final String EXTRA_PROVISIONING_LOGO_URI = "android.app.extra.PROVISIONING_LOGO_URI";
field @Deprecated public static final String EXTRA_PROVISIONING_MAIN_COLOR = "android.app.extra.PROVISIONING_MAIN_COLOR";
field public static final String EXTRA_PROVISIONING_MODE = "android.app.extra.PROVISIONING_MODE";
field public static final String EXTRA_PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT = "android.app.extra.PROVISIONING_SENSORS_PERMISSION_GRANT_OPT_OUT";
@@ -9824,7 +9827,6 @@
field public static final String STATUS_BAR_SERVICE = "statusbar";
field public static final String STORAGE_SERVICE = "storage";
field public static final String STORAGE_STATS_SERVICE = "storagestats";
- field public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process";
field public static final String SYSTEM_HEALTH_SERVICE = "systemhealth";
field public static final String TELECOM_SERVICE = "telecom";
field public static final String TELEPHONY_IMS_SERVICE = "telephony_ims";
@@ -26392,75 +26394,6 @@
method @NonNull public android.net.Ikev2VpnProfile.Builder setProxy(@Nullable android.net.ProxyInfo);
}
- public final class IpSecAlgorithm implements android.os.Parcelable {
- ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[]);
- ctor public IpSecAlgorithm(@NonNull String, @NonNull byte[], int);
- method public int describeContents();
- method @NonNull public byte[] getKey();
- method @NonNull public String getName();
- method @NonNull public static java.util.Set<java.lang.String> getSupportedAlgorithms();
- method public int getTruncationLengthBits();
- method public void writeToParcel(android.os.Parcel, int);
- field public static final String AUTH_AES_CMAC = "cmac(aes)";
- field public static final String AUTH_AES_XCBC = "xcbc(aes)";
- field public static final String AUTH_CRYPT_AES_GCM = "rfc4106(gcm(aes))";
- field public static final String AUTH_CRYPT_CHACHA20_POLY1305 = "rfc7539esp(chacha20,poly1305)";
- field public static final String AUTH_HMAC_MD5 = "hmac(md5)";
- field public static final String AUTH_HMAC_SHA1 = "hmac(sha1)";
- field public static final String AUTH_HMAC_SHA256 = "hmac(sha256)";
- field public static final String AUTH_HMAC_SHA384 = "hmac(sha384)";
- field public static final String AUTH_HMAC_SHA512 = "hmac(sha512)";
- field @NonNull public static final android.os.Parcelable.Creator<android.net.IpSecAlgorithm> CREATOR;
- field public static final String CRYPT_AES_CBC = "cbc(aes)";
- field public static final String CRYPT_AES_CTR = "rfc3686(ctr(aes))";
- }
-
- public final class IpSecManager {
- method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress) throws android.net.IpSecManager.ResourceUnavailableException;
- method @NonNull public android.net.IpSecManager.SecurityParameterIndex allocateSecurityParameterIndex(@NonNull java.net.InetAddress, int) throws android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method public void applyTransportModeTransform(@NonNull java.net.Socket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method public void applyTransportModeTransform(@NonNull java.net.DatagramSocket, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method public void applyTransportModeTransform(@NonNull java.io.FileDescriptor, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket(int) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method @NonNull public android.net.IpSecManager.UdpEncapsulationSocket openUdpEncapsulationSocket() throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- method public void removeTransportModeTransforms(@NonNull java.net.Socket) throws java.io.IOException;
- method public void removeTransportModeTransforms(@NonNull java.net.DatagramSocket) throws java.io.IOException;
- method public void removeTransportModeTransforms(@NonNull java.io.FileDescriptor) throws java.io.IOException;
- field public static final int DIRECTION_IN = 0; // 0x0
- field public static final int DIRECTION_OUT = 1; // 0x1
- }
-
- public static final class IpSecManager.ResourceUnavailableException extends android.util.AndroidException {
- }
-
- public static final class IpSecManager.SecurityParameterIndex implements java.lang.AutoCloseable {
- method public void close();
- method public int getSpi();
- }
-
- public static final class IpSecManager.SpiUnavailableException extends android.util.AndroidException {
- method public int getSpi();
- }
-
- public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
- method public void close() throws java.io.IOException;
- method public java.io.FileDescriptor getFileDescriptor();
- method public int getPort();
- }
-
- public final class IpSecTransform implements java.lang.AutoCloseable {
- method public void close();
- }
-
- public static class IpSecTransform.Builder {
- ctor public IpSecTransform.Builder(@NonNull android.content.Context);
- method @NonNull public android.net.IpSecTransform buildTransportModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- method @NonNull public android.net.IpSecTransform.Builder setAuthenticatedEncryption(@NonNull android.net.IpSecAlgorithm);
- method @NonNull public android.net.IpSecTransform.Builder setAuthentication(@NonNull android.net.IpSecAlgorithm);
- method @NonNull public android.net.IpSecTransform.Builder setEncryption(@NonNull android.net.IpSecAlgorithm);
- method @NonNull public android.net.IpSecTransform.Builder setIpv4Encapsulation(@NonNull android.net.IpSecManager.UdpEncapsulationSocket, int);
- }
-
public class LocalServerSocket implements java.io.Closeable {
ctor public LocalServerSocket(String) throws java.io.IOException;
ctor public LocalServerSocket(java.io.FileDescriptor) throws java.io.IOException;
@@ -31525,6 +31458,7 @@
method public void setDataCapacity(int);
method public void setDataPosition(int);
method public void setDataSize(int);
+ method public void setPropagateAllowBlocking();
method public void unmarshall(@NonNull byte[], int, int);
method public void writeArray(@Nullable Object[]);
method public void writeBinderArray(@Nullable android.os.IBinder[]);
@@ -48972,7 +48906,7 @@
}
public interface OnBackInvokedDispatcherOwner {
- method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+ method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
}
public interface OnReceiveContentListener {
@@ -49400,7 +49334,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.VerifiedMotionEvent> CREATOR;
}
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
ctor public View(android.content.Context);
ctor public View(android.content.Context, @Nullable android.util.AttributeSet);
ctor public View(android.content.Context, @Nullable android.util.AttributeSet, int);
@@ -49604,7 +49538,6 @@
method @IdRes public int getNextFocusLeftId();
method @IdRes public int getNextFocusRightId();
method @IdRes public int getNextFocusUpId();
- method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
method public android.view.View.OnFocusChangeListener getOnFocusChangeListener();
method @ColorInt public int getOutlineAmbientShadowColor();
method public android.view.ViewOutlineProvider getOutlineProvider();
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 6e7bc76..9737cde 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -139,9 +139,14 @@
package android.content.pm {
+ public class ApplicationInfo extends android.content.pm.PackageItemInfo implements android.os.Parcelable {
+ method @NonNull public java.util.List<android.content.pm.SharedLibraryInfo> getSharedLibraryInfos();
+ }
+
public abstract class PackageManager {
method @NonNull public String getPermissionControllerPackageName();
method @NonNull public String getSupplementalProcessPackageName();
+ field public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 67108864; // 0x4000000
}
}
@@ -192,7 +197,7 @@
method public void adjustSuggestedStreamVolumeForUid(int, int, int, @NonNull String, int, int, int);
method @NonNull public java.util.List<android.bluetooth.BluetoothCodecConfig> getHwOffloadFormatsSupportedForA2dp();
method @NonNull public java.util.List<android.bluetooth.BluetoothLeAudioCodecConfig> getHwOffloadFormatsSupportedForLeAudio();
- method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BtProfileConnectionInfo);
+ method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void handleBluetoothActiveDeviceChanged(@Nullable android.bluetooth.BluetoothDevice, @Nullable android.bluetooth.BluetoothDevice, @NonNull android.media.BluetoothProfileConnectionInfo);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setA2dpSuspended(boolean);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setBluetoothHeadsetProperties(@NonNull String, boolean, boolean);
method @RequiresPermission("android.permission.BLUETOOTH_STACK") public void setHfpEnabled(boolean);
@@ -202,18 +207,18 @@
field public static final int FLAG_FROM_KEY = 4096; // 0x1000
}
- public final class BtProfileConnectionInfo implements android.os.Parcelable {
- method @NonNull public static android.media.BtProfileConnectionInfo a2dpInfo(boolean, int);
- method @NonNull public static android.media.BtProfileConnectionInfo a2dpSinkInfo(int);
+ public final class BluetoothProfileConnectionInfo implements android.os.Parcelable {
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpInfo(boolean, int);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createA2dpSinkInfo(int);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createHearingAidInfo(boolean);
+ method @NonNull public static android.media.BluetoothProfileConnectionInfo createLeAudioInfo(boolean, boolean);
method public int describeContents();
- method public boolean getIsLeOutput();
method public int getProfile();
- method public boolean getSuppressNoisyIntent();
method public int getVolume();
- method @NonNull public static android.media.BtProfileConnectionInfo hearingAidInfo(boolean);
- method @NonNull public static android.media.BtProfileConnectionInfo leAudio(boolean, boolean);
+ method public boolean isLeOutput();
+ method public boolean isSuppressNoisyIntent();
method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.media.BtProfileConnectionInfo> CREATOR;
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.BluetoothProfileConnectionInfo> CREATOR;
}
public class MediaMetadataRetriever implements java.lang.AutoCloseable {
@@ -278,14 +283,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkSpecifier> CREATOR;
}
- public final class IpSecManager {
- field public static final int DIRECTION_FWD = 2; // 0x2
- }
-
- public static final class IpSecManager.UdpEncapsulationSocket implements java.lang.AutoCloseable {
- method public int getResourceId();
- }
-
public class LocalSocket implements java.io.Closeable {
ctor public LocalSocket(@NonNull java.io.FileDescriptor);
}
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 311b110..07639fb 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -513,7 +513,7 @@
package android.view {
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
method protected void initializeFadingEdge(android.content.res.TypedArray);
method protected void initializeScrollbars(android.content.res.TypedArray);
}
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index faeadad..4b9e1c0 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -911,6 +911,19 @@
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public int getNavBarModeOverride();
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setDisabledForSetup(boolean);
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setNavBarModeOverride(int);
+ method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferReceiverDisplay(int, @NonNull android.media.MediaRoute2Info);
+ method @RequiresPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL) public void updateMediaTapToTransferSenderDisplay(int, @NonNull android.media.MediaRoute2Info, @Nullable java.util.concurrent.Executor, @Nullable Runnable);
+ field public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0; // 0x0
+ field public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1; // 0x1
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1; // 0x1
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0; // 0x0
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8; // 0x8
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6; // 0x6
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4; // 0x4
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2; // 0x2
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7; // 0x7
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5; // 0x5
+ field public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3; // 0x3
field public static final int NAV_BAR_MODE_OVERRIDE_KIDS = 1; // 0x1
field public static final int NAV_BAR_MODE_OVERRIDE_NONE = 0; // 0x0
}
@@ -2814,8 +2827,8 @@
field public static final String APP_PREDICTION_SERVICE = "app_prediction";
field public static final String BACKUP_SERVICE = "backup";
field public static final String BATTERY_STATS_SERVICE = "batterystats";
- field @Deprecated public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
- field public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
+ field public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 1048576; // 0x100000
+ field @Deprecated public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 262144; // 0x40000
field public static final String CLOUDSEARCH_SERVICE = "cloudsearch";
field public static final String CONTENT_SUGGESTIONS_SERVICE = "content_suggestions";
field public static final String CONTEXTHUB_SERVICE = "contexthub";
@@ -7940,7 +7953,7 @@
public class FrontendStatus {
method public int getAgc();
- method @NonNull public android.media.tv.tuner.frontend.Atsc3PlpInfo[] getAllAtsc3PlpInfo();
+ method @NonNull public java.util.List<android.media.tv.tuner.frontend.Atsc3PlpInfo> getAllAtsc3PlpInfo();
method @NonNull public android.media.tv.tuner.frontend.FrontendStatus.Atsc3PlpTuningInfo[] getAtsc3PlpTuningInfo();
method public int getBandwidth();
method public int getBer();
@@ -8324,23 +8337,6 @@
method public void release();
}
- public final class IpSecManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- }
-
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method @NonNull public String getInterfaceName();
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void setUnderlyingNetwork(@NonNull android.net.Network) throws java.io.IOException;
- }
-
- public static class IpSecTransform.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- }
-
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public MatchAllNetworkSpecifier();
method public int describeContents();
@@ -9123,6 +9119,7 @@
}
public static class Build.VERSION {
+ field @NonNull public static final java.util.Set<java.lang.String> KNOWN_CODENAMES;
field @NonNull public static final String PREVIEW_SDK_FINGERPRINT;
}
@@ -14721,6 +14718,7 @@
field @NonNull public static final android.os.Parcelable.Creator<android.telephony.ims.RcsClientConfiguration> CREATOR;
field public static final String RCS_PROFILE_1_0 = "UP_1.0";
field public static final String RCS_PROFILE_2_3 = "UP_2.3";
+ field public static final String RCS_PROFILE_2_4 = "UP_2.4";
}
public final class RcsContactPresenceTuple implements android.os.Parcelable {
diff --git a/core/api/system-lint-baseline.txt b/core/api/system-lint-baseline.txt
index e17a9bb..1b45e88 100644
--- a/core/api/system-lint-baseline.txt
+++ b/core/api/system-lint-baseline.txt
@@ -1,11 +1,5 @@
// Baseline format: 1.0
ArrayReturn: android.view.contentcapture.ViewNode#getAutofillOptions():
-
-
-
-BuilderSetStyle: android.net.IpSecTransform.Builder#buildTunnelModeTransform(java.net.InetAddress, android.net.IpSecManager.SecurityParameterIndex):
- Builder methods names should use setFoo() / addFoo() / clearFoo() style: method android.net.IpSecTransform.Builder.buildTunnelModeTransform(java.net.InetAddress,android.net.IpSecManager.SecurityParameterIndex)
-
ExecutorRegistration: android.media.MediaPlayer#setOnRtpRxNoticeListener(android.content.Context, android.media.MediaPlayer.OnRtpRxNoticeListener, android.os.Handler):
Registration methods should have overload that accepts delivery Executor: `setOnRtpRxNoticeListener`
@@ -15,8 +9,6 @@
GenericException: android.hardware.location.ContextHubClient#finalize():
-GenericException: android.net.IpSecManager.IpSecTunnelInterface#finalize():
-
GenericException: android.service.autofill.augmented.FillWindow#finalize():
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index e2a78c6..9d6fc0e 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -1607,10 +1607,6 @@
method public void setIncludeTestInterfaces(boolean);
}
- public final class IpSecManager {
- field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
- }
-
public class NetworkPolicyManager {
method public boolean getRestrictBackground();
method @RequiresPermission(android.Manifest.permission.OBSERVE_NETWORK_POLICY) public boolean isUidNetworkingBlocked(int, boolean);
@@ -1741,8 +1737,11 @@
public final class Parcel {
method public boolean allowSquashing();
+ method public int getFlags();
method public int readExceptionCode();
method public void restoreAllowSquashing(boolean);
+ field public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1; // 0x1
+ field public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 2; // 0x2
}
public class ParcelFileDescriptor implements java.io.Closeable android.os.Parcelable {
@@ -2741,6 +2740,7 @@
method @NonNull public android.view.Display.Mode getDefaultMode();
method @NonNull public int[] getReportedHdrTypes();
method @NonNull public android.graphics.ColorSpace[] getSupportedWideColorGamut();
+ method @Nullable public android.view.Display.Mode getSystemPreferredDisplayMode();
method public int getType();
method @Nullable public android.view.Display.Mode getUserPreferredDisplayMode();
method public boolean hasAccess(int);
@@ -2816,7 +2816,7 @@
method public void setView(@NonNull android.view.View, @NonNull android.view.WindowManager.LayoutParams);
}
- @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback android.view.OnBackInvokedDispatcherOwner {
+ @UiThread public class View implements android.view.accessibility.AccessibilityEventSource android.graphics.drawable.Drawable.Callback android.view.KeyEvent.Callback {
method public android.view.View getTooltipView();
method public boolean isAutofilled();
method public static boolean isDefaultFocusHighlightEnabled();
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 42d2d28..50473f1 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -3120,6 +3120,33 @@
}
}
+ /**
+ * Sets the system settings values that control the scaling factor for animations. The scale
+ * controls the animation playback speed for animations that respect these settings. Animations
+ * that do not respect the settings values will not be affected by this function. A lower scale
+ * value results in a faster speed. A value of <code>0</code> disables animations entirely. When
+ * animations are disabled services receive window change events more quickly which can reduce
+ * the potential by confusion by reducing the time during which windows are in transition.
+ *
+ * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED
+ * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
+ * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE
+ * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE
+ * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE
+ * @param scale The scaling factor for all animations.
+ */
+ public void setAnimationScale(float scale) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance(this).getConnection(mConnectionId);
+ if (connection != null) {
+ try {
+ connection.setAnimationScale(scale);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
private static class AccessibilityContext extends ContextWrapper {
private final int mConnectionId;
diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
index 2cc15b4..0d6b199 100644
--- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
+++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl
@@ -144,4 +144,6 @@
void onDoubleTap(int displayId);
void onDoubleTapAndHold(int displayId);
+
+ void setAnimationScale(float scale);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 530666b..90c56ae 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -6139,6 +6139,10 @@
* you to specify a custom animation even when starting an activity from
* outside the context of the current top activity.
*
+ * <p>Af of {@link android.os.Build.VERSION_CODES#S} application can only specify
+ * a transition animation when the transition happens within the same task. System
+ * default animation is used for cross-task transition animations.
+ *
* @param enterAnim A resource ID of the animation resource to use for
* the incoming activity. Use 0 for no animation.
* @param exitAnim A resource ID of the animation resource to use for
@@ -8742,17 +8746,15 @@
* Returns the {@link OnBackInvokedDispatcher} instance associated with the window that this
* activity is attached to.
*
- * Returns null if the activity is not attached to a window with a decor.
+ * @throws IllegalStateException if this Activity is not visual.
*/
- @Nullable
+ @NonNull
@Override
public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- if (mWindow != null) {
- View decorView = mWindow.getDecorView();
- if (decorView != null) {
- return decorView.getOnBackInvokedDispatcher();
- }
+ if (mWindow == null) {
+ throw new IllegalStateException("OnBackInvokedDispatcher are not available on "
+ + "non-visual activities");
}
- return null;
+ return ((OnBackInvokedDispatcherOwner) mWindow).getOnBackInvokedDispatcher();
}
}
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index cce7dd3..a58ceaa 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -215,6 +215,14 @@
public abstract boolean isSystemReady();
/**
+ * Returns package name given pid.
+ *
+ * @param pid The pid we are searching package name for.
+ */
+ @Nullable
+ public abstract String getPackageNameByPid(int pid);
+
+ /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
diff --git a/core/java/android/app/ApplicationExitInfo.java b/core/java/android/app/ApplicationExitInfo.java
index 9039bbd..60e22f4 100644
--- a/core/java/android/app/ApplicationExitInfo.java
+++ b/core/java/android/app/ApplicationExitInfo.java
@@ -156,6 +156,12 @@
public static final int REASON_OTHER = 13;
/**
+ * Application process was killed by App Freezer, for example, because it receives
+ * sync binder transactions while being frozen.
+ */
+ public static final int REASON_FREEZER = 14;
+
+ /**
* Application process kills subreason is unknown.
*
* For internal use only.
@@ -487,6 +493,7 @@
REASON_USER_STOPPED,
REASON_DEPENDENCY_DIED,
REASON_OTHER,
+ REASON_FREEZER,
})
@Retention(RetentionPolicy.SOURCE)
public @interface Reason {}
@@ -1138,6 +1145,8 @@
return "DEPENDENCY DIED";
case REASON_OTHER:
return "OTHER KILLS BY SYSTEM";
+ case REASON_FREEZER:
+ return "FREEZER";
default:
return "UNKNOWN";
}
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a7fb83b..4b42ddc3 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -1449,15 +1449,9 @@
*
* Returns null if the dialog is not attached to a window with a decor.
*/
- @Nullable
+ @NonNull
@Override
public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- if (mWindow != null) {
- View decorView = mWindow.getDecorView();
- if (decorView != null) {
- return decorView.getOnBackInvokedDispatcher();
- }
- }
- return null;
+ return ((OnBackInvokedDispatcherOwner) mWindow).getOnBackInvokedDispatcher();
}
}
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index e4ef12c..7c48a57 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -679,6 +679,10 @@
*/
boolean isAppFreezerSupported();
+ /**
+ * Return whether the app freezer is enabled (true) or not (false) by this system.
+ */
+ boolean isAppFreezerEnabled();
/**
* Kills uid with the reason of permission change.
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 56c301f..8fcb07f 100644
--- a/core/java/android/app/StatusBarManager.java
+++ b/core/java/android/app/StatusBarManager.java
@@ -16,6 +16,7 @@
package android.app;
+import android.Manifest;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -27,6 +28,7 @@
import android.content.ComponentName;
import android.content.Context;
import android.graphics.drawable.Icon;
+import android.media.MediaRoute2Info;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
@@ -39,6 +41,7 @@
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.NotificationVisibility;
import java.lang.annotation.Retention;
@@ -338,6 +341,166 @@
@Retention(RetentionPolicy.SOURCE)
public @interface NavBarModeOverride {}
+ /**
+ * State indicating that this sender device is close to a receiver device, so the user can
+ * potentially *start* a cast to the receiver device if the user moves their device a bit
+ * closer.
+ * <p>
+ * Important notes:
+ * <ul>
+ * <li>This state represents that the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.</li>
+ * <li>This state is for *starting* a cast. It should be used when this device is currently
+ * playing media locally and the media should be transferred to be played on the receiver
+ * device instead.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST = 0;
+
+ /**
+ * State indicating that this sender device is close to a receiver device, so the user can
+ * potentially *end* a cast on the receiver device if the user moves this device a bit closer.
+ * <p>
+ * Important notes:
+ * <ul>
+ * <li>This state represents that the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.</li>
+ * <li>This state is for *ending* a cast. It should be used when media is currently being
+ * played on the receiver device and the media should be transferred to play locally
+ * instead.</li>
+ * </ul>
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST = 1;
+
+ /**
+ * State indicating that a media transfer from this sender device to a receiver device has been
+ * started.
+ * <p>
+ * Important note: This state is for *starting* a cast. It should be used when this device is
+ * currently playing media locally and the media has started being transferred to the receiver
+ * device instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED = 2;
+
+ /**
+ * State indicating that a media transfer from the receiver and back to this sender device
+ * has been started.
+ * <p>
+ * Important note: This state is for *ending* a cast. It should be used when media is currently
+ * being played on the receiver device and the media has started being transferred to play
+ * locally instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED = 3;
+
+ /**
+ * State indicating that a media transfer from this sender device to a receiver device has
+ * finished successfully.
+ * <p>
+ * Important note: This state is for *starting* a cast. It should be used when this device had
+ * previously been playing media locally and the media has successfully been transferred to the
+ * receiver device instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED = 4;
+
+ /**
+ * State indicating that a media transfer from the receiver and back to this sender device has
+ * finished successfully.
+ * <p>
+ * Important note: This state is for *ending* a cast. It should be used when media was
+ * previously being played on the receiver device and has been successfully transferred to play
+ * locally on this device instead.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED = 5;
+
+ /**
+ * State indicating that the attempted transfer to the receiver device has failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED = 6;
+
+ /**
+ * State indicating that the attempted transfer back to this device has failed.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED = 7;
+
+ /**
+ * State indicating that this sender device is no longer close to the receiver device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER = 8;
+
+ /** @hide */
+ @IntDef(prefix = {"MEDIA_TRANSFER_SENDER_STATE_"}, value = {
+ MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
+ MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaTransferSenderState {}
+
+ /**
+ * State indicating that this receiver device is close to a sender device, so the user can
+ * potentially start or end a cast to the receiver device if the user moves the sender device a
+ * bit closer.
+ * <p>
+ * Important note: This state represents that the device is close enough to inform the user that
+ * transferring is an option, but the device is *not* close enough to actually initiate a
+ * transfer yet.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER = 0;
+
+ /**
+ * State indicating that this receiver device is no longer close to the sender device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final int MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER = 1;
+
+ /** @hide */
+ @IntDef(prefix = {"MEDIA_TRANSFER_RECEIVER_STATE_"}, value = {
+ MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface MediaTransferReceiverState {}
+
@UnsupportedAppUsage
private Context mContext;
private IStatusBarService mService;
@@ -789,6 +952,81 @@
return navBarModeOverride;
}
+ /**
+ * Notifies the system of a new media tap-to-transfer state for the <b>sender</b> device.
+ *
+ * <p>The callback should only be provided for the {@link
+ * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED} or {@link
+ * MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED} states, since those are the
+ * only states where an action can be un-done.
+ *
+ * @param displayState the new state for media tap-to-transfer.
+ * @param routeInfo the media route information for the media being transferred.
+ * @param undoExecutor an executor to run the callback on and must be provided if the
+ * callback is non-null.
+ * @param undoCallback a callback that will be triggered if the user elects to undo a media
+ * transfer.
+ *
+ * @throws IllegalArgumentException if an undo callback is provided for states that are not a
+ * succeeded state.
+ * @throws IllegalArgumentException if an executor is not provided when a callback is.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void updateMediaTapToTransferSenderDisplay(
+ @MediaTransferSenderState int displayState,
+ @NonNull MediaRoute2Info routeInfo,
+ @Nullable Executor undoExecutor,
+ @Nullable Runnable undoCallback
+ ) {
+ Objects.requireNonNull(routeInfo);
+ if (displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED
+ && displayState != MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED
+ && undoCallback != null) {
+ throw new IllegalArgumentException(
+ "The undoCallback should only be provided when the state is a "
+ + "transfer succeeded state");
+ }
+ if (undoCallback != null && undoExecutor == null) {
+ throw new IllegalArgumentException(
+ "You must pass an executor when you pass an undo callback");
+ }
+ IStatusBarService svc = getService();
+ try {
+ UndoCallback callbackProxy = null;
+ if (undoExecutor != null) {
+ callbackProxy = new UndoCallback(undoExecutor, undoCallback);
+ }
+ svc.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, callbackProxy);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Notifies the system of a new media tap-to-transfer state for the <b>receiver</b> device.
+ *
+ * @param displayState the new state for media tap-to-transfer.
+ * @param routeInfo the media route information for the media being transferred.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MEDIA_CONTENT_CONTROL)
+ public void updateMediaTapToTransferReceiverDisplay(
+ @MediaTransferReceiverState int displayState,
+ @NonNull MediaRoute2Info routeInfo) {
+ Objects.requireNonNull(routeInfo);
+ IStatusBarService svc = getService();
+ try {
+ svc.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
/** @hide */
public static String windowStateToString(int state) {
if (state == WINDOW_STATE_HIDING) return "WINDOW_STATE_HIDING";
@@ -1071,4 +1309,29 @@
mExecutor.execute(() -> mCallback.accept(userResponse));
}
}
+
+ /**
+ * @hide
+ */
+ static final class UndoCallback extends IUndoMediaTransferCallback.Stub {
+ @NonNull
+ private final Executor mExecutor;
+ @NonNull
+ private final Runnable mCallback;
+
+ UndoCallback(@NonNull Executor executor, @NonNull Runnable callback) {
+ mExecutor = executor;
+ mCallback = callback;
+ }
+
+ @Override
+ public void onUndoTriggered() {
+ final long callingIdentity = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(mCallback);
+ } finally {
+ restoreCallingIdentity(callingIdentity);
+ }
+ }
+ }
}
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java
index 79180cb..4187ba0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -139,12 +139,10 @@
import android.net.ConnectivityFrameworkInitializerTiramisu;
import android.net.EthernetManager;
import android.net.IEthernetManager;
-import android.net.IIpSecService;
import android.net.INetworkPolicyManager;
import android.net.INetworkStatsService;
import android.net.IPacProxyManager;
import android.net.IVpnManager;
-import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.NetworkWatchlistManager;
@@ -441,15 +439,6 @@
return new VcnManager(ctx, service);
}});
- registerService(Context.IPSEC_SERVICE, IpSecManager.class,
- new CachedServiceFetcher<IpSecManager>() {
- @Override
- public IpSecManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- IBinder b = ServiceManager.getService(Context.IPSEC_SERVICE);
- IIpSecService service = IIpSecService.Stub.asInterface(b);
- return new IpSecManager(ctx, service);
- }});
-
registerService(Context.COUNTRY_DETECTOR, CountryDetector.class,
new StaticServiceFetcher<CountryDetector>() {
@Override
diff --git a/core/java/android/app/UiAutomation.java b/core/java/android/app/UiAutomation.java
index 00903a8..b41b5f00 100644
--- a/core/java/android/app/UiAutomation.java
+++ b/core/java/android/app/UiAutomation.java
@@ -780,6 +780,33 @@
}
/**
+ * Sets the system settings values that control the scaling factor for animations. The scale
+ * controls the animation playback speed for animations that respect these settings. Animations
+ * that do not respect the settings values will not be affected by this function. A lower scale
+ * value results in a faster speed. A value of <code>0</code> disables animations entirely. When
+ * animations are disabled services receive window change events more quickly which can reduce
+ * the potential by confusion by reducing the time during which windows are in transition.
+ *
+ * @see AccessibilityEvent#TYPE_WINDOWS_CHANGED
+ * @see AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED
+ * @see android.provider.Settings.Global#WINDOW_ANIMATION_SCALE
+ * @see android.provider.Settings.Global#TRANSITION_ANIMATION_SCALE
+ * @see android.provider.Settings.Global#ANIMATOR_DURATION_SCALE
+ * @param scale The scaling factor for all animations.
+ */
+ public void setAnimationScale(float scale) {
+ final IAccessibilityServiceConnection connection =
+ AccessibilityInteractionClient.getInstance().getConnection(mConnectionId);
+ if (connection != null) {
+ try {
+ connection.setAnimationScale(scale);
+ } catch (RemoteException re) {
+ throw new RuntimeException(re);
+ }
+ }
+ }
+
+ /**
* A request for WindowManagerService to wait until all animations have completed and input
* information has been sent from WindowManager to native InputManager.
*
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 8326580..6cd991b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -1330,7 +1330,10 @@
*
* <p>Use in an intent with action {@link #ACTION_PROVISION_MANAGED_PROFILE} or
* {@link #ACTION_PROVISION_MANAGED_DEVICE}
+ *
+ * @deprecated Logo customization is no longer supported in the provisioning flow.
*/
+ @Deprecated
public static final String EXTRA_PROVISIONING_LOGO_URI =
"android.app.extra.PROVISIONING_LOGO_URI";
@@ -10133,7 +10136,9 @@
/**
* Called by a profile owner of secondary user that is affiliated with the device to stop the
- * calling user and switch back to primary user.
+ * calling user and switch back to primary user (when the user was
+ * {@link #switchUser(ComponentName, UserHandle)} switched to) or stop the user (when it was
+ * {@link #startUserInBackground(ComponentName, UserHandle) started in background}.
*
* <p>Notice that on devices running with
* {@link UserManager#isHeadlessSystemUserMode() headless system user mode}, there is no primary
@@ -10161,7 +10166,12 @@
}
/**
- * Same as {@link #logoutUser(ComponentName)}, but called by system (like Settings), not admin.
+ * Similar to {@link #logoutUser(ComponentName)}, except:
+ *
+ * <ul>
+ * <li>Called by system (like Settings), not admin.
+ * <li>It logs out the current user, not the caller.
+ * </ul>
*
* @hide
*/
@@ -10178,7 +10188,10 @@
}
/**
* Gets the user a {@link #logoutUser(ComponentName)} call would switch to,
- * or {@code null} if the current user is not in a session.
+ * or {@code null} if the current user is not in a session (i.e., if it was not
+ * {@link #switchUser(ComponentName, UserHandle) switched} or
+ * {@link #startUserInBackground(ComponentName, UserHandle) started in background} by the
+ * device admin.
*
* @hide
*/
diff --git a/core/java/android/companion/TEST_MAPPING b/core/java/android/companion/TEST_MAPPING
index 63f54fa..b561c29 100644
--- a/core/java/android/companion/TEST_MAPPING
+++ b/core/java/android/companion/TEST_MAPPING
@@ -1,12 +1,7 @@
{
- "presubmit": [
+ "imports": [
{
- "name": "CtsOsTestCases",
- "options": [
- {
- "include-filter": "android.os.cts.CompanionDeviceManagerTest"
- }
- ]
+ "path": "frameworks/base/services/companion"
}
]
}
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 52681630..6ffea3f 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -408,6 +408,7 @@
* @hide
*/
@SystemApi
+ @Deprecated
public static final int BIND_ALLOW_FOREGROUND_SERVICE_STARTS_FROM_BACKGROUND = 0x00040000;
/**
@@ -421,12 +422,13 @@
public static final int BIND_SCHEDULE_LIKE_TOP_APP = 0x00080000;
/**
- * This flag has never been used.
+ * Flag for {@link #bindService}: allow background activity starts from the bound service's
+ * process.
+ * This flag is only respected if the caller is holding
+ * {@link android.Manifest.permission#START_ACTIVITIES_FROM_BACKGROUND}.
* @hide
- * @deprecated This flag has never been used.
*/
@SystemApi
- @Deprecated
public static final int BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS = 0x00100000;
/**
@@ -3888,7 +3890,6 @@
//@hide: SPEECH_RECOGNITION_SERVICE,
UWB_SERVICE,
MEDIA_METRICS_SERVICE,
- SUPPLEMENTAL_PROCESS_SERVICE,
//@hide: ATTESTATION_VERIFICATION_SERVICE,
//@hide: SAFETY_CENTER_SERVICE,
})
@@ -5970,13 +5971,6 @@
public static final String LOCALE_SERVICE = "locale";
/**
- * Use with {@link #getSystemService(String)} to retrieve a Supplemental Process Manager.
- *
- * @see #getSystemService(String)
- */
- public static final String SUPPLEMENTAL_PROCESS_SERVICE = "supplemental_process";
-
- /**
* Use with {@link #getSystemService(String)} to retrieve a {@link
* android.safetycenter.SafetyCenterManager} instance for interacting with the safety center.
*
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 9e9dd1e..567f649 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -19,6 +19,7 @@
import static android.os.Build.VERSION_CODES.DONUT;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.annotation.TestApi;
@@ -48,6 +49,7 @@
import java.text.Collator;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
@@ -62,58 +64,58 @@
private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class);
/**
- * Default task affinity of all activities in this application. See
- * {@link ActivityInfo#taskAffinity} for more information. This comes
- * from the "taskAffinity" attribute.
+ * Default task affinity of all activities in this application. See
+ * {@link ActivityInfo#taskAffinity} for more information. This comes
+ * from the "taskAffinity" attribute.
*/
public String taskAffinity;
-
+
/**
* Optional name of a permission required to be able to access this
* application's components. From the "permission" attribute.
*/
public String permission;
-
+
/**
* The name of the process this application should run in. From the
* "process" attribute or, if not set, the same as
* <var>packageName</var>.
*/
public String processName;
-
+
/**
* Class implementing the Application object. From the "class"
* attribute.
*/
public String className;
-
+
/**
* A style resource identifier (in the package's resources) of the
* description of an application. From the "description" attribute
* or, if not set, 0.
*/
- public int descriptionRes;
-
+ public int descriptionRes;
+
/**
* A style resource identifier (in the package's resources) of the
* default visual theme of the application. From the "theme" attribute
* or, if not set, 0.
*/
public int theme;
-
+
/**
* Class implementing the Application's manage space
* functionality. From the "manageSpaceActivity"
* attribute. This is an optional attribute and will be null if
* applications don't specify it in their manifest
*/
- public String manageSpaceActivityName;
-
+ public String manageSpaceActivityName;
+
/**
* Class implementing the Application's backup functionality. From
* the "backupAgent" attribute. This is an optional attribute and
* will be null if the application does not specify it in its manifest.
- *
+ *
* <p>If android:allowBackup is set to false, this attribute is ignored.
*/
public String backupAgentName;
@@ -174,7 +176,7 @@
* {@code signatureOrSystem}.
*/
public static final int FLAG_SYSTEM = 1<<0;
-
+
/**
* Value for {@link #flags}: set to true if this application would like to
* allow debugging of its
@@ -183,7 +185,7 @@
* android:debuggable} of the <application> tag.
*/
public static final int FLAG_DEBUGGABLE = 1<<1;
-
+
/**
* Value for {@link #flags}: set to true if this application has code
* associated with it. Comes
@@ -191,7 +193,7 @@
* android:hasCode} of the <application> tag.
*/
public static final int FLAG_HAS_CODE = 1<<2;
-
+
/**
* Value for {@link #flags}: set to true if this application is persistent.
* Comes from {@link android.R.styleable#AndroidManifestApplication_persistent
@@ -212,20 +214,20 @@
* android:allowTaskReparenting} of the <application> tag.
*/
public static final int FLAG_ALLOW_TASK_REPARENTING = 1<<5;
-
+
/**
* Value for {@link #flags}: default value for the corresponding ActivityInfo flag.
* Comes from {@link android.R.styleable#AndroidManifestApplication_allowClearUserData
* android:allowClearUserData} of the <application> tag.
*/
public static final int FLAG_ALLOW_CLEAR_USER_DATA = 1<<6;
-
+
/**
* Value for {@link #flags}: this is set if this application has been
* installed as an update to a built-in system application.
*/
public static final int FLAG_UPDATED_SYSTEM_APP = 1<<7;
-
+
/**
* Value for {@link #flags}: this is set if the application has specified
* {@link android.R.styleable#AndroidManifestApplication_testOnly
@@ -240,15 +242,15 @@
* android:smallScreens}.
*/
public static final int FLAG_SUPPORTS_SMALL_SCREENS = 1<<9;
-
+
/**
* Value for {@link #flags}: true when the application's window can be
* displayed on normal screens. Corresponds to
* {@link android.R.styleable#AndroidManifestSupportsScreens_normalScreens
* android:normalScreens}.
*/
- public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10;
-
+ public static final int FLAG_SUPPORTS_NORMAL_SCREENS = 1<<10;
+
/**
* Value for {@link #flags}: true when the application's window can be
* increased in size for larger screens. Corresponds to
@@ -256,7 +258,7 @@
* android:largeScreens}.
*/
public static final int FLAG_SUPPORTS_LARGE_SCREENS = 1<<11;
-
+
/**
* Value for {@link #flags}: true when the application knows how to adjust
* its UI for different screen sizes. Corresponds to
@@ -264,7 +266,7 @@
* android:resizeable}.
*/
public static final int FLAG_RESIZEABLE_FOR_SCREENS = 1<<12;
-
+
/**
* Value for {@link #flags}: true when the application knows how to
* accommodate different screen densities. Corresponds to
@@ -276,7 +278,7 @@
*/
@Deprecated
public static final int FLAG_SUPPORTS_SCREEN_DENSITIES = 1<<13;
-
+
/**
* Value for {@link #flags}: set to true if this application would like to
* request the VM to operate under the safe mode. Comes from
@@ -288,7 +290,7 @@
/**
* Value for {@link #flags}: set to <code>false</code> if the application does not wish
* to permit any OS-driven backups of its data; <code>true</code> otherwise.
- *
+ *
* <p>Comes from the
* {@link android.R.styleable#AndroidManifestApplication_allowBackup android:allowBackup}
* attribute of the <application> tag.
@@ -351,7 +353,7 @@
* android:xlargeScreens}.
*/
public static final int FLAG_SUPPORTS_XLARGE_SCREENS = 1<<19;
-
+
/**
* Value for {@link #flags}: true when the application has requested a
* large heap for its processes. Corresponds to
@@ -1114,7 +1116,7 @@
* the same uid).
*/
public int uid;
-
+
/**
* The minimum SDK version this application can run on. It will not run
* on earlier versions.
@@ -1817,7 +1819,7 @@
if (sb == null) {
sb = ab.packageName;
}
-
+
return sCollator.compare(sa.toString(), sb.toString());
}
@@ -1830,7 +1832,7 @@
public ApplicationInfo() {
createTimestamp = System.currentTimeMillis();
}
-
+
public ApplicationInfo(ApplicationInfo orig) {
super(orig);
taskAffinity = orig.taskAffinity;
@@ -2125,7 +2127,7 @@
/**
* Disable compatibility mode
- *
+ *
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
@@ -2346,7 +2348,7 @@
}
return pm.getDefaultActivityIcon();
}
-
+
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
private boolean isPackageUnavailable(PackageManager pm) {
try {
@@ -2655,4 +2657,22 @@
public int getLocaleConfigRes() {
return localeConfigRes;
}
+
+
+ /**
+ * List of all shared libraries this application is linked against. This
+ * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES
+ * PackageManager.GET_SHARED_LIBRARY_FILES} flag was used when retrieving the structure.
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ public List<SharedLibraryInfo> getSharedLibraryInfos() {
+ if (sharedLibraryInfos == null) {
+ return Collections.EMPTY_LIST;
+ }
+ return sharedLibraryInfos;
+ }
+
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index aa64700..07227c5 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -1063,6 +1063,7 @@
* via this flag.
* @hide
*/
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public static final int MATCH_STATIC_SHARED_AND_SDK_LIBRARIES = 0x04000000;
/**
diff --git a/core/java/android/hardware/camera2/CameraManager.java b/core/java/android/hardware/camera2/CameraManager.java
index c12e819..d6d3a97 100644
--- a/core/java/android/hardware/camera2/CameraManager.java
+++ b/core/java/android/hardware/camera2/CameraManager.java
@@ -25,6 +25,7 @@
import android.annotation.TestApi;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.graphics.Point;
import android.hardware.CameraStatus;
import android.hardware.ICameraService;
import android.hardware.ICameraServiceListener;
@@ -458,12 +459,14 @@
(DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE);
Display display = displayManager.getDisplay(Display.DEFAULT_DISPLAY);
if (display != null) {
- int width = display.getWidth();
- int height = display.getHeight();
+ Point sz = new Point();
+ display.getRealSize(sz);
+ int width = sz.x;
+ int height = sz.y;
if (height > width) {
height = width;
- width = display.getHeight();
+ width = sz.y;
}
ret = new Size(width, height);
@@ -471,7 +474,7 @@
Log.e(TAG, "Invalid default display!");
}
} catch (Exception e) {
- Log.e(TAG, "getDisplaySize Failed. " + e.toString());
+ Log.e(TAG, "getDisplaySize Failed. " + e);
}
return ret;
diff --git a/core/java/android/hardware/display/DisplayManagerGlobal.java b/core/java/android/hardware/display/DisplayManagerGlobal.java
index 1a7a63ae..af8ec27 100644
--- a/core/java/android/hardware/display/DisplayManagerGlobal.java
+++ b/core/java/android/hardware/display/DisplayManagerGlobal.java
@@ -916,6 +916,17 @@
}
/**
+ * Returns the system preferred display mode.
+ */
+ public Display.Mode getSystemPreferredDisplayMode(int displayId) {
+ try {
+ return mDm.getSystemPreferredDisplayMode(displayId);
+ } catch (RemoteException ex) {
+ throw ex.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* When enabled the app requested display resolution and refresh rate is always selected
* in DisplayModeDirector regardless of user settings and policies for low brightness, low
* battery etc.
diff --git a/core/java/android/hardware/display/IDisplayManager.aidl b/core/java/android/hardware/display/IDisplayManager.aidl
index 35663af..b3af52b 100644
--- a/core/java/android/hardware/display/IDisplayManager.aidl
+++ b/core/java/android/hardware/display/IDisplayManager.aidl
@@ -168,6 +168,7 @@
// Requires MODIFY_USER_PREFERRED_DISPLAY_MODE permission.
void setUserPreferredDisplayMode(int displayId, in Mode mode);
Mode getUserPreferredDisplayMode(int displayId);
+ Mode getSystemPreferredDisplayMode(int displayId);
// When enabled the app requested display resolution and refresh rate is always selected
// in DisplayModeDirector regardless of user settings and policies for low brightness, low
diff --git a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
index e7d76f6..bb34646 100644
--- a/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
+++ b/core/java/android/hardware/hdmi/HdmiAudioSystemClient.java
@@ -142,7 +142,7 @@
* @hide
*/
public void setSystemAudioMode(boolean state, @NonNull SetSystemAudioModeCallback callback) {
- // TODO(amyjojo): implement this when needed.
+ // TODO(b/217509829): implement this when needed.
}
/**
diff --git a/core/java/android/os/BatteryStatsManager.java b/core/java/android/os/BatteryStatsManager.java
index f16bbc6..071bdea 100644
--- a/core/java/android/os/BatteryStatsManager.java
+++ b/core/java/android/os/BatteryStatsManager.java
@@ -24,8 +24,6 @@
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
-import android.bluetooth.annotations.RequiresBluetoothConnectPermission;
-import android.bluetooth.annotations.RequiresLegacyBluetoothAdminPermission;
import android.content.Context;
import android.net.NetworkStack;
import android.os.connectivity.CellularBatteryStats;
@@ -523,8 +521,6 @@
* @param reason why Bluetooth has been turned on
* @param packageName package responsible for this change
*/
- @RequiresLegacyBluetoothAdminPermission
- @RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void reportBluetoothOn(int uid, int reason, @NonNull String packageName) {
try {
@@ -541,8 +537,6 @@
* @param reason why Bluetooth has been turned on
* @param packageName package responsible for this change
*/
- @RequiresLegacyBluetoothAdminPermission
- @RequiresBluetoothConnectPermission
@RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT)
public void reportBluetoothOff(int uid, int reason, @NonNull String packageName) {
try {
diff --git a/core/java/android/os/BinderProxy.java b/core/java/android/os/BinderProxy.java
index 1b7c00c..6330661 100644
--- a/core/java/android/os/BinderProxy.java
+++ b/core/java/android/os/BinderProxy.java
@@ -526,12 +526,15 @@
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
- if (mWarnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
+ boolean warnOnBlocking = mWarnOnBlocking; // Cache it to reduce volatile access.
+
+ if (warnOnBlocking && ((flags & FLAG_ONEWAY) == 0)
&& Binder.sWarnOnBlockingOnCurrentThread.get()) {
// For now, avoid spamming the log by disabling after we've logged
// about this interface at least once
mWarnOnBlocking = false;
+ warnOnBlocking = false;
if (Build.IS_USERDEBUG) {
// Log this as a WTF on userdebug builds.
@@ -578,7 +581,13 @@
}
try {
- return transactNative(code, data, reply, flags);
+ final boolean result = transactNative(code, data, reply, flags);
+
+ if (reply != null && !warnOnBlocking) {
+ reply.addFlags(Parcel.FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT);
+ }
+
+ return result;
} finally {
AppOpsManager.resumeNotedAppOpsCollection(prevCollection);
diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java
index 35b9ccc..9970641 100755
--- a/core/java/android/os/Build.java
+++ b/core/java/android/os/Build.java
@@ -31,6 +31,7 @@
import android.sysprop.SocProperties;
import android.sysprop.TelephonyProperties;
import android.text.TextUtils;
+import android.util.ArraySet;
import android.util.Slog;
import android.view.View;
@@ -39,6 +40,7 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.stream.Collectors;
/**
@@ -396,6 +398,17 @@
*/
public static final String CODENAME = getString("ro.build.version.codename");
+ /**
+ * All known codenames starting from {@link VERSION_CODES.Q}.
+ *
+ * <p>This includes in development codenames as well.
+ *
+ * @hide
+ */
+ @SystemApi
+ @NonNull public static final Set<String> KNOWN_CODENAMES =
+ new ArraySet<>(new String[]{"Q", "R", "S", "Sv2", "Tiramisu"});
+
private static final String[] ALL_CODENAMES
= getStringList("ro.build.version.all_codenames", ",");
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 321b364..9998e12 100644
--- a/core/java/android/os/Parcel.java
+++ b/core/java/android/os/Parcel.java
@@ -18,6 +18,7 @@
import static java.util.Objects.requireNonNull;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
@@ -53,6 +54,8 @@
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
@@ -229,6 +232,25 @@
private RuntimeException mStack;
+ /** @hide */
+ @TestApi
+ public static final int FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT = 1 << 0;
+
+ /** @hide */
+ @TestApi
+ public static final int FLAG_PROPAGATE_ALLOW_BLOCKING = 1 << 1;
+
+ /** @hide */
+ @IntDef(flag = true, prefix = { "FLAG_" }, value = {
+ FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT,
+ FLAG_PROPAGATE_ALLOW_BLOCKING,
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ParcelFlags {}
+
+ @ParcelFlags
+ private int mFlags;
+
/**
* Whether or not to parcel the stack trace of an exception. This has a performance
* impact, so should only be included in specific processes and only on debug builds.
@@ -585,6 +607,40 @@
nativeMarkForBinder(mNativePtr, binder);
}
+ /** @hide */
+ @ParcelFlags
+ @TestApi
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /** @hide */
+ public void setFlags(@ParcelFlags int flags) {
+ mFlags = flags;
+ }
+
+ /** @hide */
+ public void addFlags(@ParcelFlags int flags) {
+ mFlags |= flags;
+ }
+
+ /** @hide */
+ private boolean hasFlags(@ParcelFlags int flags) {
+ return (mFlags & flags) == flags;
+ }
+
+ /**
+ * This method is used by the AIDL compiler for system components. Not intended to be
+ * used by non-system apps.
+ */
+ // Note: Ideally this method should be @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES),
+ // but we need to make this method public due to the way the aidl compiler is compiled.
+ // We don't really need to protect it; even if 3p / non-system apps, nothing would happen.
+ // This would only work when used on a reply parcel by a binder object that's allowed-blocking.
+ public void setPropagateAllowBlocking() {
+ addFlags(FLAG_PROPAGATE_ALLOW_BLOCKING);
+ }
+
/**
* Returns the total amount of data contained in the parcel.
*/
@@ -3045,7 +3101,15 @@
* Read an object from the parcel at the current dataPosition().
*/
public final IBinder readStrongBinder() {
- return nativeReadStrongBinder(mNativePtr);
+ final IBinder result = nativeReadStrongBinder(mNativePtr);
+
+ // If it's a reply from a method with @PropagateAllowBlocking, then inherit allow-blocking
+ // from the object that returned it.
+ if (result != null && hasFlags(
+ FLAG_IS_REPLY_FROM_BLOCKING_ALLOWED_OBJECT | FLAG_PROPAGATE_ALLOW_BLOCKING)) {
+ Binder.allowBlocking(result);
+ }
+ return result;
}
/**
@@ -4995,6 +5059,7 @@
}
private void freeBuffer() {
+ mFlags = 0;
resetSqaushingState();
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
diff --git a/core/java/android/os/logcat/ILogcatManagerService.aidl b/core/java/android/os/logcat/ILogcatManagerService.aidl
index 68b5679..02db274 100644
--- a/core/java/android/os/logcat/ILogcatManagerService.aidl
+++ b/core/java/android/os/logcat/ILogcatManagerService.aidl
@@ -22,5 +22,7 @@
interface ILogcatManagerService {
void startThread(in int uid, in int gid, in int pid, in int fd);
void finishThread(in int uid, in int gid, in int pid, in int fd);
+ void approve(in int uid, in int gid, in int pid, in int fd);
+ void decline(in int uid, in int gid, in int pid, in int fd);
}
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
index fa39380..246a8c9 100644
--- a/core/java/android/view/Display.java
+++ b/core/java/android/view/Display.java
@@ -1118,6 +1118,19 @@
}
/**
+ * Returns the system's preferred display mode. This mode will be used when the user has not
+ * specified a display-mode preference. This returns null if the boot display mode feature is
+ * not supported by system.
+ *
+ * @hide
+ */
+ @TestApi
+ @Nullable
+ public Display.Mode getSystemPreferredDisplayMode() {
+ return mGlobal.getSystemPreferredDisplayMode(getDisplayId());
+ }
+
+ /**
* Returns the display's HDR capabilities.
*
* @see #isHdr()
diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java
index 05c312b..f3ca531 100644
--- a/core/java/android/view/OnBackInvokedDispatcher.java
+++ b/core/java/android/view/OnBackInvokedDispatcher.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.SuppressLint;
+import android.os.Build;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -32,6 +33,13 @@
* target (a.k.a. the callback to be invoked next), or its behavior.
*/
public abstract class OnBackInvokedDispatcher {
+
+ /** @hide */
+ public static final String TAG = "OnBackInvokedDispatcher";
+
+ /** @hide */
+ public static final boolean DEBUG = Build.isDebuggable();
+
/** @hide */
@IntDef({
PRIORITY_DEFAULT,
diff --git a/core/java/android/view/OnBackInvokedDispatcherOwner.java b/core/java/android/view/OnBackInvokedDispatcherOwner.java
index 0e14ed4..e69efe0 100644
--- a/core/java/android/view/OnBackInvokedDispatcherOwner.java
+++ b/core/java/android/view/OnBackInvokedDispatcherOwner.java
@@ -16,7 +16,7 @@
package android.view;
-import android.annotation.Nullable;
+import android.annotation.NonNull;
/**
* A class that provides an {@link OnBackInvokedDispatcher} that allows you to register
@@ -28,6 +28,6 @@
* to its registered {@link OnBackInvokedCallback}s.
* Returns null when the root view is not attached to a window or a view tree with a decor.
*/
- @Nullable
+ @NonNull
OnBackInvokedDispatcher getOnBackInvokedDispatcher();
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 904d7c8..6f5fea2 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -197,6 +197,9 @@
private static native int[] nativeGetCompositionDataspaces();
private static native boolean nativeSetActiveColorMode(IBinder displayToken,
int colorMode);
+ private static native boolean nativeGetBootDisplayModeSupport();
+ private static native void nativeSetBootDisplayMode(IBinder displayToken, int displayMode);
+ private static native void nativeClearBootDisplayMode(IBinder displayToken);
private static native void nativeSetAutoLowLatencyMode(IBinder displayToken, boolean on);
private static native void nativeSetGameContentType(IBinder displayToken, boolean on);
private static native void nativeSetDisplayPowerMode(
@@ -1878,6 +1881,8 @@
public boolean autoLowLatencyModeSupported;
public boolean gameContentTypeSupported;
+ public int preferredBootDisplayMode;
+
@Override
public String toString() {
return "DynamicDisplayInfo{"
@@ -1887,7 +1892,8 @@
+ ", activeColorMode=" + activeColorMode
+ ", hdrCapabilities=" + hdrCapabilities
+ ", autoLowLatencyModeSupported=" + autoLowLatencyModeSupported
- + ", gameContentTypeSupported" + gameContentTypeSupported + "}";
+ + ", gameContentTypeSupported" + gameContentTypeSupported
+ + ", preferredBootDisplayMode" + preferredBootDisplayMode + "}";
}
@Override
@@ -1899,7 +1905,8 @@
&& activeDisplayModeId == that.activeDisplayModeId
&& Arrays.equals(supportedColorModes, that.supportedColorModes)
&& activeColorMode == that.activeColorMode
- && Objects.equals(hdrCapabilities, that.hdrCapabilities);
+ && Objects.equals(hdrCapabilities, that.hdrCapabilities)
+ && preferredBootDisplayMode == that.preferredBootDisplayMode;
}
@Override
@@ -2266,6 +2273,36 @@
/**
* @hide
*/
+ public static boolean getBootDisplayModeSupport() {
+ return nativeGetBootDisplayModeSupport();
+ }
+
+ /** There is no associated getter for this method. When this is set, the display is expected
+ * to start up in this mode next time the device reboots.
+ * @hide
+ */
+ public static void setBootDisplayMode(IBinder displayToken, int displayModeId) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeSetBootDisplayMode(displayToken, displayModeId);
+ }
+
+ /**
+ * @hide
+ */
+ public static void clearBootDisplayMode(IBinder displayToken) {
+ if (displayToken == null) {
+ throw new IllegalArgumentException("displayToken must not be null");
+ }
+
+ nativeClearBootDisplayMode(displayToken);
+ }
+
+ /**
+ * @hide
+ */
public static void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
if (displayToken == null) {
throw new IllegalArgumentException("displayToken must not be null");
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ff7e229..36a97e4c 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -834,7 +834,7 @@
*/
@UiThread
public class View implements Drawable.Callback, KeyEvent.Callback,
- AccessibilityEventSource, OnBackInvokedDispatcherOwner {
+ AccessibilityEventSource {
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
private static final boolean DBG = false;
@@ -31447,23 +31447,4 @@
}
return null;
}
-
- /**
- * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to.
- *
- * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached
- * to a window or a view tree with a decor.
- */
- @Nullable
- public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- ViewParent parent = getParent();
- if (parent instanceof View) {
- return ((View) parent).getOnBackInvokedDispatcher();
- } else if (parent instanceof ViewRootImpl) {
- // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have
- // a {@link com.android.internal.policy.DecorView}.
- return ((ViewRootImpl) parent).getOnBackInvokedDispatcher();
- }
- return null;
- }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 386b277..3d86535 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -236,7 +236,7 @@
@SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"})
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,
- AttachedSurfaceControl {
+ AttachedSurfaceControl, OnBackInvokedDispatcherOwner {
private static final String TAG = "ViewRootImpl";
private static final boolean DBG = false;
private static final boolean LOCAL_LOGV = false;
@@ -313,9 +313,9 @@
private @SurfaceControl.BufferTransform
int mPreviousTransformHint = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
/**
- * The fallback {@link OnBackInvokedDispatcher} when the window doesn't have a decor view.
+ * The top level {@link OnBackInvokedDispatcher}.
*/
- private WindowOnBackInvokedDispatcher mFallbackOnBackInvokedDispatcher =
+ private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher =
new WindowOnBackInvokedDispatcher();
/**
@@ -893,7 +893,6 @@
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
- mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1144,9 +1143,6 @@
if (pendingInsetsController != null) {
pendingInsetsController.replayAndAttach(mInsetsController);
}
- ((RootViewSurfaceTaker) mView)
- .provideWindowOnBackInvokedDispatcher()
- .attachToWindow(mWindowSession, mWindow);
}
try {
@@ -1193,6 +1189,7 @@
getAttachedWindowFrame(), 1f /* compactScale */,
mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame);
setFrame(mTmpFrames.frame);
+ registerBackCallbackOnWindow();
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -8417,6 +8414,7 @@
mAdded = false;
}
+ mOnBackInvokedDispatcher.detachFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -10771,12 +10769,17 @@
* Returns the {@link OnBackInvokedDispatcher} on the decor view if one exists, or the
* fallback {@link OnBackInvokedDispatcher} instance.
*/
- @Nullable
- public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- if (mView instanceof RootViewSurfaceTaker) {
- return ((RootViewSurfaceTaker) mView).provideWindowOnBackInvokedDispatcher();
- }
- return mFallbackOnBackInvokedDispatcher;
+ @NonNull
+ public WindowOnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ return mOnBackInvokedDispatcher;
+ }
+
+ /**
+ * When this ViewRootImpl is added to the window manager, transfers the first
+ * {@link OnBackInvokedCallback} to be called to the server.
+ */
+ private void registerBackCallbackOnWindow() {
+ mOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}
@Override
diff --git a/core/java/android/window/BackNavigationInfo.java b/core/java/android/window/BackNavigationInfo.java
index 571714c..18c20e2 100644
--- a/core/java/android/window/BackNavigationInfo.java
+++ b/core/java/android/window/BackNavigationInfo.java
@@ -16,8 +16,6 @@
package android.window;
-import static java.util.Objects.requireNonNull;
-
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -61,6 +59,12 @@
public static final int TYPE_CROSS_TASK = 3;
/**
+ * A {@link android.view.OnBackInvokedCallback} is available and needs to be called.
+ * <p>
+ */
+ public static final int TYPE_CALLBACK = 4;
+
+ /**
* Defines the type of back destinations a back even can lead to. This is used to define the
* type of animation that need to be run on SystemUI.
*/
@@ -84,35 +88,39 @@
private final RemoteCallback mRemoteCallback;
@Nullable
private final WindowConfiguration mTaskWindowConfiguration;
+ @Nullable
+ private final IOnBackInvokedCallback mOnBackInvokedCallback;
/**
* Create a new {@link BackNavigationInfo} instance.
*
- * @param type The {@link BackTargetType} of the destination (what will be displayed after
- * the back action)
- * @param topWindowLeash The leash to animate away the current topWindow. The consumer
- * of the leash is responsible for removing it.
- * @param screenshotSurface The screenshot of the previous activity to be displayed.
- * @param screenshotBuffer A buffer containing a screenshot used to display the activity.
- * See {@link #getScreenshotHardwareBuffer()} for information
- * about nullity.
- * @param taskWindowConfiguration The window configuration of the Task being animated
- * beneath.
- * @param onBackNavigationDone The callback to be called once the client is done with the back
- * preview.
+ * @param type The {@link BackTargetType} of the destination (what will be
+ * displayed after the back action).
+ * @param topWindowLeash The leash to animate away the current topWindow. The consumer
+ * of the leash is responsible for removing it.
+ * @param screenshotSurface The screenshot of the previous activity to be displayed.
+ * @param screenshotBuffer A buffer containing a screenshot used to display the activity.
+ * See {@link #getScreenshotHardwareBuffer()} for information
+ * about nullity.
+ * @param taskWindowConfiguration The window configuration of the Task being animated beneath.
+ * @param onBackNavigationDone The callback to be called once the client is done with the
+ * back preview.
+ * @param onBackInvokedCallback The back callback registered by the current top level window.
*/
public BackNavigationInfo(@BackTargetType int type,
@Nullable SurfaceControl topWindowLeash,
@Nullable SurfaceControl screenshotSurface,
@Nullable HardwareBuffer screenshotBuffer,
@Nullable WindowConfiguration taskWindowConfiguration,
- @NonNull RemoteCallback onBackNavigationDone) {
+ @Nullable RemoteCallback onBackNavigationDone,
+ @Nullable IOnBackInvokedCallback onBackInvokedCallback) {
mType = type;
mDepartingWindowContainer = topWindowLeash;
mScreenshotSurface = screenshotSurface;
mScreenshotBuffer = screenshotBuffer;
mTaskWindowConfiguration = taskWindowConfiguration;
mRemoteCallback = onBackNavigationDone;
+ mOnBackInvokedCallback = onBackInvokedCallback;
}
private BackNavigationInfo(@NonNull Parcel in) {
@@ -121,7 +129,8 @@
mScreenshotSurface = in.readTypedObject(SurfaceControl.CREATOR);
mScreenshotBuffer = in.readTypedObject(HardwareBuffer.CREATOR);
mTaskWindowConfiguration = in.readTypedObject(WindowConfiguration.CREATOR);
- mRemoteCallback = requireNonNull(in.readTypedObject(RemoteCallback.CREATOR));
+ mRemoteCallback = in.readTypedObject(RemoteCallback.CREATOR);
+ mOnBackInvokedCallback = IOnBackInvokedCallback.Stub.asInterface(in.readStrongBinder());
}
@Override
@@ -132,10 +141,12 @@
dest.writeTypedObject(mScreenshotBuffer, flags);
dest.writeTypedObject(mTaskWindowConfiguration, flags);
dest.writeTypedObject(mRemoteCallback, flags);
+ dest.writeStrongInterface(mOnBackInvokedCallback);
}
/**
* Returns the type of back navigation that is about to happen.
+ *
* @see BackTargetType
*/
public @BackTargetType int getType() {
@@ -152,8 +163,8 @@
}
/**
- * Returns the {@link SurfaceControl} that should be used to display a screenshot of the
- * previous activity.
+ * Returns the {@link SurfaceControl} that should be used to display a screenshot of the
+ * previous activity.
*/
@Nullable
public SurfaceControl getScreenshotSurface() {
@@ -185,11 +196,27 @@
}
/**
+ * Returns the {@link android.view.OnBackInvokedCallback} of the top level window or null if
+ * the client didn't register a callback.
+ * <p>
+ * This is never null when {@link #getType} returns {@link #TYPE_CALLBACK}.
+ *
+ * @see android.view.OnBackInvokedCallback
+ * @see android.view.OnBackInvokedDispatcher
+ */
+ @Nullable
+ public IOnBackInvokedCallback getOnBackInvokedCallback() {
+ return mOnBackInvokedCallback;
+ }
+
+ /**
* Callback to be called when the back preview is finished in order to notify the server that
* it can clean up the resources created for the animation.
*/
public void onBackNavigationFinished() {
- mRemoteCallback.sendResult(null);
+ if (mRemoteCallback != null) {
+ mRemoteCallback.sendResult(null);
+ }
}
@Override
@@ -218,6 +245,7 @@
+ ", mTaskWindowConfiguration= " + mTaskWindowConfiguration
+ ", mScreenshotBuffer=" + mScreenshotBuffer
+ ", mRemoteCallback=" + mRemoteCallback
+ + ", mOnBackInvokedCallback=" + mOnBackInvokedCallback
+ '}';
}
@@ -226,7 +254,7 @@
*/
public static String typeToString(@BackTargetType int type) {
switch (type) {
- case TYPE_UNDEFINED:
+ case TYPE_UNDEFINED:
return "TYPE_UNDEFINED";
case TYPE_DIALOG_CLOSE:
return "TYPE_DIALOG_CLOSE";
diff --git a/core/java/android/window/ProxyOnBackInvokedDispatcher.java b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
new file mode 100644
index 0000000..509bbd4
--- /dev/null
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -0,0 +1,162 @@
+/*
+ * 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 android.window;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.util.Log;
+import android.util.Pair;
+import android.view.OnBackInvokedCallback;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link OnBackInvokedDispatcher} only used to hold callbacks while an actual
+ * dispatcher becomes available. <b>It does not dispatch the back events</b>.
+ * <p>
+ * Once the actual {@link OnBackInvokedDispatcherOwner} becomes available,
+ * {@link #setActualDispatcherOwner(OnBackInvokedDispatcherOwner)} needs to
+ * be called and this {@link ProxyOnBackInvokedDispatcher} will pass the callback registrations
+ * onto it.
+ * <p>
+ * This dispatcher will continue to keep track of callback registrations and when a dispatcher is
+ * removed or set it will unregister the callbacks from the old one and register them on the new
+ * one unless {@link #reset()} is called before.
+ *
+ * @hide
+ */
+public class ProxyOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+
+ /**
+ * List of pair representing an {@link OnBackInvokedCallback} and its associated priority.
+ *
+ * @see OnBackInvokedDispatcher#registerOnBackInvokedCallback(OnBackInvokedCallback, int)
+ */
+ private final List<Pair<OnBackInvokedCallback, Integer>> mCallbacks = new ArrayList<>();
+ private final Object mLock = new Object();
+ private OnBackInvokedDispatcherOwner mActualDispatcherOwner = null;
+
+ @Override
+ public void registerOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback, int priority) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("Pending register %s. Actual=%s", callback,
+ mActualDispatcherOwner));
+ }
+ synchronized (mLock) {
+ mCallbacks.add(Pair.create(callback, priority));
+ if (mActualDispatcherOwner != null) {
+ mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ callback, priority);
+ }
+
+ }
+ }
+
+ @Override
+ public void unregisterOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("Pending unregister %s. Actual=%s", callback,
+ mActualDispatcherOwner));
+ }
+ synchronized (mLock) {
+ mCallbacks.removeIf((p) -> p.first.equals(callback));
+ }
+ }
+
+ /**
+ * Transfers all the pending callbacks to the provided dispatcher.
+ * <p>
+ * The callbacks are registered on the dispatcher in the same order as they were added on this
+ * proxy dispatcher.
+ */
+ private void transferCallbacksToDispatcher() {
+ if (mActualDispatcherOwner == null) {
+ return;
+ }
+ OnBackInvokedDispatcher dispatcher =
+ mActualDispatcherOwner.getOnBackInvokedDispatcher();
+ if (DEBUG) {
+ Log.v(TAG, String.format("Pending transferring %d callbacks to %s", mCallbacks.size(),
+ dispatcher));
+ }
+ for (Pair<OnBackInvokedCallback, Integer> callbackPair : mCallbacks) {
+ dispatcher.registerOnBackInvokedCallback(callbackPair.first,
+ callbackPair.second);
+ }
+ mCallbacks.clear();
+ }
+
+ private void clearCallbacksOnDispatcher() {
+ if (mActualDispatcherOwner == null) {
+ return;
+ }
+ OnBackInvokedDispatcher onBackInvokedDispatcher =
+ mActualDispatcherOwner.getOnBackInvokedDispatcher();
+ for (Pair<OnBackInvokedCallback, Integer> callback : mCallbacks) {
+ onBackInvokedDispatcher.unregisterOnBackInvokedCallback(callback.first);
+ }
+ }
+
+ /**
+ * Resets this {@link ProxyOnBackInvokedDispatcher} so it loses track of the currently
+ * registered callbacks.
+ * <p>
+ * Using this method means that when setting a new {@link OnBackInvokedDispatcherOwner}, the
+ * callbacks registered on the old one won't be removed from it and won't be registered on
+ * the new one.
+ */
+ public void reset() {
+ if (DEBUG) {
+ Log.v(TAG, "Pending reset callbacks");
+ }
+ synchronized (mLock) {
+ mCallbacks.clear();
+ }
+ }
+
+ /**
+ * Sets the actual {@link OnBackInvokedDispatcherOwner} that will provides the
+ * {@link OnBackInvokedDispatcher} onto which the callbacks will be registered.
+ * <p>
+ * If any dispatcher owner was already present, all the callbacks that were added via this
+ * {@link ProxyOnBackInvokedDispatcher} will be unregistered from the old one and registered
+ * on the new one if it is not null.
+ * <p>
+ * If you do not wish for the previously registered callbacks to be reassigned to the new
+ * dispatcher, {@link #reset} must be called beforehand.
+ */
+ public void setActualDispatcherOwner(
+ @Nullable OnBackInvokedDispatcherOwner actualDispatcherOwner) {
+ if (DEBUG) {
+ Log.v(TAG, String.format("Pending setActual %s. Current %s",
+ actualDispatcherOwner, mActualDispatcherOwner));
+ }
+ synchronized (mLock) {
+ if (actualDispatcherOwner == mActualDispatcherOwner) {
+ return;
+ }
+ clearCallbacksOnDispatcher();
+ mActualDispatcherOwner = actualDispatcherOwner;
+ transferCallbacksToDispatcher();
+ }
+ }
+}
diff --git a/core/java/com/android/internal/app/ResolverActivity.java b/core/java/com/android/internal/app/ResolverActivity.java
index 347153c..cdb69e5 100644
--- a/core/java/com/android/internal/app/ResolverActivity.java
+++ b/core/java/com/android/internal/app/ResolverActivity.java
@@ -1011,7 +1011,9 @@
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
ViewPager viewPager = findViewById(R.id.profile_pager);
- outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+ if (viewPager != null) {
+ outState.putInt(LAST_SHOWN_TAB_KEY, viewPager.getCurrentItem());
+ }
}
@Override
@@ -1019,7 +1021,9 @@
super.onRestoreInstanceState(savedInstanceState);
resetButtonBar();
ViewPager viewPager = findViewById(R.id.profile_pager);
- viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+ if (viewPager != null) {
+ viewPager.setCurrentItem(savedInstanceState.getInt(LAST_SHOWN_TAB_KEY));
+ }
mMultiProfilePagerAdapter.clearInactiveProfileCache();
}
@@ -1568,6 +1572,11 @@
rebuildCompleted = rebuildCompleted && rebuildInactiveCompleted;
}
+ if (shouldUseMiniResolver()) {
+ configureMiniResolverContent();
+ return false;
+ }
+
if (useLayoutWithDefault()) {
mLayoutId = R.layout.resolver_list_with_default;
} else {
@@ -1578,6 +1587,72 @@
return postRebuildList(rebuildCompleted);
}
+ private void configureMiniResolverContent() {
+ mLayoutId = R.layout.miniresolver;
+ setContentView(mLayoutId);
+
+ DisplayResolveInfo sameProfileResolveInfo =
+ mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList.get(0);
+ boolean inWorkProfile = getCurrentProfile() == PROFILE_WORK;
+
+ DisplayResolveInfo otherProfileResolveInfo =
+ mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList.get(0);
+ ImageView icon = findViewById(R.id.icon);
+ // TODO: Set icon drawable to app icon.
+
+ ((TextView) findViewById(R.id.open_cross_profile)).setText(
+ getResources().getString(
+ inWorkProfile ? R.string.miniresolver_open_in_personal
+ : R.string.miniresolver_open_in_work,
+ otherProfileResolveInfo.getDisplayLabel()));
+ ((Button) findViewById(R.id.use_same_profile_browser)).setText(
+ inWorkProfile ? R.string.miniresolver_use_work_browser
+ : R.string.miniresolver_use_personal_browser);
+
+ findViewById(R.id.use_same_profile_browser).setOnClickListener(
+ v -> safelyStartActivity(sameProfileResolveInfo));
+
+ findViewById(R.id.button_open).setOnClickListener(v -> {
+ Intent intent = otherProfileResolveInfo.getResolvedIntent();
+ if (intent != null) {
+ prepareIntentForCrossProfileLaunch(intent);
+ }
+ safelyStartActivityInternal(otherProfileResolveInfo,
+ mMultiProfilePagerAdapter.getInactiveListAdapter().mResolverListController
+ .getUserHandle());
+ });
+ }
+
+ private boolean shouldUseMiniResolver() {
+ if (mMultiProfilePagerAdapter.getActiveListAdapter() == null
+ || mMultiProfilePagerAdapter.getInactiveListAdapter() == null) {
+ return false;
+ }
+ List<DisplayResolveInfo> sameProfileList =
+ mMultiProfilePagerAdapter.getActiveListAdapter().mDisplayList;
+ List<DisplayResolveInfo> otherProfileList =
+ mMultiProfilePagerAdapter.getInactiveListAdapter().mDisplayList;
+
+ if (otherProfileList.size() != 1) {
+ Log.d(TAG, "Found " + otherProfileList.size() + " resolvers in the other profile");
+ return false;
+ }
+
+ if (otherProfileList.get(0).getResolveInfo().handleAllWebDataURI) {
+ Log.d(TAG, "Other profile is a web browser");
+ return false;
+ }
+
+ for (DisplayResolveInfo info : sameProfileList) {
+ if (!info.getResolveInfo().handleAllWebDataURI) {
+ Log.d(TAG, "Non-browser found in this profile");
+ return false;
+ }
+ }
+
+ return true;
+ }
+
/**
* Finishing procedures to be performed after the list has been rebuilt.
* </p>Subclasses must call postRebuildListInternal at the end of postRebuildList.
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index c0fec62..5ba45c9 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -607,11 +607,15 @@
int UPDATE_BT = 0x08;
int UPDATE_RPM = 0x10;
int UPDATE_DISPLAY = 0x20;
+ int RESET = 0x40;
+
int UPDATE_ALL =
UPDATE_CPU | UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT | UPDATE_RPM | UPDATE_DISPLAY;
int UPDATE_ON_PROC_STATE_CHANGE = UPDATE_WIFI | UPDATE_RADIO | UPDATE_BT;
+ int UPDATE_ON_RESET = UPDATE_ALL | RESET;
+
@IntDef(flag = true, prefix = "UPDATE_", value = {
UPDATE_CPU,
UPDATE_WIFI,
@@ -12909,7 +12913,7 @@
// Flush external data, gathering snapshots, but don't process it since it is pre-reset data
mIgnoreNextExternalStats = true;
- mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ALL);
+ mExternalSync.scheduleSync("reset", ExternalStatsSync.UPDATE_ON_RESET);
mHandler.sendEmptyMessage(MSG_REPORT_RESET_STATS);
}
diff --git a/core/java/com/android/internal/policy/DecorView.java b/core/java/com/android/internal/policy/DecorView.java
index 2925341..40e4085 100644
--- a/core/java/com/android/internal/policy/DecorView.java
+++ b/core/java/com/android/internal/policy/DecorView.java
@@ -87,7 +87,6 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
-import android.view.OnBackInvokedDispatcher;
import android.view.PendingInsetsController;
import android.view.ThreadedRenderer;
import android.view.View;
@@ -109,7 +108,6 @@
import android.view.animation.Interpolator;
import android.widget.FrameLayout;
import android.widget.PopupWindow;
-import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
@@ -297,7 +295,6 @@
return true;
};
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
- private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
@@ -326,7 +323,6 @@
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
- mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher();
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1880,7 +1876,6 @@
}
mPendingInsetsController.detach();
- mOnBackInvokedDispatcher.detachFromWindow();
}
@Override
@@ -1925,11 +1920,6 @@
return mPendingInsetsController;
}
- @Override
- public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() {
- return mOnBackInvokedDispatcher;
- }
-
private ActionMode createActionMode(
int type, ActionMode.Callback2 callback, View originatingView) {
switch (type) {
@@ -2384,7 +2374,6 @@
}
}
}
- mOnBackInvokedDispatcher.clear();
}
@Override
@@ -2666,15 +2655,6 @@
}
}
- /**
- * Returns the {@link OnBackInvokedDispatcher} on the decor view.
- */
- @Override
- @Nullable
- public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- return mOnBackInvokedDispatcher;
- }
-
@Override
public String toString() {
return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
diff --git a/core/java/com/android/internal/policy/PhoneWindow.java b/core/java/com/android/internal/policy/PhoneWindow.java
index 7755b69..12f38a4 100644
--- a/core/java/com/android/internal/policy/PhoneWindow.java
+++ b/core/java/com/android/internal/policy/PhoneWindow.java
@@ -91,6 +91,8 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedDispatcher;
+import android.view.OnBackInvokedDispatcherOwner;
import android.view.ScrollCaptureCallback;
import android.view.SearchEvent;
import android.view.SurfaceHolder.Callback2;
@@ -110,6 +112,7 @@
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
+import android.window.ProxyOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.view.menu.ContextMenuBuilder;
@@ -134,7 +137,8 @@
*
* @hide
*/
-public class PhoneWindow extends Window implements MenuBuilder.Callback {
+public class PhoneWindow extends Window implements MenuBuilder.Callback,
+ OnBackInvokedDispatcherOwner {
private final static String TAG = "PhoneWindow";
@@ -340,6 +344,9 @@
boolean mDecorFitsSystemWindows = true;
+ private ProxyOnBackInvokedDispatcher mProxyOnBackInvokedDispatcher =
+ new ProxyOnBackInvokedDispatcher();
+
static class WindowManagerHolder {
static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
@@ -2146,6 +2153,7 @@
/** Notify when decor view is attached to window and {@link ViewRootImpl} is available. */
void onViewRootImplSet(ViewRootImpl viewRoot) {
viewRoot.setActivityConfigCallback(mActivityConfigCallback);
+ mProxyOnBackInvokedDispatcher.setActualDispatcherOwner(viewRoot);
applyDecorFitsSystemWindows();
}
@@ -3993,4 +4001,10 @@
public AttachedSurfaceControl getRootSurfaceControl() {
return getViewRootImplOrNull();
}
+
+ @NonNull
+ @Override
+ public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
+ return mProxyOnBackInvokedDispatcher;
+ }
}
diff --git a/core/java/com/android/internal/power/MeasuredEnergyStats.java b/core/java/com/android/internal/power/MeasuredEnergyStats.java
index a52ae10..7262e84 100644
--- a/core/java/com/android/internal/power/MeasuredEnergyStats.java
+++ b/core/java/com/android/internal/power/MeasuredEnergyStats.java
@@ -438,10 +438,16 @@
mState = state;
mStateChangeTimestampMs = timestampMs;
if (mAccumulatedMultiStateChargeMicroCoulomb == null) {
- return;
+ mAccumulatedMultiStateChargeMicroCoulomb =
+ new LongMultiStateCounter[mAccumulatedChargeMicroCoulomb.length];
}
for (int i = 0; i < mAccumulatedMultiStateChargeMicroCoulomb.length; i++) {
LongMultiStateCounter counter = mAccumulatedMultiStateChargeMicroCoulomb[i];
+ if (counter == null && mConfig.isSupportedMultiStateBucket(i)) {
+ counter = new LongMultiStateCounter(mConfig.mStateNames.length);
+ counter.updateValue(0, timestampMs);
+ mAccumulatedMultiStateChargeMicroCoulomb[i] = counter;
+ }
if (counter != null) {
counter.setState(state, timestampMs);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl
index 23ebc9f..51eb429 100644
--- a/core/java/com/android/internal/statusbar/IStatusBar.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl
@@ -24,12 +24,14 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.service.notification.StatusBarNotification;
import android.view.InsetsVisibilities;
import com.android.internal.statusbar.IAddTileResultCallback;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.view.AppearanceRegion;
@@ -296,4 +298,15 @@
void requestAddTile(in ComponentName componentName, in CharSequence appName, in CharSequence label, in Icon icon, in IAddTileResultCallback callback);
void cancelRequestAddTile(in String packageName);
+
+ /** Notifies System UI about an update to the media tap-to-transfer sender state. */
+ void updateMediaTapToTransferSenderDisplay(
+ int displayState,
+ in MediaRoute2Info routeInfo,
+ in IUndoMediaTransferCallback undoCallback);
+
+ /** Notifies System UI about an update to the media tap-to-transfer receiver state. */
+ void updateMediaTapToTransferReceiverDisplay(
+ int displayState,
+ in MediaRoute2Info routeInfo);
}
diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
index f28325e..0c45e5b 100644
--- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl
+++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl
@@ -24,6 +24,7 @@
import android.hardware.biometrics.IBiometricSysuiReceiver;
import android.hardware.biometrics.PromptInfo;
import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserHandle;
@@ -33,6 +34,7 @@
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.statusbar.StatusBarIconList;
@@ -196,4 +198,15 @@
*/
void onSessionStarted(int sessionType, in InstanceId instanceId);
void onSessionEnded(int sessionType, in InstanceId instanceId);
+
+ /** Notifies System UI about an update to the media tap-to-transfer sender state. */
+ void updateMediaTapToTransferSenderDisplay(
+ int displayState,
+ in MediaRoute2Info routeInfo,
+ in IUndoMediaTransferCallback undoCallback);
+
+ /** Notifies System UI about an update to the media tap-to-transfer receiver state. */
+ void updateMediaTapToTransferReceiverDisplay(
+ int displayState,
+ in MediaRoute2Info routeInfo);
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
similarity index 68%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
rename to core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
index b47be87..3dd2980 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IUndoTransferCallback.aidl
+++ b/core/java/com/android/internal/statusbar/IUndoMediaTransferCallback.aidl
@@ -14,17 +14,16 @@
* limitations under the License.
*/
-package com.android.systemui.shared.mediattt;
+package com.android.internal.statusbar;
/**
- * An interface that will be invoked by System UI if the user choose to undo a transfer.
- *
- * Other services will implement this interface and System UI will invoke it.
+ * An interface that will be invoked if the user chooses to undo a transfer.
*/
-interface IUndoTransferCallback {
+interface IUndoMediaTransferCallback {
/**
- * Invoked by SystemUI when the user requests to undo the media transfer that just occurred.
+ * Invoked to notify callers that the user has chosen to undo the media transfer that just
+ * occurred.
*
* Implementors of this method are repsonsible for actually undoing the transfer.
*/
diff --git a/core/java/com/android/internal/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index 4b89bf508..3ab9a33 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -15,12 +15,10 @@
*/
package com.android.internal.view;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.InputQueue;
import android.view.PendingInsetsController;
import android.view.SurfaceHolder;
-import android.window.WindowOnBackInvokedDispatcher;
/** hahahah */
public interface RootViewSurfaceTaker {
@@ -31,6 +29,4 @@
InputQueue.Callback willYouTakeTheInputQueue();
void onRootViewScrollYChanged(int scrollY);
@Nullable PendingInsetsController providePendingInsetsController();
- /** @hide */
- @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher();
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 39f17e51..93864fa 100644
--- a/core/java/com/android/server/SystemConfig.java
+++ b/core/java/com/android/server/SystemConfig.java
@@ -237,6 +237,10 @@
// be delivered anonymously even to apps which target O+.
final ArraySet<String> mAllowImplicitBroadcasts = new ArraySet<>();
+ // These are the packages that are exempted from the background restriction applied
+ // by the system automatically, i.e., due to high background current drain.
+ final ArraySet<String> mBgRestrictionExemption = new ArraySet<>();
+
// These are the package names of apps which should be automatically granted domain verification
// for all of their domains. The only way these apps can be overridden by the user is by
// explicitly disabling overall link handling support in app info.
@@ -389,6 +393,10 @@
return mAllowIgnoreLocationSettings;
}
+ public ArraySet<String> getBgRestrictionExemption() {
+ return mBgRestrictionExemption;
+ }
+
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
@@ -1049,6 +1057,20 @@
}
XmlUtils.skipCurrentTag(parser);
} break;
+ case "bg-restriction-exemption": {
+ if (allowOverrideAppRestrictions) {
+ String pkgname = parser.getAttributeValue(null, "package");
+ if (pkgname == null) {
+ Slog.w(TAG, "<" + name + "> without package in "
+ + permFile + " at " + parser.getPositionDescription());
+ } else {
+ mBgRestrictionExemption.add(pkgname);
+ }
+ } else {
+ logNotAllowedInPartition(name, permFile, parser);
+ }
+ XmlUtils.skipCurrentTag(parser);
+ } break;
case "default-enabled-vr-app": {
if (allowAppConfigs) {
String pkgname = parser.getAttributeValue(null, "package");
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a8cf253..9915913 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -104,6 +104,7 @@
jfieldID hdrCapabilities;
jfieldID autoLowLatencyModeSupported;
jfieldID gameContentTypeSupported;
+ jfieldID preferredBootDisplayMode;
} gDynamicDisplayInfoClassInfo;
static struct {
@@ -1301,6 +1302,9 @@
env->SetBooleanField(object, gDynamicDisplayInfoClassInfo.gameContentTypeSupported,
info.gameContentTypeSupported);
+
+ env->SetIntField(object, gDynamicDisplayInfoClassInfo.preferredBootDisplayMode,
+ info.preferredBootDisplayMode);
return object;
}
@@ -1638,6 +1642,27 @@
}
}
+static jboolean nativeGetBootDisplayModeSupport(JNIEnv* env, jclass clazz) {
+ bool isBootDisplayModeSupported = false;
+ SurfaceComposerClient::getBootDisplayModeSupport(&isBootDisplayModeSupported);
+ return static_cast<jboolean>(isBootDisplayModeSupported);
+}
+
+static void nativeSetBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject,
+ jint displayModId) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::setBootDisplayMode(token, displayModId);
+}
+
+static void nativeClearBootDisplayMode(JNIEnv* env, jclass clazz, jobject tokenObject) {
+ sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
+ if (token == NULL) return;
+
+ SurfaceComposerClient::clearBootDisplayMode(token);
+}
+
static void nativeSetAutoLowLatencyMode(JNIEnv* env, jclass clazz, jobject tokenObject, jboolean on) {
sp<IBinder> token(ibinderForJavaObject(env, tokenObject));
if (token == NULL) return;
@@ -2046,6 +2071,12 @@
(void*)nativeGetDisplayNativePrimaries },
{"nativeSetActiveColorMode", "(Landroid/os/IBinder;I)Z",
(void*)nativeSetActiveColorMode},
+ {"nativeGetBootDisplayModeSupport", "()Z",
+ (void*)nativeGetBootDisplayModeSupport },
+ {"nativeSetBootDisplayMode", "(Landroid/os/IBinder;I)V",
+ (void*)nativeSetBootDisplayMode },
+ {"nativeClearBootDisplayMode", "(Landroid/os/IBinder;)V",
+ (void*)nativeClearBootDisplayMode },
{"nativeSetAutoLowLatencyMode", "(Landroid/os/IBinder;Z)V",
(void*)nativeSetAutoLowLatencyMode },
{"nativeSetGameContentType", "(Landroid/os/IBinder;Z)V",
@@ -2184,6 +2215,8 @@
GetFieldIDOrDie(env, dynamicInfoClazz, "autoLowLatencyModeSupported", "Z");
gDynamicDisplayInfoClassInfo.gameContentTypeSupported =
GetFieldIDOrDie(env, dynamicInfoClazz, "gameContentTypeSupported", "Z");
+ gDynamicDisplayInfoClassInfo.preferredBootDisplayMode =
+ GetFieldIDOrDie(env, dynamicInfoClazz, "preferredBootDisplayMode", "I");
jclass modeClazz = FindClassOrDie(env, "android/view/SurfaceControl$DisplayMode");
gDisplayModeClassInfo.clazz = MakeGlobalRefOrDie(env, modeClazz);
diff --git a/core/proto/android/server/powermanagerservice.proto b/core/proto/android/server/powermanagerservice.proto
index d48ea3b..04f4d7b 100644
--- a/core/proto/android/server/powermanagerservice.proto
+++ b/core/proto/android/server/powermanagerservice.proto
@@ -452,16 +452,16 @@
optional bool is_interactive = 5;
// Time (in elapsedRealtime) when the device was last interactive
- optional bool last_interactive_time = 6;
+ optional int64 last_interactive_time = 6;
- // Time (in milliseconds) after becoming non-interactive that Low Power Standby can activate
+ // Timeout (in milliseconds) after becoming non-interactive that Low Power Standby can activate
optional int32 standby_timeout_config = 7;
// True if the device has entered idle mode since becoming non-interactive
- optional int32 idle_since_non_interactive = 8;
+ optional bool idle_since_non_interactive = 8;
// True if the device is currently in idle mode
- optional int32 is_device_idle = 9;
+ optional bool is_device_idle = 9;
// Set of app ids that are exempt form low power standby
repeated int32 allowlist = 10;
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index cf625a4..c9d7bfd 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -6545,6 +6545,14 @@
android:exported="false">
</activity>
+ <activity android:name="com.android.server.logcat.LogAccessConfirmationActivity"
+ android:theme="@style/Theme.Dialog.Confirmation"
+ android:excludeFromRecents="true"
+ android:process=":ui"
+ android:label="@string/log_access_confirmation_title"
+ android:exported="false">
+ </activity>
+
<activity android:name="com.android.server.notification.NASLearnMoreActivity"
android:theme="@style/Theme.Dialog.Confirmation"
android:excludeFromRecents="true"
diff --git a/core/res/res/layout/miniresolver.xml b/core/res/res/layout/miniresolver.xml
new file mode 100644
index 0000000..44ed6f2
--- /dev/null
+++ b/core/res/res/layout/miniresolver.xml
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<com.android.internal.widget.ResolverDrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:maxWidth="@dimen/resolver_max_width"
+ android:maxCollapsedHeight="@dimen/resolver_max_collapsed_height"
+ android:maxCollapsedHeightSmall="56dp"
+ android:id="@id/contentPanel">
+
+ <RelativeLayout
+ android:id="@+id/title_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alwaysShow="true"
+ android:elevation="@dimen/resolver_elevation"
+ android:paddingTop="@dimen/resolver_small_margin"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_edge_margin"
+ android:paddingBottom="@dimen/resolver_title_padding_bottom"
+ android:background="@drawable/bottomsheet_background">
+
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_centerHorizontal="true"
+ />
+
+ <TextView
+ android:id="@+id/open_cross_profile"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/icon"
+ android:layout_centerHorizontal="true"
+ android:textColor="?android:textColorPrimary"
+ />
+ </RelativeLayout>
+
+ <LinearLayout
+ android:id="@+id/button_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alwaysShow="true"
+ android:orientation="vertical"
+ android:background="?attr/colorBackground"
+ android:layout_ignoreOffset="true">
+ <View
+ android:id="@+id/resolver_button_bar_divider"
+ android:layout_width="match_parent"
+ android:layout_height="1dp"
+ android:background="?attr/colorBackground"
+ android:foreground="?attr/dividerVertical" />
+ <RelativeLayout
+ style="?attr/buttonBarStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_ignoreOffset="true"
+ android:layout_hasNestedScrollIndicator="true"
+ android:gravity="end|center_vertical"
+ android:orientation="horizontal"
+ android:layoutDirection="locale"
+ android:measureWithLargestChild="true"
+ android:paddingTop="@dimen/resolver_button_bar_spacing"
+ android:paddingBottom="@dimen/resolver_button_bar_spacing"
+ android:paddingStart="@dimen/resolver_edge_margin"
+ android:paddingEnd="@dimen/resolver_small_margin"
+ android:elevation="@dimen/resolver_elevation">
+
+ <Button
+ android:id="@+id/use_same_profile_browser"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentStart="true"
+ android:maxLines="2"
+ style="@android:style/Widget.DeviceDefault.Button.Borderless"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
+ android:text="@string/activity_resolver_use_once"
+ />
+
+ <Button
+ android:id="@+id/button_open"
+ android:layout_width="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:maxLines="2"
+ style="@android:style/Widget.DeviceDefault.Button.Colored"
+ android:fontFamily="@android:string/config_headlineFontFamilyMedium"
+ android:textAllCaps="false"
+ android:layout_height="wrap_content"
+ android:text="@string/whichViewApplicationLabel"
+ />
+ </RelativeLayout>
+ </LinearLayout>
+</com.android.internal.widget.ResolverDrawerLayout>
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml
index 3a2fb6e..cb40e86 100644
--- a/core/res/res/values/attrs_manifest.xml
+++ b/core/res/res/values/attrs_manifest.xml
@@ -2839,6 +2839,14 @@
<attr name="path" />
<attr name="minSdkVersion" />
<attr name="maxSdkVersion" />
+ <!-- The order in which the apex system services are initiated. When there are dependencies
+ among apex system services, setting this attribute for each of them ensures that they are
+ created in the order required by those dependencies. The apex-system-services that are
+ started manually within SystemServer ignore the initOrder and are not considered for
+ automatic starting of the other services.
+ The value is a simple integer, with higher number being initialized first. If not specified,
+ the default order is 0. -->
+ <attr name="initOrder" format="integer" />
</declare-styleable>
<!-- The <code>receiver</code> tag declares an
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 902d5e0..53cf463 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2376,6 +2376,12 @@
<!-- ComponentNames of the dreams that we should hide -->
<string-array name="config_disabledDreamComponents" translatable="false">
</string-array>
+ <!-- The list of supported dream complications -->
+ <integer-array name="config_supportedDreamComplications">
+ </integer-array>
+ <!-- The list of dream complications which should be enabled by default -->
+ <integer-array name="config_dreamComplicationsEnabledByDefault">
+ </integer-array>
<!-- Are we allowed to dream while not plugged in? -->
<bool name="config_dreamsEnabledOnBattery">false</bool>
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 2e4b783a..0dbd417 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -5689,6 +5689,20 @@
<!-- Title for the harmful app warning dialog. [CHAR LIMIT=40] -->
<string name="harmful_app_warning_title">Harmful app detected</string>
+ <!-- Title for the log access confirmation dialog. [CHAR LIMIT=40] -->
+ <string name="log_access_confirmation_title">System log access request</string>
+ <!-- Label for the allow button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="log_access_confirmation_allow">Only this time</string>
+ <!-- Label for the deny button on the log access confirmation dialog. [CHAR LIMIT=20] -->
+ <string name="log_access_confirmation_deny">Don\u2019t allow</string>
+
+ <!-- Content for the log access confirmation dialog. [CHAR LIMIT=NONE]-->
+ <string name="log_access_confirmation_body"><xliff:g id="log_access_app_name" example="Example App">%s</xliff:g> requests system logs for functional debugging.
+ These logs might contain information that apps and services on your device have written.</string>
+
+ <!-- Privacy notice do not show [CHAR LIMIT=20] -->
+ <string name="log_access_do_not_show_again">Don\u2019t show again</string>
+
<!-- Text describing a permission request for one app to show another app's
slices [CHAR LIMIT=NONE] -->
<string name="slices_permission_request"><xliff:g id="app" example="Example App">%1$s</xliff:g> wants to show <xliff:g id="app_2" example="Other Example App">%2$s</xliff:g> slices</string>
@@ -5936,9 +5950,9 @@
<string name="resolver_no_personal_apps_available">No personal apps</string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
- <string name="miniresolver_open_in_personal">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in personal profile?</string>
+ <string name="miniresolver_open_in_personal">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your personal profile?</string>
<!-- Dialog title. User must choose between opening content in a cross-profile app or same-profile browser. [CHAR LIMIT=NONE] -->
- <string name="miniresolver_open_in_work">Open in <xliff:g id="app" example="YouTube">%s</xliff:g> in work profile?</string>
+ <string name="miniresolver_open_in_work">Open <xliff:g id="app" example="YouTube">%s</xliff:g> in your work profile?</string>
<!-- Button option. Open the link in the personal browser. [CHAR LIMIT=NONE] -->
<string name="miniresolver_use_personal_browser">Use personal browser</string>
<!-- Button option. Open the link in the work browser. [CHAR LIMIT=NONE] -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 30a1963..764b273 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1599,6 +1599,13 @@
<java-symbol type="layout" name="resolver_list_per_profile" />
<java-symbol type="layout" name="chooser_list_per_profile" />
<java-symbol type="layout" name="resolver_empty_states" />
+ <java-symbol type="id" name="open_cross_profile" />
+ <java-symbol type="string" name="miniresolver_open_in_personal" />
+ <java-symbol type="string" name="miniresolver_open_in_work" />
+ <java-symbol type="string" name="miniresolver_use_personal_browser" />
+ <java-symbol type="string" name="miniresolver_use_work_browser" />
+ <java-symbol type="id" name="button_open" />
+ <java-symbol type="id" name="use_same_profile_browser" />
<java-symbol type="anim" name="slide_in_child_bottom" />
<java-symbol type="anim" name="slide_in_right" />
@@ -2221,6 +2228,8 @@
<java-symbol type="integer" name="config_dreamsBatteryLevelMinimumWhenNotPowered" />
<java-symbol type="integer" name="config_dreamsBatteryLevelDrainCutoff" />
<java-symbol type="string" name="config_dreamsDefaultComponent" />
+ <java-symbol type="array" name="config_supportedDreamComplications" />
+ <java-symbol type="array" name="config_dreamComplicationsEnabledByDefault" />
<java-symbol type="drawable" name="default_dream_preview" />
<java-symbol type="array" name="config_disabledDreamComponents" />
<java-symbol type="string" name="config_dozeComponent" />
@@ -2713,6 +2722,7 @@
<java-symbol type="bool" name="config_allow_ussd_over_ims" />
<java-symbol type="attr" name="touchscreenBlocksFocus" />
<java-symbol type="layout" name="resolver_list_with_default" />
+ <java-symbol type="layout" name="miniresolver" />
<java-symbol type="string" name="activity_resolver_use_always" />
<java-symbol type="string" name="whichApplicationNamed" />
<java-symbol type="string" name="whichApplicationLabel" />
@@ -3855,6 +3865,11 @@
<java-symbol type="string" name="harmful_app_warning_title" />
<java-symbol type="layout" name="harmful_app_warning_dialog" />
+ <java-symbol type="string" name="log_access_confirmation_allow" />
+ <java-symbol type="string" name="log_access_confirmation_deny" />
+ <java-symbol type="string" name="log_access_confirmation_title" />
+ <java-symbol type="string" name="log_access_confirmation_body" />
+
<java-symbol type="string" name="config_defaultAssistantAccessComponent" />
<java-symbol type="string" name="slices_permission_request" />
diff --git a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
index bd987a0..6639c02 100644
--- a/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
+++ b/core/tests/batterystatstests/BatteryStatsViewer/AndroidManifest.xml
@@ -20,6 +20,7 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BATTERY_STATS"/>
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
<application
android:theme="@style/Theme"
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
index 02e5942..fc385a0 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityServiceConnectionImpl.java
@@ -204,4 +204,6 @@
public void logTrace(long timestamp, String where, long loggingTypes, String callingParams,
int processId, long threadId, int callingUid, Bundle serializedCallingStackInBundle) {}
+
+ public void setAnimationScale(float scale) {}
}
diff --git a/core/tests/coretests/src/android/window/BackNavigationTest.java b/core/tests/coretests/src/android/window/BackNavigationTest.java
new file mode 100644
index 0000000..91d8531
--- /dev/null
+++ b/core/tests/coretests/src/android/window/BackNavigationTest.java
@@ -0,0 +1,134 @@
+/*
+ * 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 android.window;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import android.annotation.NonNull;
+import android.app.ActivityTaskManager;
+import android.app.EmptyActivity;
+import android.app.Instrumentation;
+import android.os.RemoteException;
+import android.support.test.uiautomator.UiDevice;
+import android.view.OnBackInvokedCallback;
+
+import androidx.lifecycle.Lifecycle;
+import androidx.test.core.app.ActivityScenario;
+import androidx.test.ext.junit.rules.ActivityScenarioRule;
+import androidx.test.platform.app.InstrumentationRegistry;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Integration test for back navigation
+ */
+public class BackNavigationTest {
+
+ @Rule
+ public final ActivityScenarioRule<EmptyActivity> mScenarioRule =
+ new ActivityScenarioRule<>(EmptyActivity.class);
+ private ActivityScenario<EmptyActivity> mScenario;
+ private Instrumentation mInstrumentation;
+
+ @Before
+ public void setup() {
+ mScenario = mScenarioRule.getScenario();
+ mInstrumentation = InstrumentationRegistry.getInstrumentation();
+ try {
+ UiDevice.getInstance(mInstrumentation).wakeUp();
+ } catch (RemoteException ignored) {
+ }
+ mInstrumentation.getUiAutomation().adoptShellPermissionIdentity();
+ }
+
+ @Test
+ public void registerCallback_initialized() {
+ CountDownLatch latch = registerBackCallback();
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ assertCallbackIsCalled(latch);
+ }
+
+ @Test
+ public void registerCallback_created() {
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ CountDownLatch latch = registerBackCallback();
+ mScenario.moveToState(Lifecycle.State.STARTED);
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ assertCallbackIsCalled(latch);
+ }
+
+ @Test
+ public void registerCallback_resumed() {
+ mScenario.moveToState(Lifecycle.State.CREATED);
+ mScenario.moveToState(Lifecycle.State.STARTED);
+ mScenario.moveToState(Lifecycle.State.RESUMED);
+ CountDownLatch latch = registerBackCallback();
+ assertCallbackIsCalled(latch);
+ }
+
+ private void assertCallbackIsCalled(CountDownLatch latch) {
+ try {
+ mInstrumentation.getUiAutomation().waitForIdle(500, 1000);
+ BackNavigationInfo info = ActivityTaskManager.getService().startBackNavigation();
+ assertNotNull("BackNavigationInfo is null", info);
+ assertNotNull("OnBackInvokedCallback is null", info.getOnBackInvokedCallback());
+ info.getOnBackInvokedCallback().onBackInvoked();
+ assertTrue(latch.await(500, TimeUnit.MILLISECONDS));
+ } catch (RemoteException ex) {
+ ex.rethrowFromSystemServer();
+ } catch (InterruptedException ex) {
+ fail("Application died before invoking the callback.\n" + ex.getMessage());
+ } catch (TimeoutException ex) {
+ fail(ex.getMessage());
+ }
+ }
+
+ @NonNull
+ private CountDownLatch registerBackCallback() {
+ CountDownLatch backInvokedLatch = new CountDownLatch(1);
+ CountDownLatch backRegisteredLatch = new CountDownLatch(1);
+ mScenario.onActivity(activity -> {
+ activity.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ backInvokedLatch.countDown();
+ }
+ }, 0
+ );
+ backRegisteredLatch.countDown();
+ });
+ try {
+ if (!backRegisteredLatch.await(100, TimeUnit.MILLISECONDS)) {
+ fail("Back callback was not registered on the Activity thread. This might be "
+ + "an error with the test itself.");
+ }
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ return backInvokedLatch;
+ }
+}
diff --git a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
index 75d2025..2817728f 100644
--- a/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
+++ b/core/tests/coretests/src/com/android/internal/app/ResolverActivityTest.java
@@ -731,6 +731,25 @@
}
@Test
+ public void testMiniResolver() {
+ ResolverActivity.ENABLE_TABBED_VIEW = true;
+ markWorkProfileUserAvailable();
+ List<ResolvedComponentInfo> personalResolvedComponentInfos =
+ createResolvedComponentsForTest(1);
+ List<ResolvedComponentInfo> workResolvedComponentInfos =
+ createResolvedComponentsForTest(1);
+ // Personal profile only has a browser
+ personalResolvedComponentInfos.get(0).getResolveInfoAt(0).handleAllWebDataURI = true;
+ setupResolverControllers(personalResolvedComponentInfos, workResolvedComponentInfos);
+ Intent sendIntent = createSendImageIntent();
+ sendIntent.setType("TestType");
+
+ mActivityRule.launchActivity(sendIntent);
+ waitForIdle();
+ onView(withId(R.id.open_cross_profile)).check(matches(isDisplayed()));
+ }
+
+ @Test
public void testWorkTab_noAppsAvailable_workOff_noAppsAvailableEmptyStateShown() {
// enable the work tab feature flag
ResolverActivity.ENABLE_TABBED_VIEW = true;
diff --git a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
index dbb2cf1..88349b3 100644
--- a/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
+++ b/core/tests/coretests/src/com/android/internal/power/MeasuredEnergyStatsTest.java
@@ -383,10 +383,10 @@
assertEquals(13, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 0));
// 6 * (6000-4000)/(6000-2000)
assertEquals(3, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_ON, 1));
-
- // POWER_BUCKET_SCREEN_OTHER was only present along with state=1
- assertEquals(0, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0));
- assertEquals(40, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1));
+ // 40 * (4000-1000)/(5000-1000)
+ assertEquals(30, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 0));
+ // 40 * (5000-4000)/(5000-1000)
+ assertEquals(10, stats.getAccumulatedStandardBucketCharge(POWER_BUCKET_SCREEN_OTHER, 1));
}
@Test
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index d002601..a331b6e 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -474,6 +474,7 @@
<!-- Permission needed for CTS test - WifiManagerTest -->
<permission name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<permission name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <permission name="android.permission.NEARBY_WIFI_DEVICES" />
<permission name="android.permission.OVERRIDE_WIFI_CONFIG" />
<!-- Permission required for CTS test CarrierMessagingServiceWrapperTest -->
<permission name="android.permission.BIND_CARRIER_SERVICES"/>
diff --git a/data/etc/services.core.protolog.json b/data/etc/services.core.protolog.json
index 1567233..f2a875c7 100644
--- a/data/etc/services.core.protolog.json
+++ b/data/etc/services.core.protolog.json
@@ -727,12 +727,6 @@
"group": "WM_DEBUG_BOOT",
"at": "com\/android\/server\/wm\/WindowManagerService.java"
},
- "-1343787701": {
- "message": "startBackNavigation task=%s, topRunningActivity=%s",
- "level": "DEBUG",
- "group": "WM_DEBUG_BACK_PREVIEW",
- "at": "com\/android\/server\/wm\/BackNavigationController.java"
- },
"-1340540100": {
"message": "Creating SnapshotStartingData",
"level": "VERBOSE",
@@ -1765,6 +1759,12 @@
"group": "WM_DEBUG_SYNC_ENGINE",
"at": "com\/android\/server\/wm\/BLASTSyncEngine.java"
},
+ "-228813488": {
+ "message": "%s: Setting back callback %s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/WindowState.java"
+ },
"-208825711": {
"message": "shouldWaitAnimatingExit: isWallpaperTarget: %s",
"level": "DEBUG",
@@ -3691,6 +3691,12 @@
"group": "WM_DEBUG_REMOTE_ANIMATIONS",
"at": "com\/android\/server\/wm\/RemoteAnimationController.java"
},
+ "1898905572": {
+ "message": "startBackNavigation task=%s, topRunningActivity=%s, topWindow=%s backCallback=%s",
+ "level": "DEBUG",
+ "group": "WM_DEBUG_BACK_PREVIEW",
+ "at": "com\/android\/server\/wm\/BackNavigationController.java"
+ },
"1903353011": {
"message": "notifyAppStopped: %s",
"level": "VERBOSE",
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml b/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml
deleted file mode 100644
index 16dea48..0000000
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more_ripple.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
- ~ 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.
- -->
-<ripple xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@android:color/system_neutral2_200">
- <item android:drawable="@drawable/letterbox_education_ic_expand_more"/>
-</ripple>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml b/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml
deleted file mode 100644
index a309d48..0000000
--- a/libs/WindowManager/Shell/res/layout/letterbox_education_toast_layout.xml
+++ /dev/null
@@ -1,61 +0,0 @@
-<!--
- ~ 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.
- -->
-<com.android.wm.shell.compatui.LetterboxEduToastLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@color/compat_controls_background"
- android:gravity="center"
- android:paddingVertical="14dp"
- android:paddingHorizontal="16dp">
-
- <!-- Adding an extra layer to animate the alpha of the background and content separately. -->
- <LinearLayout
- android:id="@+id/letterbox_education_content"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:gravity="center_vertical"
- android:orientation="horizontal">
-
- <ImageView
- android:id="@+id/letterbox_education_icon"
- android:layout_width="@dimen/letterbox_education_toast_icon_size"
- android:layout_height="@dimen/letterbox_education_toast_icon_size"/>
-
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:maxWidth="@dimen/letterbox_education_toast_text_max_width"
- android:paddingHorizontal="16dp"
- android:lineSpacingExtra="5sp"
- android:text="@string/letterbox_education_toast_title"
- android:textAlignment="viewStart"
- android:textColor="@color/compat_controls_text"
- android:textSize="16sp"
- android:maxLines="1"
- android:ellipsize="end"/>
-
- <ImageButton
- android:id="@+id/letterbox_education_toast_expand"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:src="@drawable/letterbox_education_ic_expand_more_ripple"
- android:background="@android:color/transparent"
- android:contentDescription="@string/letterbox_education_expand_button_description"/>
-
- </LinearLayout>
-
-</com.android.wm.shell.compatui.LetterboxEduToastLayout>
diff --git a/libs/WindowManager/Shell/res/values/dimen.xml b/libs/WindowManager/Shell/res/values/dimen.xml
index ab2c9b1..40c7647 100644
--- a/libs/WindowManager/Shell/res/values/dimen.xml
+++ b/libs/WindowManager/Shell/res/values/dimen.xml
@@ -219,18 +219,9 @@
<!-- The width of the camera compat hint. -->
<dimen name="camera_compat_hint_width">143dp</dimen>
- <!-- The corner radius of the letterbox education toast. -->
- <dimen name="letterbox_education_toast_corner_radius">100dp</dimen>
-
<!-- The corner radius of the letterbox education dialog. -->
<dimen name="letterbox_education_dialog_corner_radius">28dp</dimen>
- <!-- The margin between the letterbox education toast/dialog and the bottom of the task. -->
- <dimen name="letterbox_education_margin_bottom">16dp</dimen>
-
- <!-- The size of the icon in the letterbox education toast. -->
- <dimen name="letterbox_education_toast_icon_size">24dp</dimen>
-
<!-- The size of an icon in the letterbox education dialog. -->
<dimen name="letterbox_education_dialog_icon_size">48dp</dimen>
@@ -243,9 +234,6 @@
<!-- The maximum width of the title and subtitle in the letterbox education dialog. -->
<dimen name="letterbox_education_dialog_title_max_width">444dp</dimen>
- <!-- The maximum width of the text in the letterbox education toast. -->
- <dimen name="letterbox_education_toast_text_max_width">398dp</dimen>
-
<!-- The distance that the letterbox education dialog will move up during appear/dismiss
animation. -->
<dimen name="letterbox_education_dialog_animation_elevation">20dp</dimen>
diff --git a/libs/WindowManager/Shell/res/values/strings.xml b/libs/WindowManager/Shell/res/values/strings.xml
index a8a9ed7..16a4b52 100644
--- a/libs/WindowManager/Shell/res/values/strings.xml
+++ b/libs/WindowManager/Shell/res/values/strings.xml
@@ -174,9 +174,6 @@
<!-- The title of the letterbox education dialog. [CHAR LIMIT=NONE] -->
<string name="letterbox_education_dialog_title">Get the most out of <xliff:g id="app_name" example="YouTube">%s</xliff:g></string>
- <!-- The title of the letterbox education toast. [CHAR LIMIT=60] -->
- <string name="letterbox_education_toast_title">Rotate your device for a full-screen view</string>
-
<!-- Description of the rotate screen into portrait action. [CHAR LIMIT=NONE] -->
<string name="letterbox_education_screen_rotation_portrait_text">Rotate your screen to portrait</string>
@@ -192,7 +189,4 @@
<!-- Button text for dismissing the letterbox education dialog. [CHAR LIMIT=20] -->
<string name="letterbox_education_got_it">Got it</string>
- <!-- Accessibility description of the letterbox education toast expand to dialog button. [CHAR LIMIT=NONE] -->
- <string name="letterbox_education_expand_button_description">Expand for more information.</string>
-
</resources>
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
index a477bd7..7ab6835 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleStackView.java
@@ -1790,6 +1790,7 @@
/**
* Changes the expanded state of the stack.
+ * Don't call this directly, call mBubbleData#setExpanded.
*
* @param shouldExpand whether the bubble stack should appear expanded
*/
@@ -1836,7 +1837,7 @@
} else if (mManageEduView != null && mManageEduView.getVisibility() == VISIBLE) {
mManageEduView.hide();
} else {
- setExpanded(false);
+ mBubbleData.setExpanded(false);
}
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java
deleted file mode 100644
index e7f592d..0000000
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/letterboxedu/LetterboxEduToastLayout.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.wm.shell.compatui.letterboxedu;
-
-import android.content.Context;
-import android.graphics.drawable.Drawable;
-import android.util.AttributeSet;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-
-import com.android.wm.shell.R;
-
-/**
- * Container for the Letterbox Education Toast.
- */
-// TODO(b/215316431): Add tests
-public class LetterboxEduToastLayout extends FrameLayout {
-
- public LetterboxEduToastLayout(Context context) {
- this(context, null);
- }
-
- public LetterboxEduToastLayout(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public LetterboxEduToastLayout(Context context, AttributeSet attrs, int defStyleAttr,
- int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- }
-
- /**
- * Register a callback for the dismiss button.
- * @param callback The callback to register
- */
- void setExpandOnClickListener(Runnable callback) {
- findViewById(R.id.letterbox_education_toast_expand).setOnClickListener(
- view -> callback.run());
- }
-
- /**
- * Updates the layout with the given app info.
- * @param appName The name of the app
- * @param appIcon The icon of the app
- */
- void updateAppInfo(String appName, Drawable appIcon) {
- ImageView icon = findViewById(R.id.letterbox_education_icon);
- icon.setContentDescription(appName);
- icon.setImageDrawable(appIcon);
- }
-}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
index 3cfa541..d022ec1 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/ISplitScreen.aidl
@@ -89,9 +89,17 @@
/**
* Version of startTasks using legacy transition system.
*/
- oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
- int sideTaskId, in Bundle sideOptions, int sidePosition,
- float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+ oneway void startTasksWithLegacyTransition(int mainTaskId, in Bundle mainOptions,
+ int sideTaskId, in Bundle sideOptions, int sidePosition,
+ float splitRatio, in RemoteAnimationAdapter adapter) = 11;
+
+ /**
+ * Start a pair of intent and task using legacy transition system.
+ */
+ oneway void startIntentAndTaskWithLegacyTransition(in PendingIntent pendingIntent,
+ in Intent fillInIntent, int taskId, boolean intentFirst, in Bundle mainOptions,
+ in Bundle sideOptions, int sidePosition, float splitRatio,
+ in RemoteAnimationAdapter adapter) = 12;
/**
* Blocking call that notifies and gets additional split-screen targets when entering
@@ -100,5 +108,7 @@
* @param appTargets apps that will be re-parented to display area
*/
RemoteAnimationTarget[] onGoingToRecentsLegacy(boolean cancel,
- in RemoteAnimationTarget[] appTargets) = 12;
+ in RemoteAnimationTarget[] appTargets) = 13;
+
+
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
index 3e6dc82..990b53a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/SplitScreenController.java
@@ -641,6 +641,18 @@
}
@Override
+ public void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent,
+ Intent fillInIntent, int taskId, boolean intentFirst, Bundle mainOptions,
+ Bundle sideOptions, int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter) {
+ executeRemoteCallWithTaskPermission(mController,
+ "startIntentAndTaskWithLegacyTransition", (controller) ->
+ controller.mStageCoordinator.startIntentAndTaskWithLegacyTransition(
+ pendingIntent, fillInIntent, taskId, intentFirst, mainOptions,
+ sideOptions, sidePosition, splitRatio, adapter));
+ }
+
+ @Override
public void startTasks(int mainTaskId, @Nullable Bundle mainOptions,
int sideTaskId, @Nullable Bundle sideOptions,
@SplitPosition int sidePosition, float splitRatio,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
index a2c2f59..219530b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/splitscreen/StageCoordinator.java
@@ -57,8 +57,10 @@
import android.app.ActivityManager;
import android.app.ActivityOptions;
import android.app.ActivityTaskManager;
+import android.app.PendingIntent;
import android.app.WindowConfiguration;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.hardware.devicestate.DeviceStateManager;
@@ -467,6 +469,116 @@
mTaskOrganizer.applyTransaction(wct);
}
+ /** Start an intent and a task ordered by {@code intentFirst}. */
+ void startIntentAndTaskWithLegacyTransition(PendingIntent pendingIntent, Intent fillInIntent,
+ int taskId, boolean intentFirst, @Nullable Bundle mainOptions,
+ @Nullable Bundle sideOptions, @SplitPosition int sidePosition, float splitRatio,
+ RemoteAnimationAdapter adapter) {
+ // TODO: try pulling the first chunk of this method into a method so that it can be shared
+ // with startTasksWithLegacyTransition. So far attempts to do so result in failure in split.
+
+ // Init divider first to make divider leash for remote animation target.
+ mSplitLayout.init();
+ // Set false to avoid record new bounds with old task still on top;
+ mShouldUpdateRecents = false;
+ final WindowContainerTransaction wct = new WindowContainerTransaction();
+ final WindowContainerTransaction evictWct = new WindowContainerTransaction();
+ prepareEvictChildTasks(SPLIT_POSITION_TOP_OR_LEFT, evictWct);
+ prepareEvictChildTasks(SPLIT_POSITION_BOTTOM_OR_RIGHT, evictWct);
+ // Need to add another wrapper here in shell so that we can inject the divider bar
+ // and also manage the process elevation via setRunningRemote
+ IRemoteAnimationRunner wrapper = new IRemoteAnimationRunner.Stub() {
+ @Override
+ public void onAnimationStart(@WindowManager.TransitionOldType int transit,
+ RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ final IRemoteAnimationFinishedCallback finishedCallback) {
+ RemoteAnimationTarget[] augmentedNonApps =
+ new RemoteAnimationTarget[nonApps.length + 1];
+ for (int i = 0; i < nonApps.length; ++i) {
+ augmentedNonApps[i] = nonApps[i];
+ }
+ augmentedNonApps[augmentedNonApps.length - 1] = getDividerBarLegacyTarget();
+
+ IRemoteAnimationFinishedCallback wrapCallback =
+ new IRemoteAnimationFinishedCallback.Stub() {
+ @Override
+ public void onAnimationFinished() throws RemoteException {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ finishedCallback.onAnimationFinished();
+ }
+ };
+ try {
+ try {
+ ActivityTaskManager.getService().setRunningRemoteTransitionDelegate(
+ adapter.getCallingApplication());
+ } catch (SecurityException e) {
+ Slog.e(TAG, "Unable to boost animation thread. This should only happen"
+ + " during unit tests");
+ }
+ adapter.getRunner().onAnimationStart(transit, apps, wallpapers,
+ augmentedNonApps, wrapCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+
+ @Override
+ public void onAnimationCancelled() {
+ mShouldUpdateRecents = true;
+ mSyncQueue.queue(evictWct);
+ mSyncQueue.runInSync(t -> setDividerVisibility(true, t));
+ try {
+ adapter.getRunner().onAnimationCancelled();
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error starting remote animation", e);
+ }
+ }
+ };
+ RemoteAnimationAdapter wrappedAdapter = new RemoteAnimationAdapter(
+ wrapper, adapter.getDuration(), adapter.getStatusBarTransitionDelay());
+
+ if (mainOptions == null) {
+ mainOptions = ActivityOptions.makeRemoteAnimation(wrappedAdapter).toBundle();
+ } else {
+ ActivityOptions mainActivityOptions = ActivityOptions.fromBundle(mainOptions);
+ mainActivityOptions.update(ActivityOptions.makeRemoteAnimation(wrappedAdapter));
+ mainOptions = mainActivityOptions.toBundle();
+ }
+
+ sideOptions = sideOptions != null ? sideOptions : new Bundle();
+ setSideStagePosition(sidePosition, wct);
+
+ mSplitLayout.setDivideRatio(splitRatio);
+ if (mMainStage.isActive()) {
+ mMainStage.moveToTop(getMainStageBounds(), wct);
+ } else {
+ // Build a request WCT that will launch both apps such that task 0 is on the main stage
+ // while task 1 is on the side stage.
+ mMainStage.activate(getMainStageBounds(), wct, false /* reparent */);
+ }
+ mSideStage.moveToTop(getSideStageBounds(), wct);
+
+ // Make sure the launch options will put tasks in the corresponding split roots
+ addActivityOptions(mainOptions, mMainStage);
+ addActivityOptions(sideOptions, mSideStage);
+
+ // Add task launch requests
+ if (intentFirst) {
+ wct.sendPendingIntent(pendingIntent, fillInIntent, mainOptions);
+ wct.startTask(taskId, sideOptions);
+ } else {
+ wct.startTask(taskId, mainOptions);
+ wct.sendPendingIntent(pendingIntent, fillInIntent, sideOptions);
+ }
+
+ // Using legacy transitions, so we can't use blast sync since it conflicts.
+ mTaskOrganizer.applyTransaction(wct);
+ }
+
/**
* Collects all the current child tasks of a specific split and prepares transaction to evict
* them to display.
diff --git a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
index 574a9f4..556742e 100644
--- a/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
+++ b/libs/WindowManager/Shell/tests/flicker/AndroidTest.xml
@@ -26,8 +26,16 @@
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6000s" />
<option name="hidden-api-checks" value="false" />
+ <option name="device-listeners"
+ value="com.android.server.wm.flicker.TraceFileReadyListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="(\w)+\.winscope" />
+ <option name="pull-pattern-keys" value="(\w)+\.mp4" />
+ <option name="collect-on-run-ended-only" value="false" />
+ <option name="clean-up" value="true" />
+ </metrics_collector>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
index 960c7ac..b738c47 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/back/BackAnimationControllerTest.java
@@ -75,7 +75,8 @@
screenshotSurface,
hardwareBuffer,
new WindowConfiguration(),
- new RemoteCallback((bundle) -> {}));
+ new RemoteCallback((bundle) -> {}),
+ null);
try {
doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
} catch (RemoteException ex) {
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 15a398d..b8333fb 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5919,13 +5919,14 @@
* @param newDevice Bluetooth device connected or null if there is no new devices
* @param previousDevice Bluetooth device disconnected or null if there is no disconnected
* devices
- * @param info contain all info related to the device. {@link BtProfileConnectionInfo}
+ * @param info contain all info related to the device. {@link BluetoothProfileConnectionInfo}
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
@RequiresPermission(android.Manifest.permission.BLUETOOTH_STACK)
public void handleBluetoothActiveDeviceChanged(@Nullable BluetoothDevice newDevice,
- @Nullable BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
+ @Nullable BluetoothDevice previousDevice,
+ @NonNull BluetoothProfileConnectionInfo info) {
final IAudioService service = getService();
try {
service.handleBluetoothActiveDeviceChanged(newDevice, previousDevice, info);
diff --git a/media/java/android/media/BtProfileConnectionInfo.aidl b/media/java/android/media/BluetoothProfileConnectionInfo.aidl
similarity index 93%
rename from media/java/android/media/BtProfileConnectionInfo.aidl
rename to media/java/android/media/BluetoothProfileConnectionInfo.aidl
index 047f06b..0617084 100644
--- a/media/java/android/media/BtProfileConnectionInfo.aidl
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.aidl
@@ -16,5 +16,5 @@
package android.media;
-parcelable BtProfileConnectionInfo;
+parcelable BluetoothProfileConnectionInfo;
diff --git a/media/java/android/media/BtProfileConnectionInfo.java b/media/java/android/media/BluetoothProfileConnectionInfo.java
similarity index 65%
rename from media/java/android/media/BtProfileConnectionInfo.java
rename to media/java/android/media/BluetoothProfileConnectionInfo.java
index 88b9777..c148846 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.java
@@ -26,15 +26,14 @@
* {@hide}
*/
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
-public final class BtProfileConnectionInfo implements Parcelable {
-
+public final class BluetoothProfileConnectionInfo implements Parcelable {
private final int mProfile;
private final boolean mSupprNoisy;
private final int mVolume;
private final boolean mIsLeOutput;
- private BtProfileConnectionInfo(int profile, boolean suppressNoisyIntent, int volume,
- boolean isLeOutput) {
+ private BluetoothProfileConnectionInfo(int profile, boolean suppressNoisyIntent,
+ int volume, boolean isLeOutput) {
mProfile = profile;
mSupprNoisy = suppressNoisyIntent;
mVolume = volume;
@@ -45,21 +44,21 @@
* Constructor used by BtHelper when a profile is connected
* {@hide}
*/
- public BtProfileConnectionInfo(int profile) {
+ public BluetoothProfileConnectionInfo(int profile) {
this(profile, false, -1, false);
}
- public static final @NonNull Parcelable.Creator<BtProfileConnectionInfo> CREATOR =
- new Parcelable.Creator<BtProfileConnectionInfo>() {
+ public static final @NonNull Parcelable.Creator<BluetoothProfileConnectionInfo> CREATOR =
+ new Parcelable.Creator<BluetoothProfileConnectionInfo>() {
@Override
- public BtProfileConnectionInfo createFromParcel(Parcel source) {
- return new BtProfileConnectionInfo(source.readInt(), source.readBoolean(),
- source.readInt(), source.readBoolean());
+ public BluetoothProfileConnectionInfo createFromParcel(Parcel source) {
+ return new BluetoothProfileConnectionInfo(source.readInt(),
+ source.readBoolean(), source.readInt(), source.readBoolean());
}
@Override
- public BtProfileConnectionInfo[] newArray(int size) {
- return new BtProfileConnectionInfo[size];
+ public BluetoothProfileConnectionInfo[] newArray(int size) {
+ return new BluetoothProfileConnectionInfo[size];
}
};
@@ -84,10 +83,10 @@
*
* @param volume of device -1 to ignore value
*/
- public static @NonNull BtProfileConnectionInfo a2dpInfo(boolean suppressNoisyIntent,
- int volume) {
- return new BtProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent, volume,
- false);
+ public static @NonNull BluetoothProfileConnectionInfo createA2dpInfo(
+ boolean suppressNoisyIntent, int volume) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP, suppressNoisyIntent,
+ volume, false);
}
/**
@@ -96,8 +95,8 @@
*
* @param volume of device -1 to ignore value
*/
- public static @NonNull BtProfileConnectionInfo a2dpSinkInfo(int volume) {
- return new BtProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
+ public static @NonNull BluetoothProfileConnectionInfo createA2dpSinkInfo(int volume) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.A2DP_SINK, true, volume, false);
}
/**
@@ -106,9 +105,10 @@
* @param suppressNoisyIntent if true the {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY}
* intent will not be sent.
*/
- public static @NonNull BtProfileConnectionInfo hearingAidInfo(boolean suppressNoisyIntent) {
- return new BtProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent, -1,
- false);
+ public static @NonNull BluetoothProfileConnectionInfo createHearingAidInfo(
+ boolean suppressNoisyIntent) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.HEARING_AID, suppressNoisyIntent,
+ -1, false);
}
/**
@@ -119,10 +119,10 @@
*
* @param isLeOutput if true mean the device is an output device, if false it's an input device
*/
- public static @NonNull BtProfileConnectionInfo leAudio(boolean suppressNoisyIntent,
- boolean isLeOutput) {
- return new BtProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent, -1,
- isLeOutput);
+ public static @NonNull BluetoothProfileConnectionInfo createLeAudioInfo(
+ boolean suppressNoisyIntent, boolean isLeOutput) {
+ return new BluetoothProfileConnectionInfo(BluetoothProfile.LE_AUDIO, suppressNoisyIntent,
+ -1, isLeOutput);
}
/**
@@ -136,7 +136,7 @@
* @return {@code true} if {@link AudioManager.ACTION_AUDIO_BECOMING_NOISY} intent will not be
* sent
*/
- public boolean getSuppressNoisyIntent() {
+ public boolean isSuppressNoisyIntent() {
return mSupprNoisy;
}
@@ -153,7 +153,7 @@
* @return {@code true} is the LE device is an output device, {@code false} if it's an input
* device
*/
- public boolean getIsLeOutput() {
+ public boolean isLeOutput() {
return mIsLeOutput;
}
}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 96199a9..4451c64 100755
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -25,7 +25,7 @@
import android.media.AudioPlaybackConfiguration;
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
@@ -276,7 +276,7 @@
oneway void playerHasOpPlayAudio(in int piid, in boolean hasOpPlayAudio);
void handleBluetoothActiveDeviceChanged(in BluetoothDevice newDevice,
- in BluetoothDevice previousDevice, in BtProfileConnectionInfo info);
+ in BluetoothDevice previousDevice, in BluetoothProfileConnectionInfo info);
oneway void setFocusRequestResultFromExtPolicy(in AudioFocusInfo afi, int requestResult,
in IAudioPolicyCallback pcb);
diff --git a/media/java/android/media/ImageReader.java b/media/java/android/media/ImageReader.java
index e2e48d3..5f02a43 100644
--- a/media/java/android/media/ImageReader.java
+++ b/media/java/android/media/ImageReader.java
@@ -43,6 +43,7 @@
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.locks.StampedLock;
/**
* <p>The ImageReader class allows direct application access to image data
@@ -675,7 +676,8 @@
* If no handler specified and the calling thread has no looper.
*/
public void setOnImageAvailableListener(OnImageAvailableListener listener, Handler handler) {
- synchronized (mListenerLock) {
+ long writeStamp = mListenerLock.writeLock();
+ try {
if (listener != null) {
Looper looper = handler != null ? handler.getLooper() : Looper.myLooper();
if (looper == null) {
@@ -691,6 +693,8 @@
mListenerExecutor = null;
}
mListener = listener;
+ } finally {
+ mListenerLock.unlockWrite(writeStamp);
}
}
@@ -713,9 +717,12 @@
throw new IllegalArgumentException("executor must not be null");
}
- synchronized (mListenerLock) {
+ long writeStamp = mListenerLock.writeLock();
+ try {
mListenerExecutor = executor;
mListener = listener;
+ } finally {
+ mListenerLock.unlockWrite(writeStamp);
}
}
@@ -731,6 +738,8 @@
/**
* Callback that is called when a new image is available from ImageReader.
*
+ * This callback must not modify or close the passed {@code reader}.
+ *
* @param reader the ImageReader the callback is associated with.
* @see ImageReader
* @see Image
@@ -889,28 +898,41 @@
return;
}
- final Executor executor;
- final OnImageAvailableListener listener;
- synchronized (ir.mListenerLock) {
- executor = ir.mListenerExecutor;
- listener = ir.mListener;
- }
- final boolean isReaderValid;
synchronized (ir.mCloseLock) {
- isReaderValid = ir.mIsReaderValid;
+ if (!ir.mIsReaderValid) {
+ // It's dangerous to fire onImageAvailable() callback when the ImageReader
+ // is being closed, as application could acquire next image in the
+ // onImageAvailable() callback.
+ return;
+ }
}
- // It's dangerous to fire onImageAvailable() callback when the ImageReader
- // is being closed, as application could acquire next image in the
- // onImageAvailable() callback.
- if (executor != null && listener != null && isReaderValid) {
- executor.execute(new Runnable() {
- @Override
- public void run() {
- listener.onImageAvailable(ir);
- }
- });
+ final Executor executor;
+ final long readStamp = ir.mListenerLock.readLock();
+ try {
+ executor = ir.mListenerExecutor;
+ if (executor == null) {
+ return;
+ }
+ } finally {
+ ir.mListenerLock.unlockRead(readStamp);
}
+
+ executor.execute(() -> {
+ // Acquire readlock to ensure that the ImageReader does not change its
+ // state while a listener is actively processing.
+ final long rStamp = ir.mListenerLock.readLock();
+ try {
+ // Fire onImageAvailable of the latest non-null listener
+ // This ensures that if the listener changes while messages are in queue, the
+ // in-flight messages will call onImageAvailable of the new listener instead
+ if (ir.mListener != null) {
+ ir.mListener.onImageAvailable(ir);
+ }
+ } finally {
+ ir.mListenerLock.unlockRead(rStamp);
+ }
+ });
}
/**
@@ -1070,7 +1092,7 @@
private Surface mSurface;
private int mEstimatedNativeAllocBytes;
- private final Object mListenerLock = new Object();
+ private final StampedLock mListenerLock = new StampedLock();
private final Object mCloseLock = new Object();
private boolean mIsReaderValid = false;
private OnImageAvailableListener mListener;
diff --git a/media/java/android/media/tv/tuner/DemuxCapabilities.java b/media/java/android/media/tv/tuner/DemuxCapabilities.java
index 0f5bf08..14a9144 100644
--- a/media/java/android/media/tv/tuner/DemuxCapabilities.java
+++ b/media/java/android/media/tv/tuner/DemuxCapabilities.java
@@ -36,13 +36,8 @@
public class DemuxCapabilities {
/** @hide */
- @IntDef(flag = true, value = {
- Filter.TYPE_TS,
- Filter.TYPE_MMTP,
- Filter.TYPE_IP,
- Filter.TYPE_TLV,
- Filter.TYPE_ALP
- })
+ @IntDef(value = {Filter.TYPE_TS, Filter.TYPE_MMTP, Filter.TYPE_IP, Filter.TYPE_TLV,
+ Filter.TYPE_ALP})
@Retention(RetentionPolicy.SOURCE)
public @interface FilterCapabilities {}
diff --git a/media/java/android/media/tv/tuner/filter/Filter.java b/media/java/android/media/tv/tuner/filter/Filter.java
index 9f44236..14accaa 100644
--- a/media/java/android/media/tv/tuner/filter/Filter.java
+++ b/media/java/android/media/tv/tuner/filter/Filter.java
@@ -153,8 +153,8 @@
/** @hide */
- @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_DATA_READY, STATUS_LOW_WATER,
- STATUS_HIGH_WATER, STATUS_OVERFLOW})
+ @IntDef(prefix = "STATUS_",
+ value = {STATUS_DATA_READY, STATUS_LOW_WATER, STATUS_HIGH_WATER, STATUS_OVERFLOW})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {}
@@ -185,8 +185,7 @@
public static final int STATUS_OVERFLOW = DemuxFilterStatus.OVERFLOW;
/** @hide */
- @IntDef(flag = true,
- prefix = "SCRAMBLING_STATUS_",
+ @IntDef(prefix = "SCRAMBLING_STATUS_",
value = {SCRAMBLING_STATUS_UNKNOWN, SCRAMBLING_STATUS_NOT_SCRAMBLED,
SCRAMBLING_STATUS_SCRAMBLED})
@Retention(RetentionPolicy.SOURCE)
@@ -209,8 +208,7 @@
android.hardware.tv.tuner.ScramblingStatus.SCRAMBLED;
/** @hide */
- @IntDef(flag = true,
- prefix = "MONITOR_EVENT_",
+ @IntDef(prefix = "MONITOR_EVENT_",
value = {MONITOR_EVENT_SCRAMBLING_STATUS, MONITOR_EVENT_IP_CID_CHANGE})
@Retention(RetentionPolicy.SOURCE)
public @interface MonitorEventMask {}
diff --git a/media/java/android/media/tv/tuner/filter/RecordSettings.java b/media/java/android/media/tv/tuner/filter/RecordSettings.java
index d34581d..b16d9fb 100644
--- a/media/java/android/media/tv/tuner/filter/RecordSettings.java
+++ b/media/java/android/media/tv/tuner/filter/RecordSettings.java
@@ -40,8 +40,7 @@
*
* @hide
*/
- @IntDef(flag = true,
- value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
+ @IntDef(value = {TS_INDEX_INVALID, TS_INDEX_FIRST_PACKET, TS_INDEX_PAYLOAD_UNIT_START_INDICATOR,
TS_INDEX_CHANGE_TO_NOT_SCRAMBLED, TS_INDEX_CHANGE_TO_EVEN_SCRAMBLED,
TS_INDEX_CHANGE_TO_ODD_SCRAMBLED, TS_INDEX_DISCONTINUITY_INDICATOR,
TS_INDEX_RANDOM_ACCESS_INDICATOR, TS_INDEX_PRIORITY_INDICATOR,
@@ -165,7 +164,6 @@
* @hide
*/
@IntDef(prefix = "SC_INDEX_",
- flag = true,
value = {SC_INDEX_I_FRAME, SC_INDEX_P_FRAME, SC_INDEX_B_FRAME,
SC_INDEX_SEQUENCE, SC_INDEX_I_SLICE, SC_INDEX_P_SLICE,
SC_INDEX_B_SLICE, SC_INDEX_SI_SLICE, SC_INDEX_SP_SLICE})
@@ -214,8 +212,7 @@
*
* @hide
*/
- @IntDef(flag = true,
- value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
+ @IntDef(value = {SC_HEVC_INDEX_SPS, SC_HEVC_INDEX_AUD, SC_HEVC_INDEX_SLICE_CE_BLA_W_LP,
SC_HEVC_INDEX_SLICE_BLA_W_RADL, SC_HEVC_INDEX_SLICE_BLA_N_LP,
SC_HEVC_INDEX_SLICE_IDR_W_RADL, SC_HEVC_INDEX_SLICE_IDR_N_LP,
SC_HEVC_INDEX_SLICE_TRAIL_CRA})
@@ -258,8 +255,7 @@
/**
* @hide
*/
- @IntDef(flag = true,
- prefix = "SC_",
+ @IntDef(prefix = "SC_",
value = {
SC_INDEX_I_FRAME,
SC_INDEX_P_FRAME,
diff --git a/media/java/android/media/tv/tuner/filter/SharedFilter.java b/media/java/android/media/tv/tuner/filter/SharedFilter.java
index 740ab9c..21964ee 100644
--- a/media/java/android/media/tv/tuner/filter/SharedFilter.java
+++ b/media/java/android/media/tv/tuner/filter/SharedFilter.java
@@ -38,7 +38,7 @@
@SystemApi
public final class SharedFilter implements AutoCloseable {
/** @hide */
- @IntDef(flag = true, prefix = "STATUS_", value = {STATUS_INACCESSIBLE})
+ @IntDef(prefix = "STATUS_", value = {STATUS_INACCESSIBLE})
@Retention(RetentionPolicy.SOURCE)
public @interface Status {}
diff --git a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
index e0405ef..6c1134a 100644
--- a/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AnalogFrontendSettings.java
@@ -36,8 +36,7 @@
@SystemApi
public class AnalogFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "SIGNAL_TYPE_",
+ @IntDef(prefix = "SIGNAL_TYPE_",
value = {SIGNAL_TYPE_UNDEFINED, SIGNAL_TYPE_AUTO, SIGNAL_TYPE_PAL, SIGNAL_TYPE_PAL_M,
SIGNAL_TYPE_PAL_N, SIGNAL_TYPE_PAL_60, SIGNAL_TYPE_NTSC, SIGNAL_TYPE_NTSC_443,
SIGNAL_TYPE_SECAM})
@@ -82,8 +81,7 @@
public static final int SIGNAL_TYPE_SECAM = FrontendAnalogType.SECAM;
/** @hide */
- @IntDef(flag = true,
- prefix = "SIF_",
+ @IntDef(prefix = "SIF_",
value = {SIF_UNDEFINED, SIF_AUTO, SIF_BG, SIF_BG_A2, SIF_BG_NICAM, SIF_I, SIF_DK,
SIF_DK1_A2, SIF_DK2_A2, SIF_DK3_A2, SIF_DK_NICAM, SIF_L, SIF_M, SIF_M_BTSC, SIF_M_A2,
SIF_M_EIAJ, SIF_I_NICAM, SIF_L_NICAM, SIF_L_PRIME})
diff --git a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
index a7157e2..c99f911 100644
--- a/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Atsc3FrontendSettings.java
@@ -39,8 +39,7 @@
public class Atsc3FrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "BANDWIDTH_",
+ @IntDef(prefix = "BANDWIDTH_",
value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_BANDWIDTH_6MHZ,
BANDWIDTH_BANDWIDTH_7MHZ, BANDWIDTH_BANDWIDTH_8MHZ})
@Retention(RetentionPolicy.SOURCE)
@@ -69,8 +68,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO,
MODULATION_MOD_QPSK, MODULATION_MOD_16QAM,
MODULATION_MOD_64QAM, MODULATION_MOD_256QAM,
@@ -113,8 +111,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "TIME_INTERLEAVE_MODE_",
+ @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
TIME_INTERLEAVE_MODE_CTI, TIME_INTERLEAVE_MODE_HTI})
@Retention(RetentionPolicy.SOURCE)
@@ -140,8 +137,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "CODERATE_",
+ @IntDef(prefix = "CODERATE_",
value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_15, CODERATE_3_15, CODERATE_4_15,
CODERATE_5_15, CODERATE_6_15, CODERATE_7_15, CODERATE_8_15, CODERATE_9_15,
CODERATE_10_15, CODERATE_11_15, CODERATE_12_15, CODERATE_13_15})
@@ -207,8 +203,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "FEC_",
+ @IntDef(prefix = "FEC_",
value = {FEC_UNDEFINED, FEC_AUTO, FEC_BCH_LDPC_16K, FEC_BCH_LDPC_64K, FEC_CRC_LDPC_16K,
FEC_CRC_LDPC_64K, FEC_LDPC_16K, FEC_LDPC_64K})
@Retention(RetentionPolicy.SOURCE)
@@ -249,8 +244,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "DEMOD_OUTPUT_FORMAT_",
+ @IntDef(prefix = "DEMOD_OUTPUT_FORMAT_",
value = {DEMOD_OUTPUT_FORMAT_UNDEFINED, DEMOD_OUTPUT_FORMAT_ATSC3_LINKLAYER_PACKET,
DEMOD_OUTPUT_FORMAT_BASEBAND_PACKET})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
index 3071ce8..64c6ce6 100644
--- a/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/AtscFrontendSettings.java
@@ -34,8 +34,7 @@
public class AtscFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_8VSB,
MODULATION_MOD_16VSB})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
index 6b5d6ca..07c1fbf 100644
--- a/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DtmbFrontendSettings.java
@@ -43,8 +43,7 @@
public final class DtmbFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "BANDWIDTH_",
+ @IntDef(prefix = "BANDWIDTH_",
value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_6MHZ, BANDWIDTH_8MHZ})
@Retention(RetentionPolicy.SOURCE)
public @interface Bandwidth {}
@@ -68,8 +67,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "TIME_INTERLEAVE_MODE_",
+ @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
TIME_INTERLEAVE_MODE_TIMER_INT_240, TIME_INTERLEAVE_MODE_TIMER_INT_720})
@Retention(RetentionPolicy.SOURCE)
@@ -97,8 +95,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "GUARD_INTERVAL_",
+ @IntDef(prefix = "GUARD_INTERVAL_",
value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
GUARD_INTERVAL_PN_420_VARIOUS, GUARD_INTERVAL_PN_595_CONST,
GUARD_INTERVAL_PN_945_VARIOUS, GUARD_INTERVAL_PN_420_CONST,
@@ -143,8 +140,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_CONSTELLATION_UNDEFINED, MODULATION_CONSTELLATION_AUTO,
MODULATION_CONSTELLATION_4QAM, MODULATION_CONSTELLATION_4QAM_NR,
MODULATION_CONSTELLATION_16QAM, MODULATION_CONSTELLATION_32QAM,
@@ -187,8 +183,7 @@
FrontendDtmbModulation.CONSTELLATION_64QAM;
/** @hide */
- @IntDef(flag = true,
- prefix = "CODERATE_",
+ @IntDef(prefix = "CODERATE_",
value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_2_5, CODERATE_3_5, CODERATE_4_5})
@Retention(RetentionPolicy.SOURCE)
public @interface CodeRate {}
@@ -215,8 +210,7 @@
public static final int CODERATE_4_5 = FrontendDtmbCodeRate.CODERATE_4_5;
/** @hide */
- @IntDef(flag = true,
- prefix = "TRANSMISSION_MODE_",
+ @IntDef(prefix = "TRANSMISSION_MODE_",
value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
TRANSMISSION_MODE_C1, TRANSMISSION_MODE_C3780})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
index afe953d..45bfc09 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbcFrontendSettings.java
@@ -40,8 +40,7 @@
public class DvbcFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_16QAM,
MODULATION_MOD_32QAM, MODULATION_MOD_64QAM, MODULATION_MOD_128QAM,
MODULATION_MOD_256QAM})
@@ -98,8 +97,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "ANNEX_",
+ @IntDef(prefix = "ANNEX_",
value = {ANNEX_UNDEFINED, ANNEX_A, ANNEX_B, ANNEX_C})
@Retention(RetentionPolicy.SOURCE)
public @interface Annex {}
@@ -159,8 +157,7 @@
android.hardware.tv.tuner.FrontendSpectralInversion.INVERTED;
/** @hide */
- @IntDef(flag = true,
- prefix = "TIME_INTERLEAVE_MODE_",
+ @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
TIME_INTERLEAVE_MODE_128_1_0, TIME_INTERLEAVE_MODE_128_1_1,
TIME_INTERLEAVE_MODE_64_2, TIME_INTERLEAVE_MODE_32_4,
@@ -226,8 +223,7 @@
FrontendCableTimeInterleaveMode.INTERLEAVING_128_4;
/** @hide */
- @IntDef(flag = true,
- prefix = "BANDWIDTH_",
+ @IntDef(prefix = "BANDWIDTH_",
value = {BANDWIDTH_UNDEFINED, BANDWIDTH_5MHZ, BANDWIDTH_6MHZ, BANDWIDTH_7MHZ,
BANDWIDTH_8MHZ})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
index e16f192..56dbb48 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbsFrontendSettings.java
@@ -42,8 +42,7 @@
@SystemApi
public class DvbsFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "SCAN_TYPE_",
+ @IntDef(prefix = "SCAN_TYPE_",
value = {SCAN_TYPE_UNDEFINED, SCAN_TYPE_DIRECT, SCAN_TYPE_DISEQC,
SCAN_TYPE_UNICABLE, SCAN_TYPE_JESS})
@Retention(RetentionPolicy.SOURCE)
@@ -75,8 +74,7 @@
public static final int SCAN_TYPE_JESS = FrontendDvbsScanType.JESS;
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_QPSK,
MODULATION_MOD_8PSK, MODULATION_MOD_16QAM, MODULATION_MOD_16PSK,
MODULATION_MOD_32PSK, MODULATION_MOD_ACM, MODULATION_MOD_8APSK,
@@ -207,8 +205,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "STANDARD_",
+ @IntDef(prefix = "STANDARD_",
value = {STANDARD_AUTO, STANDARD_S, STANDARD_S2, STANDARD_S2X})
@Retention(RetentionPolicy.SOURCE)
public @interface Standard {}
diff --git a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
index d86e9a8..06547e1 100644
--- a/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/DvbtFrontendSettings.java
@@ -42,8 +42,7 @@
public class DvbtFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "TRANSMISSION_MODE_",
+ @IntDef(prefix = "TRANSMISSION_MODE_",
value = {TRANSMISSION_MODE_UNDEFINED, TRANSMISSION_MODE_AUTO,
TRANSMISSION_MODE_2K, TRANSMISSION_MODE_8K, TRANSMISSION_MODE_4K,
TRANSMISSION_MODE_1K, TRANSMISSION_MODE_16K, TRANSMISSION_MODE_32K})
@@ -98,8 +97,7 @@
FrontendDvbtTransmissionMode.MODE_32K_E;
/** @hide */
- @IntDef(flag = true,
- prefix = "BANDWIDTH_",
+ @IntDef(prefix = "BANDWIDTH_",
value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ,
BANDWIDTH_6MHZ, BANDWIDTH_5MHZ, BANDWIDTH_1_7MHZ, BANDWIDTH_10MHZ})
@Retention(RetentionPolicy.SOURCE)
@@ -140,8 +138,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "CONSTELLATION_",
+ @IntDef(prefix = "CONSTELLATION_",
value = {CONSTELLATION_UNDEFINED, CONSTELLATION_AUTO, CONSTELLATION_QPSK,
CONSTELLATION_16QAM, CONSTELLATION_64QAM, CONSTELLATION_256QAM,
CONSTELLATION_QPSK_R, CONSTELLATION_16QAM_R, CONSTELLATION_64QAM_R,
@@ -192,8 +189,7 @@
FrontendDvbtConstellation.CONSTELLATION_256QAM_R;
/** @hide */
- @IntDef(flag = true,
- prefix = "HIERARCHY_",
+ @IntDef(prefix = "HIERARCHY_",
value = {HIERARCHY_UNDEFINED, HIERARCHY_AUTO, HIERARCHY_NON_NATIVE, HIERARCHY_1_NATIVE,
HIERARCHY_2_NATIVE, HIERARCHY_4_NATIVE, HIERARCHY_NON_INDEPTH, HIERARCHY_1_INDEPTH,
HIERARCHY_2_INDEPTH, HIERARCHY_4_INDEPTH})
@@ -243,8 +239,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "CODERATE_",
+ @IntDef(prefix = "CODERATE_",
value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4,
CODERATE_5_6, CODERATE_7_8, CODERATE_3_5, CODERATE_4_5, CODERATE_6_7, CODERATE_8_9})
@Retention(RetentionPolicy.SOURCE)
@@ -296,8 +291,7 @@
public static final int CODERATE_8_9 = FrontendDvbtCoderate.CODERATE_8_9;
/** @hide */
- @IntDef(flag = true,
- prefix = "GUARD_INTERVAL_",
+ @IntDef(prefix = "GUARD_INTERVAL_",
value = {GUARD_INTERVAL_UNDEFINED, GUARD_INTERVAL_AUTO,
GUARD_INTERVAL_1_32, GUARD_INTERVAL_1_16,
GUARD_INTERVAL_1_8, GUARD_INTERVAL_1_4,
@@ -346,8 +340,7 @@
public static final int GUARD_INTERVAL_19_256 = FrontendDvbtGuardInterval.INTERVAL_19_256;
/** @hide */
- @IntDef(flag = true,
- prefix = "STANDARD",
+ @IntDef(prefix = "STANDARD_",
value = {STANDARD_AUTO, STANDARD_T, STANDARD_T2}
)
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
index 38bffec..2f45a70 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendSettings.java
@@ -89,8 +89,7 @@
/** @hide */
- @LongDef(flag = true,
- prefix = "FEC_",
+ @LongDef(prefix = "FEC_",
value = {FEC_UNDEFINED, FEC_AUTO, FEC_1_2, FEC_1_3, FEC_1_4, FEC_1_5, FEC_2_3, FEC_2_5,
FEC_2_9, FEC_3_4, FEC_3_5, FEC_4_5, FEC_4_15, FEC_5_6, FEC_5_9, FEC_6_7, FEC_7_8,
FEC_7_9, FEC_7_15, FEC_8_9, FEC_8_15, FEC_9_10, FEC_9_20, FEC_11_15, FEC_11_20,
diff --git a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
index c1e9b38a..9fbea72 100644
--- a/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
+++ b/media/java/android/media/tv/tuner/frontend/FrontendStatus.java
@@ -25,6 +25,9 @@
import android.media.tv.tuner.TunerVersionChecker;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* A Frontend Status class that contains the metrics of the active frontend.
@@ -1086,22 +1089,26 @@
}
/**
- * Gets an array of all PLPs information of ATSC3 frontend, which includes both tuned and not
+ * Gets a list of all PLPs information of ATSC3 frontend, which includes both tuned and not
* tuned PLPs for currently watching service.
*
- * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version or if HAL
- * doesn't return all PLPs information will throw IllegalStateException. Use
- * {@link TunerVersionChecker#getTunerVersion()} to check the version.
+ * <p>This query is only supported by Tuner HAL 2.0 or higher. Unsupported version will throw
+ * UnsupportedOperationException. Use {@link TunerVersionChecker#getTunerVersion()} to check
+ * the version.
+ *
+ * @return a list of all PLPs information. It is empty if HAL doesn't return all PLPs
+ * information status.
*/
- @SuppressLint("ArrayReturn")
@NonNull
- public Atsc3PlpInfo[] getAllAtsc3PlpInfo() {
- TunerVersionChecker.checkHigherOrEqualVersionTo(
- TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status");
- if (mAllPlpInfo == null) {
- throw new IllegalStateException("Atsc3PlpInfo all status is empty");
+ public List<Atsc3PlpInfo> getAllAtsc3PlpInfo() {
+ if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
+ TunerVersionChecker.TUNER_VERSION_2_0, "Atsc3PlpInfo all status")) {
+ throw new UnsupportedOperationException("Atsc3PlpInfo all status is empty");
}
- return mAllPlpInfo;
+ if (mAllPlpInfo == null) {
+ return Collections.EMPTY_LIST;
+ }
+ return Arrays.asList(mAllPlpInfo);
}
/**
diff --git a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
index 726fe15..7e83d15 100644
--- a/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/Isdbs3FrontendSettings.java
@@ -36,8 +36,7 @@
@SystemApi
public class Isdbs3FrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK,
MODULATION_MOD_QPSK, MODULATION_MOD_8PSK, MODULATION_MOD_16APSK,
MODULATION_MOD_32APSK})
@@ -75,8 +74,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true,
- prefix = "CODERATE_",
+ @IntDef(prefix = "CODERATE_",
value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_3, CODERATE_2_5, CODERATE_1_2,
CODERATE_3_5, CODERATE_2_3, CODERATE_3_4, CODERATE_7_9, CODERATE_4_5,
CODERATE_5_6, CODERATE_7_8, CODERATE_9_10})
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
index 51ec5ae..5029453 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbsFrontendSettings.java
@@ -54,8 +54,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_BPSK,
MODULATION_MOD_QPSK, MODULATION_MOD_TC8PSK})
@Retention(RetentionPolicy.SOURCE)
@@ -84,8 +83,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "CODERATE_",
+ @IntDef(prefix = "CODERATE_",
value = {CODERATE_UNDEFINED, CODERATE_AUTO, CODERATE_1_2, CODERATE_2_3, CODERATE_3_4,
CODERATE_5_6, CODERATE_7_8})
@Retention(RetentionPolicy.SOURCE)
diff --git a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
index 89512a0..f08a514 100644
--- a/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
+++ b/media/java/android/media/tv/tuner/frontend/IsdbtFrontendSettings.java
@@ -40,8 +40,7 @@
@SystemApi
public class IsdbtFrontendSettings extends FrontendSettings {
/** @hide */
- @IntDef(flag = true,
- prefix = "MODULATION_",
+ @IntDef(prefix = "MODULATION_",
value = {MODULATION_UNDEFINED, MODULATION_AUTO, MODULATION_MOD_DQPSK,
MODULATION_MOD_QPSK, MODULATION_MOD_16QAM, MODULATION_MOD_64QAM})
@Retention(RetentionPolicy.SOURCE)
@@ -74,8 +73,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "MODE_",
+ @IntDef(prefix = "MODE_",
value = {MODE_UNDEFINED, MODE_AUTO, MODE_1, MODE_2, MODE_3})
@Retention(RetentionPolicy.SOURCE)
public @interface Mode {}
@@ -103,8 +101,7 @@
/** @hide */
- @IntDef(flag = true,
- prefix = "BANDWIDTH_",
+ @IntDef(prefix = "BANDWIDTH_",
value = {BANDWIDTH_UNDEFINED, BANDWIDTH_AUTO, BANDWIDTH_8MHZ, BANDWIDTH_7MHZ,
BANDWIDTH_6MHZ})
@Retention(RetentionPolicy.SOURCE)
@@ -132,7 +129,7 @@
public static final int BANDWIDTH_6MHZ = FrontendIsdbtBandwidth.BANDWIDTH_6MHZ;
/** @hide */
- @IntDef(flag = true, prefix = "PARTIAL_RECEPTION_FLAG_",
+ @IntDef(prefix = "PARTIAL_RECEPTION_FLAG_",
value = {PARTIAL_RECEPTION_FLAG_UNDEFINED, PARTIAL_RECEPTION_FLAG_FALSE,
PARTIAL_RECEPTION_FLAG_TRUE})
@Retention(RetentionPolicy.SOURCE)
@@ -153,7 +150,7 @@
public static final int PARTIAL_RECEPTION_FLAG_TRUE = FrontendIsdbtPartialReceptionFlag.TRUE;
/** @hide */
- @IntDef(flag = true, prefix = "TIME_INTERLEAVE_MODE_",
+ @IntDef(prefix = "TIME_INTERLEAVE_MODE_",
value = {TIME_INTERLEAVE_MODE_UNDEFINED, TIME_INTERLEAVE_MODE_AUTO,
TIME_INTERLEAVE_MODE_1_0, TIME_INTERLEAVE_MODE_1_4, TIME_INTERLEAVE_MODE_1_8,
TIME_INTERLEAVE_MODE_1_16, TIME_INTERLEAVE_MODE_2_0, TIME_INTERLEAVE_MODE_2_2,
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
similarity index 66%
rename from media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
rename to media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
index fd66d3b..f23794b 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BtProfileConnectionInfoTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/unit/BluetoothProfileConnectionInfoTest.java
@@ -19,7 +19,7 @@
import static org.junit.Assert.assertEquals;
import android.bluetooth.BluetoothProfile;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import androidx.test.runner.AndroidJUnit4;
@@ -27,22 +27,24 @@
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
-public class BtProfileConnectionInfoTest {
+public class BluetoothProfileConnectionInfoTest {
@Test
public void testCoverageA2dp() {
final boolean supprNoisy = false;
final int volume = 42;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpInfo(supprNoisy, volume);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createA2dpInfo(supprNoisy, volume);
assertEquals(info.getProfile(), BluetoothProfile.A2DP);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
assertEquals(info.getVolume(), volume);
}
@Test
public void testCoverageA2dpSink() {
final int volume = 42;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.a2dpSinkInfo(volume);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createA2dpSinkInfo(volume);
assertEquals(info.getProfile(), BluetoothProfile.A2DP_SINK);
assertEquals(info.getVolume(), volume);
}
@@ -50,20 +52,21 @@
@Test
public void testCoveragehearingAid() {
final boolean supprNoisy = true;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.hearingAidInfo(supprNoisy);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createHearingAidInfo(supprNoisy);
assertEquals(info.getProfile(), BluetoothProfile.HEARING_AID);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
}
@Test
public void testCoverageLeAudio() {
final boolean supprNoisy = false;
final boolean isLeOutput = true;
- final BtProfileConnectionInfo info = BtProfileConnectionInfo.leAudio(supprNoisy,
- isLeOutput);
+ final BluetoothProfileConnectionInfo info = BluetoothProfileConnectionInfo
+ .createLeAudioInfo(supprNoisy, isLeOutput);
assertEquals(info.getProfile(), BluetoothProfile.LE_AUDIO);
- assertEquals(info.getSuppressNoisyIntent(), supprNoisy);
- assertEquals(info.getIsLeOutput(), isLeOutput);
+ assertEquals(info.isSuppressNoisyIntent(), supprNoisy);
+ assertEquals(info.isLeOutput(), isLeOutput);
}
}
diff --git a/native/android/libandroid.map.txt b/native/android/libandroid.map.txt
index f9a1774..b7beb6e 100644
--- a/native/android/libandroid.map.txt
+++ b/native/android/libandroid.map.txt
@@ -309,6 +309,9 @@
android_res_nquery; # introduced=29
android_res_nresult; # introduced=29
android_res_nsend; # introduced=29
+ android_tag_socket_with_uid; # introduced=Tiramisu
+ android_tag_socket; # introduced=Tiramisu
+ android_untag_socket; # introduced=Tiramisu
AThermal_acquireManager; # introduced=30
AThermal_releaseManager; # introduced=30
AThermal_getCurrentThermalStatus; # introduced=30
diff --git a/packages/ConnectivityT/framework-t/Android.bp b/packages/ConnectivityT/framework-t/Android.bp
index 327b1fb..6329565 100644
--- a/packages/ConnectivityT/framework-t/Android.bp
+++ b/packages/ConnectivityT/framework-t/Android.bp
@@ -125,14 +125,14 @@
name: "framework-connectivity-ethernet-sources",
srcs: [
"src/android/net/EthernetManager.java",
+ "src/android/net/EthernetNetworkManagementException.java",
+ "src/android/net/EthernetNetworkManagementException.aidl",
"src/android/net/EthernetNetworkSpecifier.java",
+ "src/android/net/EthernetNetworkUpdateRequest.java",
+ "src/android/net/EthernetNetworkUpdateRequest.aidl",
"src/android/net/IEthernetManager.aidl",
+ "src/android/net/IEthernetNetworkManagementListener.aidl",
"src/android/net/IEthernetServiceListener.aidl",
- "src/android/net/IInternalNetworkManagementListener.aidl",
- "src/android/net/InternalNetworkUpdateRequest.java",
- "src/android/net/InternalNetworkUpdateRequest.aidl",
- "src/android/net/InternalNetworkManagementException.java",
- "src/android/net/InternalNetworkManagementException.aidl",
"src/android/net/ITetheredInterfaceCallback.aidl",
],
path: "src",
@@ -158,7 +158,6 @@
name: "framework-connectivity-tiramisu-sources",
srcs: [
":framework-connectivity-ethernet-sources",
- ":framework-connectivity-ipsec-sources",
":framework-connectivity-netstats-sources",
],
visibility: ["//frameworks/base"],
@@ -167,6 +166,7 @@
filegroup {
name: "framework-connectivity-tiramisu-updatable-sources",
srcs: [
+ ":framework-connectivity-ipsec-sources",
":framework-connectivity-nsd-sources",
":framework-connectivity-tiramisu-internal-sources",
],
diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
index 630f902e..577ac54 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
@@ -48,5 +48,14 @@
return new NsdManager(context, service);
}
);
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.IPSEC_SERVICE,
+ IpSecManager.class,
+ (context, serviceBinder) -> {
+ IIpSecService service = IIpSecService.Stub.asInterface(serviceBinder);
+ return new IpSecManager(context, service);
+ }
+ );
}
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
index 77f18e3..e0ce081 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -320,15 +320,15 @@
}
private static final class InternalNetworkManagementListener
- extends IInternalNetworkManagementListener.Stub {
+ extends IEthernetNetworkManagementListener.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
- private final BiConsumer<Network, InternalNetworkManagementException> mListener;
+ private final BiConsumer<Network, EthernetNetworkManagementException> mListener;
InternalNetworkManagementListener(
@NonNull final Executor executor,
- @NonNull final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) {
Objects.requireNonNull(executor, "Pass a non-null executor");
Objects.requireNonNull(listener, "Pass a non-null listener");
mExecutor = executor;
@@ -338,14 +338,14 @@
@Override
public void onComplete(
@Nullable final Network network,
- @Nullable final InternalNetworkManagementException e) {
+ @Nullable final EthernetNetworkManagementException e) {
mExecutor.execute(() -> mListener.accept(network, e));
}
}
private InternalNetworkManagementListener getInternalNetworkManagementListener(
@Nullable final Executor executor,
- @Nullable final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) {
if (null != listener) {
Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
}
@@ -361,9 +361,9 @@
@RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
private void updateConfiguration(
@NonNull String iface,
- @NonNull InternalNetworkUpdateRequest request,
+ @NonNull EthernetNetworkUpdateRequest request,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -377,7 +377,7 @@
private void connectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -391,7 +391,7 @@
private void disconnectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
index dcce706..adf9e5a 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
@@ -16,4 +16,4 @@
package android.net;
- parcelable InternalNetworkManagementException;
\ No newline at end of file
+ parcelable EthernetNetworkManagementException;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
similarity index 73%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
index 798e9c3..a35f28e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
@@ -23,11 +23,11 @@
import java.util.Objects;
/** @hide */
-public final class InternalNetworkManagementException
+public final class EthernetNetworkManagementException
extends RuntimeException implements Parcelable {
/* @hide */
- public InternalNetworkManagementException(@NonNull final String errorMessage) {
+ public EthernetNetworkManagementException(@NonNull final String errorMessage) {
super(errorMessage);
}
@@ -40,7 +40,7 @@
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null || getClass() != obj.getClass()) return false;
- final InternalNetworkManagementException that = (InternalNetworkManagementException) obj;
+ final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj;
return Objects.equals(getMessage(), that.getMessage());
}
@@ -56,16 +56,16 @@
}
@NonNull
- public static final Parcelable.Creator<InternalNetworkManagementException> CREATOR =
- new Parcelable.Creator<InternalNetworkManagementException>() {
+ public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR =
+ new Parcelable.Creator<EthernetNetworkManagementException>() {
@Override
- public InternalNetworkManagementException[] newArray(int size) {
- return new InternalNetworkManagementException[size];
+ public EthernetNetworkManagementException[] newArray(int size) {
+ return new EthernetNetworkManagementException[size];
}
@Override
- public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkManagementException(source.readString());
+ public EthernetNetworkManagementException createFromParcel(@NonNull Parcel source) {
+ return new EthernetNetworkManagementException(source.readString());
}
};
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
index da00cb9..debc348 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.aidl
@@ -16,4 +16,4 @@
package android.net;
- parcelable InternalNetworkUpdateRequest;
\ No newline at end of file
+ parcelable EthernetNetworkUpdateRequest;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index f42c4b7..4d229d2 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -23,7 +23,7 @@
import java.util.Objects;
/** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
+public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
@NonNull
@@ -40,7 +40,7 @@
}
/** @hide */
- public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+ public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
@NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
Objects.requireNonNull(networkCapabilities);
@@ -48,7 +48,7 @@
mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
- private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
+ private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
@@ -56,7 +56,7 @@
@Override
public String toString() {
- return "InternalNetworkUpdateRequest{"
+ return "EthernetNetworkUpdateRequest{"
+ "mIpConfig=" + mIpConfig
+ ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
}
@@ -65,7 +65,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
+ EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
return Objects.equals(that.getIpConfig(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
@@ -88,16 +88,16 @@
}
@NonNull
- public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
- new Parcelable.Creator<InternalNetworkUpdateRequest>() {
+ public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR =
+ new Parcelable.Creator<EthernetNetworkUpdateRequest>() {
@Override
- public InternalNetworkUpdateRequest[] newArray(int size) {
- return new InternalNetworkUpdateRequest[size];
+ public EthernetNetworkUpdateRequest[] newArray(int size) {
+ return new EthernetNetworkUpdateRequest[size];
}
@Override
- public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkUpdateRequest(source);
+ public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
+ return new EthernetNetworkUpdateRequest(source);
}
};
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e688bea..544d02b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
@@ -18,8 +18,8 @@
import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;
-import android.net.IInternalNetworkManagementListener;
-import android.net.InternalNetworkUpdateRequest;
+import android.net.IEthernetNetworkManagementListener;
+import android.net.EthernetNetworkUpdateRequest;
import android.net.ITetheredInterfaceCallback;
/**
@@ -38,8 +38,8 @@
void setIncludeTestInterfaces(boolean include);
void requestTetheredInterface(in ITetheredInterfaceCallback callback);
void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
- void updateConfiguration(String iface, in InternalNetworkUpdateRequest request,
- in IInternalNetworkManagementListener listener);
- void connectNetwork(String iface, in IInternalNetworkManagementListener listener);
- void disconnectNetwork(String iface, in IInternalNetworkManagementListener listener);
+ void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
+ in IEthernetNetworkManagementListener listener);
+ void connectNetwork(String iface, in IEthernetNetworkManagementListener listener);
+ void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
index 69cde3b..93edccf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
@@ -16,10 +16,10 @@
package android.net;
-import android.net.InternalNetworkManagementException;
+import android.net.EthernetNetworkManagementException;
import android.net.Network;
/** @hide */
-oneway interface IInternalNetworkManagementListener {
- void onComplete(in Network network, in InternalNetworkManagementException exception);
+oneway interface IEthernetNetworkManagementListener {
+ void onComplete(in Network network, in EthernetNetworkManagementException exception);
}
\ No newline at end of file
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 24bc91d..d2d6ef0 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -83,7 +83,6 @@
name: "services.connectivity-tiramisu-sources",
srcs: [
":services.connectivity-ethernet-sources",
- ":services.connectivity-ipsec-sources",
":services.connectivity-netstats-sources",
],
path: "src",
@@ -93,6 +92,7 @@
filegroup {
name: "services.connectivity-tiramisu-updatable-sources",
srcs: [
+ ":services.connectivity-ipsec-sources",
":services.connectivity-nsd-sources",
],
path: "src",
diff --git a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
index 179d945..4bc40ea 100644
--- a/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/IpSecService.java
@@ -1008,16 +1008,10 @@
*
* @param context Binder context for this service
*/
- private IpSecService(Context context) {
+ public IpSecService(Context context) {
this(context, new Dependencies());
}
- static IpSecService create(Context context)
- throws InterruptedException {
- final IpSecService service = new IpSecService(context);
- return service;
- }
-
@NonNull
private AppOpsManager getAppOpsManager() {
AppOpsManager appOps = (AppOpsManager) mContext.getSystemService(Context.APP_OPS_SERVICE);
@@ -1054,26 +1048,6 @@
}
}
- /** Called by system server when system is ready. */
- public void systemReady() {
- if (isNetdAlive()) {
- Log.d(TAG, "IpSecService is ready");
- } else {
- Log.wtf(TAG, "IpSecService not ready: failed to connect to NetD Native Service!");
- }
- }
-
- synchronized boolean isNetdAlive() {
- try {
- if (mNetd == null) {
- return false;
- }
- return mNetd.isAlive();
- } catch (RemoteException re) {
- return false;
- }
- }
-
/**
* Checks that the provided InetAddress is valid for use in an IPsec SA. The address must not be
* a wildcard address and must be in a numeric form such as 1.2.3.4 or 2001::1.
@@ -1896,7 +1870,6 @@
mContext.enforceCallingOrSelfPermission(DUMP, TAG);
pw.println("IpSecService dump:");
- pw.println("NetdNativeService Connection: " + (isNetdAlive() ? "alive" : "dead"));
pw.println();
pw.println("mUserResourceTracker:");
diff --git a/packages/SettingsLib/res/values/config.xml b/packages/SettingsLib/res/values/config.xml
index b150e01..45253bb 100644
--- a/packages/SettingsLib/res/values/config.xml
+++ b/packages/SettingsLib/res/values/config.xml
@@ -28,9 +28,4 @@
<!-- Control whether status bar should distinguish HSPA data icon form UMTS
data icon on devices -->
<bool name="config_hspa_data_distinguishable">false</bool>
-
- <integer-array name="config_supportedDreamComplications">
- </integer-array>
- <integer-array name="config_dreamComplicationsEnabledByDefault">
- </integer-array>
</resources>
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
index 99e3160..df19c67 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/A2dpProfile.java
@@ -173,8 +173,10 @@
}
public BluetoothDevice getActiveDevice() {
- if (mService == null) return null;
- return mService.getActiveDevice();
+ if (mBluetoothAdapter == null) return null;
+ final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+ .getActiveDevices(BluetoothProfile.A2DP);
+ return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
}
@Override
@@ -221,7 +223,7 @@
}
public boolean supportsHighQualityAudio(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -234,7 +236,7 @@
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public boolean isHighQualityAudioEnabled(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return false;
}
@@ -260,7 +262,7 @@
}
public void setHighQualityAudioEnabled(BluetoothDevice device, boolean enabled) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
if (bluetoothDevice == null) {
return;
}
@@ -286,7 +288,7 @@
*/
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
public String getHighQualityAudioOptionLabel(BluetoothDevice device) {
- BluetoothDevice bluetoothDevice = (device != null) ? device : mService.getActiveDevice();
+ BluetoothDevice bluetoothDevice = (device != null) ? device : getActiveDevice();
int unknownCodecId = R.string.bluetooth_profile_a2dp_high_quality_unknown_codec;
if (bluetoothDevice == null || !supportsHighQualityAudio(device)
|| getConnectionStatus(device) != BluetoothProfile.STATE_CONNECTED) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
index b11bbde..7e5c124 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HeadsetProfile.java
@@ -132,10 +132,12 @@
}
public BluetoothDevice getActiveDevice() {
- if (mService == null) {
+ if (mBluetoothAdapter == null) {
return null;
}
- return mService.getActiveDevice();
+ final List<BluetoothDevice> activeDevices = mBluetoothAdapter
+ .getActiveDevices(BluetoothProfile.HEADSET);
+ return (activeDevices.size() > 0) ? activeDevices.get(0) : null;
}
public int getAudioState(BluetoothDevice device) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
index dc109ca..6f2d4de 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/HearingAidProfile.java
@@ -173,8 +173,10 @@
}
public List<BluetoothDevice> getActiveDevices() {
- if (mService == null) return new ArrayList<>();
- return mService.getActiveDevices();
+ if (mBluetoothAdapter == null) {
+ return new ArrayList<>();
+ }
+ return mBluetoothAdapter.getActiveDevices(BluetoothProfile.HEARING_AID);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
index 209507a..db6d41e 100644
--- a/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
+++ b/packages/SettingsLib/src/com/android/settingslib/bluetooth/LeAudioProfile.java
@@ -21,12 +21,12 @@
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_ALLOWED;
import static android.bluetooth.BluetoothProfile.CONNECTION_POLICY_FORBIDDEN;
-import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothClass;
import android.bluetooth.BluetoothCodecConfig;
import android.bluetooth.BluetoothCodecStatus;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothLeAudio;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothUuid;
import android.content.Context;
@@ -177,10 +177,10 @@
}
public List<BluetoothDevice> getActiveDevices() {
- if (mService == null) {
+ if (mBluetoothAdapter == null) {
return new ArrayList<>();
}
- return mService.getActiveDevices();
+ return mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
}
@Override
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 6bf43e5..a000c09 100644
--- a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
+++ b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
@@ -151,13 +151,13 @@
.map(ComponentName::unflattenFromString)
.collect(Collectors.toSet());
- mSupportedComplications =
- Arrays.stream(resources.getIntArray(R.array.config_supportedDreamComplications))
- .boxed()
- .collect(Collectors.toSet());
+ mSupportedComplications = Arrays.stream(resources.getIntArray(
+ com.android.internal.R.array.config_supportedDreamComplications))
+ .boxed()
+ .collect(Collectors.toSet());
- mDefaultEnabledComplications = Arrays.stream(
- resources.getIntArray(R.array.config_dreamComplicationsEnabledByDefault))
+ mDefaultEnabledComplications = Arrays.stream(resources.getIntArray(
+ com.android.internal.R.array.config_dreamComplicationsEnabledByDefault))
.boxed()
// A complication can only be enabled by default if it is also supported.
.filter(mSupportedComplications::contains)
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
index f167721..d7b366e 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/bluetooth/A2dpProfileTest.java
@@ -60,6 +60,8 @@
private BluetoothDevice mDevice;
@Mock
private BluetoothA2dp mBluetoothA2dp;
+ @Mock
+ private BluetoothAdapter mBluetoothAdapter;
private BluetoothProfile.ServiceListener mServiceListener;
private A2dpProfile mProfile;
@@ -72,7 +74,8 @@
mProfile = new A2dpProfile(mContext, mDeviceManager, mProfileManager);
mServiceListener = mShadowBluetoothAdapter.getServiceListener();
mServiceListener.onServiceConnected(BluetoothProfile.A2DP, mBluetoothA2dp);
- when(mBluetoothA2dp.getActiveDevice()).thenReturn(mDevice);
+ when(mBluetoothAdapter.getActiveDevices(eq(BluetoothProfile.A2DP)))
+ .thenReturn(Arrays.asList(mDevice));
}
@Test
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
index 53d4653..86f7850 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/dream/DreamBackendTest.java
@@ -24,8 +24,6 @@
import android.content.Context;
import android.content.res.Resources;
-import com.android.settingslib.R;
-
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -53,10 +51,15 @@
final Resources res = mock(Resources.class);
when(mContext.getResources()).thenReturn(res);
- when(res.getIntArray(R.array.config_supportedDreamComplications)).thenReturn(
+ when(res.getIntArray(
+ com.android.internal.R.array.config_supportedDreamComplications)).thenReturn(
SUPPORTED_DREAM_COMPLICATIONS);
- when(res.getIntArray(R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
+ when(res.getIntArray(
+ com.android.internal.R.array.config_dreamComplicationsEnabledByDefault)).thenReturn(
DEFAULT_DREAM_COMPLICATIONS);
+ when(res.getStringArray(
+ com.android.internal.R.array.config_disabledDreamComponents)).thenReturn(
+ new String[]{});
mBackend = new DreamBackend(mContext);
}
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index ca90fbe..6f4cd4a 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -533,6 +533,7 @@
<!-- Permission needed for CTS test - WifiManagerTest -->
<uses-permission android:name="android.permission.WIFI_ACCESS_COEX_UNSAFE_CHANNELS" />
<uses-permission android:name="android.permission.WIFI_UPDATE_COEX_UNSAFE_CHANNELS" />
+ <uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES" />
<uses-permission android:name="android.permission.OVERRIDE_WIFI_CONFIG" />
<!-- Permission required for CTS tests to enable/disable rate limiting toasts. -->
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index f83431b..776a511 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -873,12 +873,6 @@
android:singleUser="true"
android:permission="android.permission.BIND_DREAM_SERVICE" />
- <!-- Service for external clients to do media transfer -->
- <!-- TODO(b/203800643): Export and guard with a permission. -->
- <service
- android:name=".media.taptotransfer.sender.MediaTttSenderService"
- />
-
<!-- Service for external clients to notify us of nearby media devices -->
<!-- TODO(b/216313420): Export and guard with a permission. -->
<service
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
index 08d217d..4540b77 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/ActivityLaunchAnimator.kt
@@ -301,6 +301,7 @@
* The intent was started. If [willAnimate] is false, nothing else will happen and the
* animation will not be started.
*/
+ @JvmDefault
fun onIntentStarted(willAnimate: Boolean) {}
/**
@@ -308,6 +309,7 @@
* this if the animation was already started, i.e. if [onLaunchAnimationStart] was called
* before the cancellation.
*/
+ @JvmDefault
fun onLaunchAnimationCancelled() {}
}
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
index f7a7603..3051d80 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/DialogLaunchAnimator.kt
@@ -19,6 +19,7 @@
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
+import android.app.ActivityManager
import android.app.Dialog
import android.graphics.Color
import android.graphics.Rect
@@ -45,7 +46,8 @@
class DialogLaunchAnimator @JvmOverloads constructor(
private val dreamManager: IDreamManager,
private val launchAnimator: LaunchAnimator = LaunchAnimator(TIMINGS, INTERPOLATORS),
- private var isForTesting: Boolean = false
+ // TODO(b/217621394): Remove special handling for low-RAM devices after animation sync is fixed
+ private var forceDisableSynchronization: Boolean = ActivityManager.isLowRamDeviceStatic()
) {
private companion object {
private val TIMINGS = ActivityLaunchAnimator.TIMINGS
@@ -111,7 +113,7 @@
dialog = dialog,
animateBackgroundBoundsChange,
animatedParent,
- isForTesting
+ forceDisableSynchronization
)
openedDialogs.add(animatedDialog)
@@ -187,10 +189,9 @@
private val parentAnimatedDialog: AnimatedDialog? = null,
/**
- * Whether we are currently running in a test, in which case we need to disable
- * synchronization.
+ * Whether synchronization should be disabled, which can be useful if we are running in a test.
*/
- private val isForTesting: Boolean
+ private val forceDisableSynchronization: Boolean
) {
/**
* The DecorView of this dialog window.
@@ -420,8 +421,9 @@
* (or inversely, removed from the UI when the touch surface is made visible).
*/
private fun synchronizeNextDraw(then: () -> Unit) {
- if (isForTesting || !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
- !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
+ if (forceDisableSynchronization ||
+ !touchSurface.isAttachedToWindow || touchSurface.viewRootImpl == null ||
+ !decorView.isAttachedToWindow || decorView.viewRootImpl == null) {
// No need to synchronize if either the touch surface or dialog view is not attached
// to a window.
then()
diff --git a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
index ebe96eb..77386cf 100644
--- a/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
+++ b/packages/SystemUI/animation/src/com/android/systemui/animation/LaunchAnimator.kt
@@ -100,9 +100,11 @@
* needed for the animation. [isExpandingFullyAbove] will be true if the window is expanding
* fully above the [launchContainer].
*/
+ @JvmDefault
fun onLaunchAnimationStart(isExpandingFullyAbove: Boolean) {}
/** The animation made progress and the expandable view [state] should be updated. */
+ @JvmDefault
fun onLaunchAnimationProgress(state: State, progress: Float, linearProgress: Float) {}
/**
@@ -110,6 +112,7 @@
* called previously. This is typically used to clean up the resources initialized when the
* animation was started.
*/
+ @JvmDefault
fun onLaunchAnimationEnd(isExpandingFullyAbove: Boolean) {}
}
diff --git a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
index 29221aa..208825c 100644
--- a/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
+++ b/packages/SystemUI/monet/src/com/android/systemui/monet/ColorScheme.kt
@@ -103,6 +103,20 @@
n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 32.0))
)),
+ RAINBOW(CoreSpec(
+ a1 = TonalSpec(chroma = Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0)),
+ a3 = TonalSpec(Hue(HueStrategy.ADD, 60.0), Chroma(ChromaStrategy.EQ, 24.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 0.0))
+ )),
+ FRUIT_SALAD(CoreSpec(
+ a1 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.GTE, 48.0)),
+ a2 = TonalSpec(Hue(HueStrategy.SUBTRACT, 50.0), Chroma(ChromaStrategy.EQ, 36.0)),
+ a3 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 36.0)),
+ n1 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 10.0)),
+ n2 = TonalSpec(chroma = Chroma(ChromaStrategy.EQ, 16.0))
+ )),
}
class ColorScheme(
diff --git a/packages/SystemUI/res-keyguard/layout/footer_actions.xml b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
index ecb3cb3..339cab4 100644
--- a/packages/SystemUI/res-keyguard/layout/footer_actions.xml
+++ b/packages/SystemUI/res-keyguard/layout/footer_actions.xml
@@ -19,8 +19,10 @@
<com.android.systemui.qs.FooterActionsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
- android:layout_height="48dp"
- android:gravity="center_vertical">
+ android:layout_height="@dimen/qs_footer_height"
+ android:gravity="center_vertical"
+ android:layout_gravity="bottom"
+>
<com.android.systemui.statusbar.phone.MultiUserSwitch
android:id="@+id/multi_user_switch"
diff --git a/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
new file mode 100644
index 0000000..95bdd89
--- /dev/null
+++ b/packages/SystemUI/res-keyguard/layout/new_footer_actions.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+** Copyright 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.
+-->
+
+<!-- Action buttons for footer in QS/QQS, containing settings button, power off button etc -->
+<com.android.systemui.qs.FooterActionsView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_height"
+ android:gravity="center_vertical"
+ android:layout_gravity="bottom"
+>
+
+ <View
+ android:layout_height="1dp"
+ android:layout_width="0dp"
+ android:layout_weight="1"
+ />
+
+ <LinearLayout
+ android:layout_height="match_parent"
+ android:layout_width="wrap_content"
+ >
+
+ <com.android.systemui.statusbar.phone.MultiUserSwitch
+ android:id="@+id/multi_user_switch"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:background="@drawable/qs_footer_action_circle"
+ android:focusable="true">
+
+ <ImageView
+ android:id="@+id/multi_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_expanded_size"
+ android:layout_height="@dimen/multi_user_avatar_expanded_size"
+ android:layout_gravity="center"
+ android:scaleType="centerInside" />
+ </com.android.systemui.statusbar.phone.MultiUserSwitch>
+
+ <com.android.systemui.statusbar.AlphaOptimizedFrameLayout
+ android:id="@+id/settings_button_container"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_marginEnd="@dimen/qs_tile_margin_horizontal"
+ android:background="@drawable/qs_footer_action_circle"
+ android:clipChildren="false"
+ android:clipToPadding="false">
+
+ <com.android.systemui.statusbar.phone.SettingsButton
+ android:id="@+id/settings_button"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:layout_gravity="center"
+ android:background="@android:color/transparent"
+ android:contentDescription="@string/accessibility_quick_settings_settings"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:scaleType="centerInside"
+ android:src="@drawable/ic_settings"
+ android:tint="?android:attr/textColorPrimary" />
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/tuner_icon"
+ android:layout_width="8dp"
+ android:layout_height="8dp"
+ android:layout_gravity="center_horizontal|bottom"
+ android:layout_marginBottom="@dimen/qs_footer_icon_padding"
+ android:src="@drawable/tuner"
+ android:tint="?android:attr/textColorTertiary"
+ android:visibility="invisible" />
+
+ </com.android.systemui.statusbar.AlphaOptimizedFrameLayout>
+
+ <com.android.systemui.statusbar.AlphaOptimizedImageView
+ android:id="@+id/pm_lite"
+ android:layout_width="@dimen/qs_footer_action_button_size"
+ android:layout_height="@dimen/qs_footer_action_button_size"
+ android:background="@drawable/qs_footer_action_circle_color"
+ android:clickable="true"
+ android:clipToPadding="false"
+ android:focusable="true"
+ android:padding="@dimen/qs_footer_icon_padding"
+ android:src="@*android:drawable/ic_lock_power_off"
+ android:contentDescription="@string/accessibility_quick_settings_power_menu"
+ android:tint="?androidprv:attr/textColorOnAccent" />
+
+ </LinearLayout>
+</com.android.systemui.qs.FooterActionsView>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_media_next.xml
similarity index 69%
rename from libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
rename to packages/SystemUI/res/drawable/ic_media_next.xml
index ff57406..016653b 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
+++ b/packages/SystemUI/res/drawable/ic_media_next.xml
@@ -12,14 +12,15 @@
~ 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.
+ ~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="12dp"
+ android:height="12dp"
+ android:viewportWidth="12"
+ android:viewportHeight="12">
<path
- android:fillColor="@android:color/system_neutral2_400"
- android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
-</vector>
\ No newline at end of file
+ android:pathData="M0,12L8.5,6L0,0V12ZM2,3.86L5.03,6L2,8.14V3.86ZM12,0H10V12H12V0Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_media_pause.xml
similarity index 67%
copy from libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
copy to packages/SystemUI/res/drawable/ic_media_pause.xml
index ff57406..1f4b2cf 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
+++ b/packages/SystemUI/res/drawable/ic_media_pause.xml
@@ -12,14 +12,15 @@
~ 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.
+ ~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="14dp"
+ android:height="16dp"
+ android:viewportWidth="14"
+ android:viewportHeight="16">
<path
- android:fillColor="@android:color/system_neutral2_400"
- android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
+ android:pathData="M9.1818,15.6363H13.5455V0.3635H9.1818V15.6363ZM0.4546,15.6363H4.8182V0.3635H0.4546V15.6363Z"
+ android:fillColor="#FFFFFF"
+ android:fillType="evenOdd"/>
</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_media_play.xml
similarity index 69%
copy from libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
copy to packages/SystemUI/res/drawable/ic_media_play.xml
index ff57406..0eac1ad 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
+++ b/packages/SystemUI/res/drawable/ic_media_play.xml
@@ -12,14 +12,15 @@
~ 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.
+ ~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
<path
- android:fillColor="@android:color/system_neutral2_400"
- android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
+ android:pathData="M20,12L6,21V3L20,12ZM15.26,12L8.55,7.68V16.32L15.26,12Z"
+ android:fillColor="#FFFFFF"
+ android:fillType="evenOdd"/>
</vector>
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml b/packages/SystemUI/res/drawable/ic_media_prev.xml
similarity index 69%
copy from libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
copy to packages/SystemUI/res/drawable/ic_media_prev.xml
index ff57406..b4aeed4 100644
--- a/libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
+++ b/packages/SystemUI/res/drawable/ic_media_prev.xml
@@ -12,14 +12,15 @@
~ 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.
+ ~ limitations under the License
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
- android:width="24dp"
- android:height="24dp"
- android:viewportWidth="24"
- android:viewportHeight="24">
+ android:width="12dp"
+ android:height="12dp"
+ android:viewportWidth="12"
+ android:viewportHeight="12">
<path
- android:fillColor="@android:color/system_neutral2_400"
- android:pathData="M16.59,8.59L12.0,13.17 7.41,8.59 6.0,10.0l6.0,6.0 6.0,-6.0z"/>
-</vector>
\ No newline at end of file
+ android:pathData="M0,0H2V12H0V0ZM3.5,6L12,12V0L3.5,6ZM6.97,6L10,8.14V3.86L6.97,6Z"
+ android:fillColor="#ffffff"
+ android:fillType="evenOdd"/>
+</vector>
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
new file mode 100644
index 0000000..f54c30f
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset"
+ android:insetLeft="@dimen/qs_footer_action_inset"
+ android:insetRight="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="?attr/offStateColor"/>
+ </shape>
+ </item>
+
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
new file mode 100644
index 0000000..1a323bc
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_footer_action_circle_color.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+<inset xmlns:android="http://schemas.android.com/apk/res/android"
+ android:insetTop="@dimen/qs_footer_action_inset"
+ android:insetBottom="@dimen/qs_footer_action_inset"
+ android:insetLeft="@dimen/qs_footer_action_inset"
+ android:insetRight="@dimen/qs_footer_action_inset">
+ <ripple
+ android:color="?android:attr/colorControlHighlight">
+ <item android:id="@android:id/mask">
+ <shape android:shape="oval">
+ <solid android:color="@android:color/white"/>
+ </shape>
+ </item>
+ <item>
+ <shape android:shape="oval">
+ <solid android:color="?android:attr/colorAccent"/>
+ </shape>
+ </item>
+
+ </ripple>
+</inset>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/qs_media_scrim.xml b/packages/SystemUI/res/drawable/qs_media_scrim.xml
new file mode 100644
index 0000000..2ec319c
--- /dev/null
+++ b/packages/SystemUI/res/drawable/qs_media_scrim.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="@dimen/notification_corner_radius"/>
+ <!-- gradient from 25% in the center to 100% at edges -->
+ <gradient
+ android:type="radial"
+ android:gradientRadius="100%p"
+ android:startColor="#40000000"
+ android:endColor="#FF000000" />
+</shape>
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 4817d45..c58c001 100644
--- a/packages/SystemUI/res/layout/clipboard_overlay.xml
+++ b/packages/SystemUI/res/layout/clipboard_overlay.xml
@@ -14,126 +14,137 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.clipboardoverlay.DraggableConstraintLayout
+<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
- android:theme="@style/FloatingOverlay"
android:alpha="0"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
- android:id="@+id/actions_container_background"
- android:visibility="gone"
- android:layout_height="0dp"
- android:layout_width="0dp"
- android:elevation="1dp"
- android:background="@drawable/action_chip_container_background"
- android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
- app:layout_constraintBottom_toBottomOf="@+id/actions_container"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toTopOf="@+id/actions_container"
- app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
- <HorizontalScrollView
- android:id="@+id/actions_container"
- android:layout_width="0dp"
- android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
- android:paddingEnd="@dimen/overlay_action_container_padding_right"
- android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
- android:elevation="1dp"
- android:scrollbars="none"
- app:layout_constraintHorizontal_bias="0"
- app:layout_constraintWidth_percent="1.0"
- app:layout_constraintWidth_max="wrap"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintStart_toEndOf="@+id/preview_border"
- app:layout_constraintEnd_toEndOf="parent">
- <LinearLayout
- android:id="@+id/actions"
+ android:id="@+id/background_protection"
+ android:layout_height="@dimen/overlay_bg_protection_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ android:src="@drawable/overlay_actions_background_protection"/>
+ <com.android.systemui.clipboardoverlay.DraggableConstraintLayout
+ android:id="@+id/clipboard_ui"
+ android:theme="@style/FloatingOverlay"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+ <ImageView
+ android:id="@+id/actions_container_background"
+ android:visibility="gone"
+ android:layout_height="0dp"
+ android:layout_width="0dp"
+ android:elevation="1dp"
+ android:background="@drawable/action_chip_container_background"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
+ app:layout_constraintBottom_toBottomOf="@+id/actions_container"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="@+id/actions_container"
+ app:layout_constraintEnd_toEndOf="@+id/actions_container"/>
+ <HorizontalScrollView
+ android:id="@+id/actions_container"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:layout_marginEnd="@dimen/overlay_action_container_margin_horizontal"
+ android:paddingEnd="@dimen/overlay_action_container_padding_right"
+ android:paddingVertical="@dimen/overlay_action_container_padding_vertical"
+ android:elevation="1dp"
+ android:scrollbars="none"
+ app:layout_constraintHorizontal_bias="0"
+ app:layout_constraintWidth_percent="1.0"
+ app:layout_constraintWidth_max="wrap"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintStart_toEndOf="@+id/preview_border"
+ app:layout_constraintEnd_toEndOf="parent">
+ <LinearLayout
+ android:id="@+id/actions"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:animateLayoutChanges="true">
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/remote_copy_chip"/>
+ <include layout="@layout/overlay_action_chip"
+ android:id="@+id/edit_chip"/>
+ </LinearLayout>
+ </HorizontalScrollView>
+ <View
+ android:id="@+id/preview_border"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ android:layout_marginStart="@dimen/overlay_offset_x"
+ android:layout_marginBottom="@dimen/overlay_offset_y"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
+ android:elevation="@dimen/overlay_preview_elevation"
+ app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
+ app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
+ android:background="@drawable/overlay_border"/>
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/clipboard_preview_end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:animateLayoutChanges="true">
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/remote_copy_chip"/>
- <include layout="@layout/overlay_action_chip"
- android:id="@+id/edit_chip"/>
- </LinearLayout>
- </HorizontalScrollView>
- <View
- android:id="@+id/preview_border"
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_marginStart="@dimen/overlay_offset_x"
- android:layout_marginBottom="@dimen/overlay_offset_y"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintBottom_toBottomOf="@id/actions_container_background"
- android:elevation="@dimen/overlay_preview_elevation"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview_end"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview_top"
- android:background="@drawable/overlay_border"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_end"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierMargin="@dimen/overlay_border_width"
- app:barrierDirection="end"
- app:constraint_referenced_ids="clipboard_preview"/>
- <androidx.constraintlayout.widget.Barrier
- android:id="@+id/clipboard_preview_top"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- app:barrierDirection="top"
- app:barrierMargin="@dimen/overlay_border_width_neg"
- app:constraint_referenced_ids="clipboard_preview"/>
- <FrameLayout
- android:id="@+id/clipboard_preview"
- android:elevation="@dimen/overlay_preview_elevation"
- android:background="@drawable/overlay_preview_background"
- android:clipChildren="true"
- android:clipToOutline="true"
- android:clipToPadding="true"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_margin="@dimen/overlay_border_width"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- app:layout_constraintBottom_toBottomOf="@id/preview_border"
- app:layout_constraintStart_toStartOf="@id/preview_border"
- app:layout_constraintEnd_toEndOf="@id/preview_border"
- app:layout_constraintTop_toTopOf="@id/preview_border">
- <TextView android:id="@+id/text_preview"
- android:textFontWeight="500"
- android:padding="8dp"
- android:gravity="center|start"
- android:ellipsize="end"
- android:autoSizeTextType="uniform"
- android:autoSizeMinTextSize="10sp"
- android:autoSizeMaxTextSize="200sp"
- android:textColor="?android:attr/textColorPrimary"
- android:layout_width="@dimen/clipboard_preview_size"
- android:layout_height="@dimen/clipboard_preview_size"/>
- <ImageView
- android:id="@+id/image_preview"
- android:scaleType="fitCenter"
- android:adjustViewBounds="true"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </FrameLayout>
- <FrameLayout
- android:id="@+id/dismiss_button"
- android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
- android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
- android:elevation="@dimen/overlay_dismiss_button_elevation"
- android:visibility="gone"
- app:layout_constraintStart_toEndOf="@id/clipboard_preview"
- app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
- app:layout_constraintTop_toTopOf="@id/clipboard_preview"
- app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
- android:contentDescription="@string/clipboard_dismiss_description">
- <ImageView
- android:id="@+id/dismiss_image"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_margin="@dimen/overlay_dismiss_button_margin"
- android:src="@drawable/overlay_cancel"/>
- </FrameLayout>
-</com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
+ app:barrierMargin="@dimen/overlay_border_width"
+ app:barrierDirection="end"
+ app:constraint_referenced_ids="clipboard_preview"/>
+ <androidx.constraintlayout.widget.Barrier
+ android:id="@+id/clipboard_preview_top"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ app:barrierDirection="top"
+ app:barrierMargin="@dimen/overlay_border_width_neg"
+ app:constraint_referenced_ids="clipboard_preview"/>
+ <FrameLayout
+ android:id="@+id/clipboard_preview"
+ android:elevation="@dimen/overlay_preview_elevation"
+ android:background="@drawable/overlay_preview_background"
+ android:clipChildren="true"
+ android:clipToOutline="true"
+ android:clipToPadding="true"
+ android:layout_width="@dimen/clipboard_preview_size"
+ android:layout_margin="@dimen/overlay_border_width"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ app:layout_constraintBottom_toBottomOf="@id/preview_border"
+ app:layout_constraintStart_toStartOf="@id/preview_border"
+ app:layout_constraintEnd_toEndOf="@id/preview_border"
+ app:layout_constraintTop_toTopOf="@id/preview_border">
+ <TextView android:id="@+id/text_preview"
+ android:textFontWeight="500"
+ android:padding="8dp"
+ android:gravity="center|start"
+ android:ellipsize="end"
+ android:autoSizeTextType="uniform"
+ android:autoSizeMinTextSize="10sp"
+ android:autoSizeMaxTextSize="200sp"
+ android:textColor="?android:attr/textColorPrimary"
+ android:layout_width="@dimen/clipboard_preview_size"
+ android:layout_height="@dimen/clipboard_preview_size"/>
+ <ImageView
+ android:id="@+id/image_preview"
+ android:scaleType="fitCenter"
+ android:adjustViewBounds="true"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"/>
+ </FrameLayout>
+ <FrameLayout
+ android:id="@+id/dismiss_button"
+ android:layout_width="@dimen/overlay_dismiss_button_tappable_size"
+ android:layout_height="@dimen/overlay_dismiss_button_tappable_size"
+ android:elevation="@dimen/overlay_dismiss_button_elevation"
+ android:visibility="gone"
+ app:layout_constraintStart_toEndOf="@id/clipboard_preview"
+ app:layout_constraintEnd_toEndOf="@id/clipboard_preview"
+ app:layout_constraintTop_toTopOf="@id/clipboard_preview"
+ app:layout_constraintBottom_toTopOf="@id/clipboard_preview"
+ android:contentDescription="@string/clipboard_dismiss_description">
+ <ImageView
+ android:id="@+id/dismiss_image"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_margin="@dimen/overlay_dismiss_button_margin"
+ android:src="@drawable/overlay_cancel"/>
+ </FrameLayout>
+ </com.android.systemui.clipboardoverlay.DraggableConstraintLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
index a41d34f..82c8d5f 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/dream_overlay_complication_clock_time_padding_left"
- android:fontFamily="sans-serif-thin"
+ android:fontFamily="@font/clock"
android:textColor="@android:color/white"
android:format12Hour="h:mm"
android:format24Hour="kk:mm"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index cc02fea..51d1608 100644
--- a/packages/SystemUI/res/layout/media_session_view.xml
+++ b/packages/SystemUI/res/layout/media_session_view.xml
@@ -28,6 +28,22 @@
android:background="@drawable/qs_media_background"
android:theme="@style/MediaPlayer">
+ <ImageView
+ android:layout_width="match_parent"
+ android:layout_height="184dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:translationZ="0dp"
+ android:id="@+id/album_art"
+ android:scaleType="centerCrop"
+ android:adjustViewBounds="true"
+ android:clipToOutline="true"
+ android:foreground="@drawable/qs_media_scrim"
+ android:background="@drawable/qs_media_scrim"
+ />
+
<androidx.constraintlayout.widget.Guideline
android:id="@+id/center_vertical_guideline"
android:layout_width="wrap_content"
@@ -281,6 +297,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
+ android:id="@+id/cancel_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|bottom"
@@ -304,6 +321,7 @@
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
+ android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|bottom"
diff --git a/packages/SystemUI/res/layout/media_view.xml b/packages/SystemUI/res/layout/media_view.xml
index b546a9c..9471b9f 100644
--- a/packages/SystemUI/res/layout/media_view.xml
+++ b/packages/SystemUI/res/layout/media_view.xml
@@ -264,6 +264,7 @@
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
+ android:id="@+id/cancel_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|bottom"
@@ -288,6 +289,7 @@
app:layout_constraintTop_toBottomOf="@id/remove_text">
<TextView
+ android:id="@+id/dismiss_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center|bottom"
diff --git a/packages/SystemUI/res/layout/overlay_action_chip.xml b/packages/SystemUI/res/layout/overlay_action_chip.xml
index 6d2d931..e0c20ff 100644
--- a/packages/SystemUI/res/layout/overlay_action_chip.xml
+++ b/packages/SystemUI/res/layout/overlay_action_chip.xml
@@ -17,6 +17,7 @@
<com.android.systemui.screenshot.OverlayActionChip
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/overlay_action_chip"
+ android:theme="@style/FloatingOverlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/overlay_action_chip_margin_start"
diff --git a/packages/SystemUI/res/layout/qs_footer_impl.xml b/packages/SystemUI/res/layout/qs_footer_impl.xml
index 5cd9e94..b6e3499 100644
--- a/packages/SystemUI/res/layout/qs_footer_impl.xml
+++ b/packages/SystemUI/res/layout/qs_footer_impl.xml
@@ -19,7 +19,7 @@
<com.android.systemui.qs.QSFooterView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/qs_footer"
android:layout_width="match_parent"
- android:layout_height="@dimen/qs_footer_height"
+ android:layout_height="wrap_content"
android:layout_marginStart="@dimen/qs_footer_margin"
android:layout_marginEnd="@dimen/qs_footer_margin"
android:layout_marginBottom="@dimen/qs_footers_margin_bottom"
@@ -36,7 +36,7 @@
<LinearLayout
android:layout_width="match_parent"
- android:layout_height="48dp"
+ android:layout_height="@dimen/qs_footer_height"
android:layout_gravity="center_vertical">
<TextView
@@ -80,8 +80,13 @@
</LinearLayout>
- <include layout="@layout/footer_actions"
- android:id="@+id/qs_footer_actions"/>
+ <ViewStub
+ android:id="@+id/footer_stub"
+ android:inflatedId="@+id/qs_footer_actions"
+ android:layout="@layout/footer_actions"
+ android:layout_height="@dimen/qs_footer_height"
+ android:layout_width="match_parent"
+ />
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/qs_panel.xml b/packages/SystemUI/res/layout/qs_panel.xml
index f5c6036..22abd0c 100644
--- a/packages/SystemUI/res/layout/qs_panel.xml
+++ b/packages/SystemUI/res/layout/qs_panel.xml
@@ -51,6 +51,15 @@
android:id="@+id/qs_detail"
layout="@layout/qs_detail" />
+ <ViewStub
+ android:id="@+id/container_stub"
+ android:inflatedId="@+id/qs_footer_actions"
+ android:layout="@layout/new_footer_actions"
+ android:layout_height="@dimen/qs_footer_height"
+ android:layout_width="match_parent"
+ android:layout_gravity="bottom"
+ />
+
<include
android:id="@+id/qs_customize"
layout="@layout/qs_customize_panel"
diff --git a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
index 10a2f4c..2c29f07 100644
--- a/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
+++ b/packages/SystemUI/res/layout/quick_status_bar_expanded_header.xml
@@ -57,16 +57,6 @@
android:focusable="true"
android:paddingBottom="24dp"
android:importantForAccessibility="yes">
-
- <include
- layout="@layout/footer_actions"
- android:id="@+id/qqs_footer_actions"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:layout_marginTop="@dimen/qqs_layout_margin_top"
- android:layout_marginStart="@dimen/qs_footer_margin"
- android:layout_marginEnd="@dimen/qs_footer_margin"
- />
</com.android.systemui.qs.QuickQSPanel>
</RelativeLayout>
diff --git a/packages/SystemUI/res/values-sw720dp-land/dimens.xml b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
index 219fd43..ae557c4 100644
--- a/packages/SystemUI/res/values-sw720dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
+
<!--
* Copyright (c) 2022, The Android Open Source Project
*
@@ -16,6 +17,7 @@
*/
-->
<resources>
+ <dimen name="controls_padding_horizontal">205dp</dimen>
<dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
<dimen name="notification_panel_margin_bottom">56dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-sw720dp/dimens.xml b/packages/SystemUI/res/values-sw720dp/dimens.xml
index 1564ee8..95df594 100644
--- a/packages/SystemUI/res/values-sw720dp/dimens.xml
+++ b/packages/SystemUI/res/values-sw720dp/dimens.xml
@@ -16,8 +16,9 @@
*/
-->
<resources>
-
<!-- gap on either side of status bar notification icons -->
<dimen name="status_bar_icon_padding">1dp</dimen>
+
+ <dimen name="controls_padding_horizontal">75dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/packages/SystemUI/res/values-w500dp/dimens.xml
deleted file mode 100644
index 5ce5cee..0000000
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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>
- <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values-w850dp/dimens.xml b/packages/SystemUI/res/values-w850dp/dimens.xml
deleted file mode 100644
index bb6ba8fb..0000000
--- a/packages/SystemUI/res/values-w850dp/dimens.xml
+++ /dev/null
@@ -1,21 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
- ~ 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>
- <dimen name="controls_padding_horizontal">205dp</dimen>
-</resources>
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index 74bb9e4..dba7290 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -327,7 +327,7 @@
<!-- The height of the quick settings footer that holds the user switcher, settings icon,
etc. -->
- <dimen name="qs_footer_height">96dp</dimen>
+ <dimen name="qs_footer_height">48dp</dimen>
<!-- The size of each of the icon buttons in the QS footer -->
<dimen name="qs_footer_action_button_size">48dp</dimen>
@@ -491,7 +491,7 @@
<dimen name="qs_tile_text_size">14sp</dimen>
<dimen name="qs_panel_padding">16dp</dimen>
<dimen name="qs_dual_tile_padding_horizontal">6dp</dimen>
- <dimen name="qs_panel_padding_bottom">0dp</dimen>
+ <dimen name="qs_panel_padding_bottom">@dimen/qs_footer_height</dimen>
<dimen name="qs_panel_padding_top">48dp</dimen>
<dimen name="qs_detail_header_padding">0dp</dimen>
<dimen name="qs_detail_image_width">56dp</dimen>
diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml
index 4dca0b0..e5cabb0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -2379,11 +2379,15 @@
<string name="fgs_manager_dialog_title">Active apps</string>
<!-- Label of the button to stop an app from running [CHAR LIMIT=12]-->
<string name="fgs_manager_app_item_stop_button_label">Stop</string>
+ <!-- Label of the button to stop an app from running but the app is already stopped and the button is disabled [CHAR LIMIT=12]-->
+ <string name="fgs_manager_app_item_stop_button_stopped_label">Stopped</string>
<!-- Label for button to copy edited text back to the clipboard [CHAR LIMIT=20] -->
<string name="clipboard_edit_text_copy">Copy</string>
<!-- Text informing user that content has been copied to the system clipboard [CHAR LIMIT=NONE] -->
<string name="clipboard_overlay_text_copied">Copied</string>
+ <!-- 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>
</resources>
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
deleted file mode 100644
index 861a4ed..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/*
- * 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.systemui.shared.mediattt;
-
-parcelable DeviceInfo;
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
deleted file mode 100644
index d41aaf3..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.kt
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * 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.systemui.shared.mediattt
-
-import android.os.Parcel
-import android.os.Parcelable
-
-/**
- * Represents a device that can send or receive media. Includes any device information necessary for
- * SysUI to display an informative chip to the user.
- */
-class DeviceInfo(val name: String) : Parcelable {
- constructor(parcel: Parcel) : this(parcel.readString())
-
- override fun writeToParcel(dest: Parcel?, flags: Int) {
- dest?.writeString(name)
- }
-
- override fun describeContents() = 0
-
- override fun toString() = "name: $name"
-
- companion object CREATOR : Parcelable.Creator<DeviceInfo> {
- override fun createFromParcel(parcel: Parcel) = DeviceInfo(parcel)
- override fun newArray(size: Int) = arrayOfNulls<DeviceInfo?>(size)
- }
-}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
deleted file mode 100644
index eb1c9d0..0000000
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/IDeviceSenderService.aidl
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.systemui.shared.mediattt;
-
-import android.media.MediaRoute2Info;
-import com.android.systemui.shared.mediattt.DeviceInfo;
-import com.android.systemui.shared.mediattt.IUndoTransferCallback;
-
-/**
- * An interface that can be invoked to trigger media transfer events on System UI.
- *
- * This interface is for the *sender* device, which is the device currently playing media. This
- * sender device can transfer the media to a different device, called the receiver.
- *
- * System UI will implement this interface and other services will invoke it.
- */
-interface IDeviceSenderService {
- /**
- * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
- * the user can potentially *start* a cast to the receiver device if the user moves their device
- * a bit closer.
- *
- * Important notes:
- * - When this callback triggers, the device is close enough to inform the user that
- * transferring is an option, but the device is *not* close enough to actually initiate a
- * transfer yet.
- * - This callback is for *starting* a cast. It should be used when this device is currently
- * playing media locally and the media should be transferred to be played on the receiver
- * device instead.
- */
- oneway void closeToReceiverToStartCast(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
- /**
- * Invoke to notify System UI that this device (the sender) is close to a receiver device, so
- * the user can potentially *end* a cast on the receiver device if the user moves this device a
- * bit closer.
- *
- * Important notes:
- * - When this callback triggers, the device is close enough to inform the user that
- * transferring is an option, but the device is *not* close enough to actually initiate a
- * transfer yet.
- * - This callback is for *ending* a cast. It should be used when media is currently being
- * played on the receiver device and the media should be transferred to play locally
- * instead.
- */
- oneway void closeToReceiverToEndCast(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
- /**
- * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
- * device has been started.
- *
- * Important notes:
- * - This callback is for *starting* a cast. It should be used when this device is currently
- * playing media locally and the media has started being transferred to the receiver device
- * instead.
- */
- oneway void transferToReceiverTriggered(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
- /**
- * Invoke to notify System UI that a media transfer from the receiver and back to this device
- * (the sender) has been started.
- *
- * Important notes:
- * - This callback is for *ending* a cast. It should be used when media is currently being
- * played on the receiver device and the media has started being transferred to play locally
- * instead.
- */
- oneway void transferToThisDeviceTriggered(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
- /**
- * Invoke to notify System UI that a media transfer from this device (the sender) to a receiver
- * device has finished successfully.
- *
- * Important notes:
- * - This callback is for *starting* a cast. It should be used when this device had previously
- * been playing media locally and the media has successfully been transferred to the
- * receiver device instead.
- *
- * @param undoCallback will be invoked if the user chooses to undo this transfer.
- */
- oneway void transferToReceiverSucceeded(
- in MediaRoute2Info mediaInfo,
- in DeviceInfo otherDeviceInfo,
- in IUndoTransferCallback undoCallback);
-
- /**
- * Invoke to notify System UI that a media transfer from the receiver and back to this device
- * (the sender) has finished successfully.
- *
- * Important notes:
- * - This callback is for *ending* a cast. It should be used when media was previously being
- * played on the receiver device and has been successfully transferred to play locally on
- * this device instead.
- *
- * @param undoCallback will be invoked if the user chooses to undo this transfer.
- */
- oneway void transferToThisDeviceSucceeded(
- in MediaRoute2Info mediaInfo,
- in DeviceInfo otherDeviceInfo,
- in IUndoTransferCallback undoCallback);
-
- /**
- * Invoke to notify System UI that the attempted transfer has failed.
- *
- * This callback will be used for both the transfer that should've *started* playing the media
- * on the receiver and the transfer that should've *ended* the playing on the receiver.
- */
- oneway void transferFailed(in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-
- /**
- * Invoke to notify System UI that this device is no longer close to the receiver device.
- */
- oneway void noLongerCloseToReceiver(
- in MediaRoute2Info mediaInfo, in DeviceInfo otherDeviceInfo);
-}
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index cc10b02..d7a8a7a 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -680,6 +680,13 @@
}
/**
+ * Whether the secure camera is currently showing over the keyguard.
+ */
+ public boolean isSecureCameraLaunchedOverKeyguard() {
+ return mSecureCameraLaunched;
+ }
+
+ /**
* @return a cached version of DreamManager.isDreaming()
*/
public boolean isDreaming() {
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
index 72b40d4..54664f2 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardListener.java
@@ -48,7 +48,7 @@
@Override
public void start() {
if (DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, false)) {
+ DeviceConfig.NAMESPACE_SYSTEMUI, CLIPBOARD_OVERLAY_ENABLED, true)) {
mClipboardManager = requireNonNull(mContext.getSystemService(ClipboardManager.class));
mClipboardManager.addPrimaryClipChangedListener(this);
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
index 8b549b4..f6d6464 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -103,6 +103,7 @@
private final AccessibilityManager mAccessibilityManager;
private final TextClassifier mTextClassifier;
+ private final FrameLayout mContainer;
private final DraggableConstraintLayout mView;
private final ImageView mImagePreview;
private final TextView mTextPreview;
@@ -147,8 +148,9 @@
mWindow = FloatingWindowUtil.getFloatingWindow(mContext);
mWindow.setWindowManager(mWindowManager, null, null);
- mView = (DraggableConstraintLayout)
+ mContainer = (FrameLayout)
LayoutInflater.from(mContext).inflate(R.layout.clipboard_overlay, null);
+ mView = requireNonNull(mContainer.findViewById(R.id.clipboard_ui));
mActionContainerBackground =
requireNonNull(mView.findViewById(R.id.actions_container_background));
mActionContainer = requireNonNull(mView.findViewById(R.id.actions));
@@ -180,7 +182,7 @@
attachWindow();
withWindowAttached(() -> {
- mWindow.setContentView(mView);
+ mWindow.setContentView(mContainer);
updateInsets(mWindowManager.getCurrentWindowMetrics().getWindowInsets());
mView.requestLayout();
mView.post(this::animateIn);
@@ -371,7 +373,7 @@
private ValueAnimator getEnterAnimation() {
ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
- mView.setAlpha(0);
+ mContainer.setAlpha(0);
mDismissButton.setVisibility(View.GONE);
final View previewBorder = requireNonNull(mView.findViewById(R.id.preview_border));
final View actionBackground = requireNonNull(
@@ -383,7 +385,7 @@
}
anim.addUpdateListener(animation -> {
- mView.setAlpha(animation.getAnimatedFraction());
+ mContainer.setAlpha(animation.getAnimatedFraction());
float scale = 0.6f + 0.4f * animation.getAnimatedFraction();
mView.setPivotY(mView.getHeight() - previewBorder.getHeight() / 2f);
mView.setPivotX(actionBackground.getWidth() / 2f);
@@ -394,7 +396,7 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mView.setAlpha(1);
+ mContainer.setAlpha(1);
mTimeoutHandler.resetTimeout();
}
});
@@ -439,7 +441,7 @@
private void reset() {
mView.setTranslationX(0);
- mView.setAlpha(0);
+ mContainer.setAlpha(0);
resetActionChips();
mTimeoutHandler.cancelTimeout();
}
diff --git a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
index be10c35..a57a135 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/EditTextActivity.java
@@ -22,9 +22,12 @@
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
+import android.content.pm.PackageManager;
import android.os.Bundle;
+import android.util.Log;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
+import android.widget.TextView;
import com.android.systemui.R;
@@ -32,8 +35,11 @@
* Lightweight activity for editing text clipboard contents
*/
public class EditTextActivity extends Activity {
+ private static final String TAG = "EditTextActivity";
+
private EditText mEditText;
private ClipboardManager mClipboardManager;
+ private TextView mAttribution;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -42,6 +48,7 @@
findViewById(R.id.copy_button).setOnClickListener((v) -> saveToClipboard());
findViewById(R.id.share).setOnClickListener((v) -> share());
mEditText = findViewById(R.id.edit_text);
+ mAttribution = findViewById(R.id.attribution);
mClipboardManager = requireNonNull(getSystemService(ClipboardManager.class));
}
@@ -53,7 +60,15 @@
finish();
return;
}
- // TODO: put clip attribution in R.id.attribution TextView
+ PackageManager pm = getApplicationContext().getPackageManager();
+ try {
+ CharSequence label = pm.getApplicationLabel(
+ pm.getApplicationInfo(mClipboardManager.getPrimaryClipSource(),
+ PackageManager.ApplicationInfoFlags.of(0)));
+ mAttribution.setText(getResources().getString(R.string.clipboard_edit_source, label));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Package not found: " + mClipboardManager.getPrimaryClipSource(), e);
+ }
mEditText.setText(clip.getItemAt(0).getText());
mEditText.requestFocus();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
index 2160744..77997e4 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayService.java
@@ -31,8 +31,10 @@
import com.android.internal.policy.PhoneWindow;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.Complication;
+import com.android.systemui.dreams.complication.ComplicationUtils;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
@@ -57,6 +59,7 @@
// content area).
private final DreamOverlayContainerViewController mDreamOverlayContainerViewController;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ private final DreamBackend mDreamBackend;
// A reference to the {@link Window} used to hold the dream overlay.
private Window mWindow;
@@ -109,6 +112,7 @@
setCurrentState(Lifecycle.State.CREATED);
mLifecycleRegistry = component.getLifecycleRegistry();
mDreamOverlayTouchMonitor = component.getDreamOverlayTouchMonitor();
+ mDreamBackend = component.getDreamBackend();
mDreamOverlayTouchMonitor.init();
}
@@ -130,6 +134,9 @@
public void onStartDream(@NonNull WindowManager.LayoutParams layoutParams) {
setCurrentState(Lifecycle.State.STARTED);
mExecutor.execute(() -> {
+ mStateController.setAvailableComplicationTypes(
+ ComplicationUtils.convertComplicationTypes(
+ mDreamBackend.getEnabledComplications()));
addOverlayWindowLocked(layoutParams);
setCurrentState(Lifecycle.State.RESUMED);
mStateController.setOverlayActive(true);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
index ac7457d..bc5a52a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayStateController.java
@@ -131,9 +131,7 @@
.filter(complication -> {
@Complication.ComplicationType
final int requiredTypes = complication.getRequiredTypeAvailability();
-
- return requiredTypes == Complication.COMPLICATION_TYPE_NONE
- || (requiredTypes & getAvailableComplicationTypes()) == requiredTypes;
+ return (requiredTypes & getAvailableComplicationTypes()) == requiredTypes;
})
.collect(Collectors.toCollection(HashSet::new))
: mComplications);
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
index 3a2a6ef..a4a0075 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationUtils.java
@@ -25,6 +25,8 @@
import com.android.settingslib.dream.DreamBackend;
+import java.util.Set;
+
/**
* A collection of utility methods for working with {@link Complication}.
*/
@@ -50,4 +52,14 @@
return COMPLICATION_TYPE_NONE;
}
}
+
+ /**
+ * Converts a set of {@link com.android.settingslib.dream.DreamBackend.ComplicationType} to
+ * a combined complications types state.
+ */
+ @Complication.ComplicationType
+ public static int convertComplicationTypes(@DreamBackend.ComplicationType Set<Integer> types) {
+ return types.stream().mapToInt(ComplicationUtils::convertComplicationType).reduce(
+ COMPLICATION_TYPE_NONE, (a, b) -> a | b);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
index 59c52b9..6861c74 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
@@ -44,6 +44,11 @@
mComponentFactory = componentFactory;
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_DATE;
+ }
+
/**
* Create {@link DreamClockDateViewHolder}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
index b0c3a42..936767a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -44,6 +44,11 @@
mComponentFactory = componentFactory;
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_TIME;
+ }
+
/**
* Create {@link DreamClockTimeViewHolder}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
index cbdbef3..f5c5a43 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
@@ -53,6 +53,11 @@
mComponentFactory = componentFactory;
}
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_WEATHER;
+ }
+
/**
* Create {@link DreamWeatherViewHolder}.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
index 053c5b3..d539f5c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
@@ -22,6 +22,7 @@
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.TextClock;
import com.android.internal.util.Preconditions;
import com.android.systemui.R;
@@ -78,6 +79,8 @@
"clock_time_complication_layout_params";
// Order weight of insert into parent container
int INSERT_ORDER_WEIGHT = 0;
+ String TAG_WEIGHT = "'wght' ";
+ int WEIGHT = 200;
/**
* Provides the complication view.
@@ -86,10 +89,12 @@
@DreamClockTimeComplicationScope
@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
static View provideComplicationView(LayoutInflater layoutInflater) {
- return Preconditions.checkNotNull(
- layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
- null, false),
+ final TextClock view = Preconditions.checkNotNull((TextClock)
+ layoutInflater.inflate(R.layout.dream_overlay_complication_clock_time,
+ null, false),
"R.layout.dream_overlay_complication_clock_time did not properly inflated");
+ view.setFontVariationSettings(TAG_WEIGHT + WEIGHT);
+ return view;
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
index 05ab901..60278a9 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayComponent.java
@@ -22,6 +22,7 @@
import androidx.lifecycle.LifecycleRegistry;
import androidx.lifecycle.ViewModelStore;
+import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.dreams.DreamOverlayContainerViewController;
import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.complication.dagger.ComplicationModule;
@@ -68,4 +69,7 @@
/** Builds a {@link DreamOverlayTouchMonitor} */
DreamOverlayTouchMonitor getDreamOverlayTouchMonitor();
+
+ /** Builds a ${@link DreamBackend} */
+ DreamBackend getDreamBackend();
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
index 4eb5cb9..efa063f 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamOverlayModule.java
@@ -17,6 +17,7 @@
package com.android.systemui.dreams.dagger;
import android.content.ContentResolver;
+import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.view.LayoutInflater;
@@ -27,6 +28,7 @@
import androidx.lifecycle.LifecycleRegistry;
import com.android.internal.util.Preconditions;
+import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.battery.BatteryMeterViewController;
@@ -147,4 +149,10 @@
static Lifecycle providesLifecycle(LifecycleOwner lifecycleOwner) {
return lifecycleOwner.getLifecycle();
}
+
+ @Provides
+ @DreamOverlayComponent.DreamOverlayScope
+ static DreamBackend providesDreamBackend(Context context) {
+ return DreamBackend.getInstance(context);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt
deleted file mode 100644
index 42f3512..0000000
--- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialog.kt
+++ /dev/null
@@ -1,141 +0,0 @@
-/*
- * 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.systemui.fgsmanager
-
-import android.content.Context
-import android.os.Bundle
-import android.text.format.DateUtils
-import android.view.LayoutInflater
-import android.view.View
-import android.view.ViewGroup
-import android.widget.Button
-import android.widget.ImageView
-import android.widget.TextView
-import androidx.annotation.GuardedBy
-import androidx.recyclerview.widget.DiffUtil
-import androidx.recyclerview.widget.LinearLayoutManager
-import androidx.recyclerview.widget.RecyclerView
-import com.android.systemui.R
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.fgsmanager.FgsManagerDialogController.RunningApp
-import com.android.systemui.statusbar.phone.SystemUIDialog
-import com.android.systemui.util.time.SystemClock
-import java.util.concurrent.Executor
-
-/**
- * Dialog which shows a list of running foreground services and offers controls to them
- */
-class FgsManagerDialog(
- context: Context,
- private val executor: Executor,
- @Background private val backgroundExecutor: Executor,
- private val systemClock: SystemClock,
- private val fgsManagerDialogController: FgsManagerDialogController
-) : SystemUIDialog(context, R.style.Theme_SystemUI_Dialog) {
-
- private val appListRecyclerView: RecyclerView = RecyclerView(this.context)
- private val adapter: AppListAdapter = AppListAdapter()
-
- init {
- setTitle(R.string.fgs_manager_dialog_title)
- setView(appListRecyclerView)
- }
-
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- appListRecyclerView.layoutManager = LinearLayoutManager(context)
- fgsManagerDialogController.registerDialogForChanges(
- object : FgsManagerDialogController.FgsManagerDialogCallback {
- override fun onRunningAppsChanged(apps: List<RunningApp>) {
- executor.execute {
- adapter.setData(apps)
- }
- }
- }
- )
- appListRecyclerView.adapter = adapter
- backgroundExecutor.execute { adapter.setData(fgsManagerDialogController.runningAppList) }
- }
-
- private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() {
- private val lock = Any()
-
- @GuardedBy("lock")
- private val data: MutableList<RunningApp> = ArrayList()
- override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder {
- return AppItemViewHolder(LayoutInflater.from(context)
- .inflate(R.layout.fgs_manager_app_item, parent, false))
- }
-
- override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) {
- var runningApp: RunningApp
- synchronized(lock) {
- runningApp = data[position]
- }
- with(holder) {
- iconView.setImageDrawable(runningApp.mIcon)
- appLabelView.text = runningApp.mAppLabel
- durationView.text = DateUtils.formatDuration(
- Math.max(systemClock.elapsedRealtime() - runningApp.mTimeStarted, 60000),
- DateUtils.LENGTH_MEDIUM)
- stopButton.setOnClickListener {
- fgsManagerDialogController
- .stopAllFgs(runningApp.mUserId, runningApp.mPackageName)
- }
- }
- }
-
- override fun getItemCount(): Int {
- synchronized(lock) { return data.size }
- }
-
- fun setData(newData: List<RunningApp>) {
- var oldData: List<RunningApp>
- synchronized(lock) {
- oldData = ArrayList(data)
- data.clear()
- data.addAll(newData)
- }
-
- DiffUtil.calculateDiff(object : DiffUtil.Callback() {
- override fun getOldListSize(): Int {
- return oldData.size
- }
-
- override fun getNewListSize(): Int {
- return newData.size
- }
-
- override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int):
- Boolean {
- return oldData[oldItemPosition] == newData[newItemPosition]
- }
-
- override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int):
- Boolean {
- return true // TODO, look into updating the time subtext
- }
- }).dispatchUpdatesTo(this)
- }
- }
-
- private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
- val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label)
- val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration)
- val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon)
- val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt
deleted file mode 100644
index 159ed39..0000000
--- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogController.kt
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * 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.systemui.fgsmanager
-
-import android.content.pm.PackageManager
-import android.content.pm.PackageManager.NameNotFoundException
-import android.graphics.drawable.Drawable
-import android.os.Handler
-import android.os.UserHandle
-import android.util.ArrayMap
-import android.util.Log
-import androidx.annotation.GuardedBy
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.statusbar.policy.RunningFgsController
-import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime
-import javax.inject.Inject
-
-/**
- * Controls events relevant to FgsManagerDialog
- */
-class FgsManagerDialogController @Inject constructor(
- private val packageManager: PackageManager,
- @Background private val backgroundHandler: Handler,
- private val runningFgsController: RunningFgsController
-) : RunningFgsController.Callback {
- private val lock = Any()
- private val clearCacheToken = Any()
-
- @GuardedBy("lock")
- private var runningApps: Map<UserPackageTime, RunningApp>? = null
- @GuardedBy("lock")
- private var listener: FgsManagerDialogCallback? = null
-
- interface FgsManagerDialogCallback {
- fun onRunningAppsChanged(apps: List<RunningApp>)
- }
-
- data class RunningApp(
- val mUserId: Int,
- val mPackageName: String,
- val mAppLabel: CharSequence,
- val mIcon: Drawable,
- val mTimeStarted: Long
- )
-
- val runningAppList: List<RunningApp>
- get() {
- synchronized(lock) {
- if (runningApps == null) {
- onFgsPackagesChangedLocked(runningFgsController.getPackagesWithFgs())
- }
- return convertToRunningAppList(runningApps!!)
- }
- }
-
- fun registerDialogForChanges(callback: FgsManagerDialogCallback) {
- synchronized(lock) {
- runningFgsController.addCallback(this)
- listener = callback
- backgroundHandler.removeCallbacksAndMessages(clearCacheToken)
- }
- }
-
- fun onFinishDialog() {
- synchronized(lock) {
- listener = null
- // Keep data such as icons cached for some time since loading can be slow
- backgroundHandler.postDelayed(
- {
- synchronized(lock) {
- runningFgsController.removeCallback(this)
- runningApps = null
- }
- }, clearCacheToken, RUNNING_APP_CACHE_TIMEOUT_MILLIS)
- }
- }
-
- private fun onRunningAppsChanged(apps: ArrayMap<UserPackageTime, RunningApp>) {
- listener?.let {
- backgroundHandler.post { it.onRunningAppsChanged(convertToRunningAppList(apps)) }
- }
- }
-
- override fun onFgsPackagesChanged(packages: List<UserPackageTime>) {
- backgroundHandler.post {
- synchronized(lock) { onFgsPackagesChangedLocked(packages) }
- }
- }
-
- /**
- * Run on background thread
- */
- private fun onFgsPackagesChangedLocked(packages: List<UserPackageTime>) {
- val newRunningApps = ArrayMap<UserPackageTime, RunningApp>()
- for (packageWithFgs in packages) {
- val ra = runningApps?.get(packageWithFgs)
- if (ra == null) {
- val userId = packageWithFgs.userId
- val packageName = packageWithFgs.packageName
- try {
- val ai = packageManager.getApplicationInfo(packageName, 0)
- var icon = packageManager.getApplicationIcon(ai)
- icon = packageManager.getUserBadgedIcon(icon,
- UserHandle.of(userId))
- val label = packageManager.getApplicationLabel(ai)
- newRunningApps[packageWithFgs] = RunningApp(userId, packageName,
- label, icon, packageWithFgs.startTimeMillis)
- } catch (e: NameNotFoundException) {
- Log.e(LOG_TAG,
- "Application info not found: $packageName", e)
- }
- } else {
- newRunningApps[packageWithFgs] = ra
- }
- }
- runningApps = newRunningApps
- onRunningAppsChanged(newRunningApps)
- }
-
- fun stopAllFgs(userId: Int, packageName: String) {
- runningFgsController.stopFgs(userId, packageName)
- }
-
- companion object {
- private val LOG_TAG = FgsManagerDialogController::class.java.simpleName
- private const val RUNNING_APP_CACHE_TIMEOUT_MILLIS: Long = 20_000
-
- private fun convertToRunningAppList(apps: Map<UserPackageTime, RunningApp>):
- List<RunningApp> {
- val result = mutableListOf<RunningApp>()
- result.addAll(apps.values)
- result.sortWith { a: RunningApp, b: RunningApp ->
- b.mTimeStarted.compareTo(a.mTimeStarted)
- }
- return result
- }
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt b/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt
deleted file mode 100644
index 2874929..0000000
--- a/packages/SystemUI/src/com/android/systemui/fgsmanager/FgsManagerDialogFactory.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.systemui.fgsmanager
-
-import android.content.Context
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.animation.DialogLaunchAnimator
-import android.content.DialogInterface
-import android.view.View
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.util.time.SystemClock
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-/**
- * Factory to create [FgsManagerDialog] instances
- */
-@SysUISingleton
-class FgsManagerDialogFactory
-@Inject constructor(
- private val context: Context,
- @Main private val executor: Executor,
- @Background private val backgroundExecutor: Executor,
- private val systemClock: SystemClock,
- private val dialogLaunchAnimator: DialogLaunchAnimator,
- private val fgsManagerDialogController: FgsManagerDialogController
-) {
-
- val lock = Any()
-
- companion object {
- private var fgsManagerDialog: FgsManagerDialog? = null
- }
-
- /**
- * Creates the dialog if it doesn't exist
- */
- fun create(viewLaunchedFrom: View?) {
- if (fgsManagerDialog == null) {
- fgsManagerDialog = FgsManagerDialog(context, executor, backgroundExecutor,
- systemClock, fgsManagerDialogController)
- fgsManagerDialog!!.setOnDismissListener { i: DialogInterface? ->
- fgsManagerDialogController.onFinishDialog()
- fgsManagerDialog = null
- }
- dialogLaunchAnimator.showFromView(fgsManagerDialog!!, viewLaunchedFrom!!)
- }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.java b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
index c894b70..357a68f 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -107,6 +107,8 @@
public static final ResourceBooleanFlag QS_USER_DETAIL_SHORTCUT =
new ResourceBooleanFlag(503, R.bool.flag_lockscreen_qs_user_detail_shortcut);
+ public static final BooleanFlag NEW_FOOTER = new BooleanFlag(504, false);
+
/***************************************/
// 600- status bar
public static final BooleanFlag COMBINED_STATUS_BAR_SIGNAL_ICONS =
diff --git a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
index d1a103e..de67ba8 100644
--- a/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
+++ b/packages/SystemUI/src/com/android/systemui/globalactions/GlobalActionsPopupMenu.java
@@ -40,6 +40,7 @@
private boolean mIsDropDownMode;
private int mMenuVerticalPadding = 0;
private int mGlobalActionsSidePadding = 0;
+ private int mMaximumWidthThresholdDp = 800;
private ListAdapter mAdapter;
private AdapterView.OnItemLongClickListener mOnItemLongClickListener;
@@ -92,6 +93,8 @@
// width should be between [.5, .9] of screen
int parentWidth = res.getSystem().getDisplayMetrics().widthPixels;
+ float parentDensity = res.getSystem().getDisplayMetrics().density;
+ float parentWidthDp = parentWidth / parentDensity;
int widthSpec = MeasureSpec.makeMeasureSpec(
(int) (parentWidth * 0.9), MeasureSpec.AT_MOST);
int maxWidth = 0;
@@ -101,9 +104,12 @@
int w = child.getMeasuredWidth();
maxWidth = Math.max(w, maxWidth);
}
- int width = Math.max(maxWidth, (int) (parentWidth * 0.5));
- listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding);
+ int width = maxWidth;
+ if (parentWidthDp < mMaximumWidthThresholdDp) {
+ width = Math.max(maxWidth, (int) (parentWidth * 0.5));
+ }
+ listView.setPadding(0, mMenuVerticalPadding, 0, mMenuVerticalPadding);
setWidth(width);
if (getAnchorView().getLayoutDirection() == LayoutDirection.LTR) {
setHorizontalOffset(getAnchorView().getWidth() - mGlobalActionsSidePadding - width);
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
index e88011e..88555ed 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
@@ -233,10 +233,13 @@
mKeyguardViewMediator = keyguardViewMediator;
mKeyguardLifecyclesDispatcher = keyguardLifecyclesDispatcher;
mShellTransitions = shellTransitions;
+ }
- if (shellTransitions != null && Transitions.ENABLE_SHELL_TRANSITIONS) {
- // Nothing here. Initialization for this happens in onCreate.
- } else {
+ @Override
+ public void onCreate() {
+ ((SystemUIApplication) getApplication()).startServicesIfNeeded();
+
+ if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) {
RemoteAnimationDefinition definition = new RemoteAnimationDefinition();
if (sEnableRemoteKeyguardGoingAwayAnimation) {
final RemoteAnimationAdapter exitAnimationAdapter =
@@ -248,22 +251,19 @@
}
if (sEnableRemoteKeyguardOccludeAnimation) {
final RemoteAnimationAdapter occludeAnimationAdapter =
- new RemoteAnimationAdapter(mOccludeAnimationRunner, 0, 0);
+ new RemoteAnimationAdapter(
+ mKeyguardViewMediator.getOccludeAnimationRunner(), 0, 0);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_OCCLUDE,
occludeAnimationAdapter);
+
+ final RemoteAnimationAdapter unoccludeAnimationAdapter =
+ new RemoteAnimationAdapter(
+ mKeyguardViewMediator.getUnoccludeAnimationRunner(), 0, 0);
definition.addRemoteAnimation(TRANSIT_OLD_KEYGUARD_UNOCCLUDE,
- occludeAnimationAdapter);
+ unoccludeAnimationAdapter);
}
ActivityTaskManager.getInstance().registerRemoteAnimationsForDisplay(
DEFAULT_DISPLAY, definition);
- }
- }
-
- @Override
- public void onCreate() {
- ((SystemUIApplication) getApplication()).startServicesIfNeeded();
-
- if (mShellTransitions == null || !Transitions.ENABLE_SHELL_TRANSITIONS) {
return;
}
if (sEnableRemoteKeyguardGoingAwayAnimation) {
@@ -354,33 +354,6 @@
}
};
- private final IRemoteAnimationRunner.Stub mOccludeAnimationRunner =
- new IRemoteAnimationRunner.Stub() {
- @Override // Binder interface
- public void onAnimationStart(@WindowManager.TransitionOldType int transit,
- RemoteAnimationTarget[] apps,
- RemoteAnimationTarget[] wallpapers,
- RemoteAnimationTarget[] nonApps,
- IRemoteAnimationFinishedCallback finishedCallback) {
- Slog.d(TAG, "mOccludeAnimationRunner.onAnimationStart: transit=" + transit);
- try {
- if (transit == TRANSIT_OLD_KEYGUARD_OCCLUDE) {
- mBinder.setOccluded(true /* isOccluded */, true /* animate */);
- } else if (transit == TRANSIT_OLD_KEYGUARD_UNOCCLUDE) {
- mBinder.setOccluded(false /* isOccluded */, false /* animate */);
- }
- // TODO(bc-unlock): Implement (un)occlude animation.
- finishedCallback.onAnimationFinished();
- } catch (RemoteException e) {
- Slog.e(TAG, "RemoteException");
- }
- }
-
- @Override // Binder interface
- public void onAnimationCancelled() {
- }
- };
-
final IRemoteTransition mOccludeAnimation = new IRemoteTransition.Stub() {
@Override
public void startAnimation(IBinder transition, TransitionInfo info,
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
index fd2c6dd..0f08a18 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
@@ -77,11 +77,13 @@
import android.view.RemoteAnimationTarget;
import android.view.SyncRtSurfaceTransactionApplier;
import android.view.View;
+import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.WindowManagerPolicyConstants;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.jank.InteractionJankMonitor;
@@ -89,6 +91,7 @@
import com.android.internal.policy.IKeyguardDismissCallback;
import com.android.internal.policy.IKeyguardExitCallback;
import com.android.internal.policy.IKeyguardStateCallback;
+import com.android.internal.policy.ScreenDecorationsUtils;
import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardConstants;
@@ -102,7 +105,10 @@
import com.android.systemui.CoreStartable;
import com.android.systemui.DejankUtils;
import com.android.systemui.Dumpable;
+import com.android.systemui.R;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.animation.LaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.dagger.qualifiers.UiBackground;
@@ -375,6 +381,9 @@
private int mUnlockSoundId;
private int mTrustedSoundId;
private int mLockSoundStreamId;
+ private final float mPowerButtonY;
+ private final float mWindowCornerRadius;
+
/**
* The animation used for hiding keyguard. This is used to fetch the animation timings if
* WindowManager is not providing us with them.
@@ -815,6 +824,109 @@
}
};
+ /**
+ * Animation launch controller for activities that occlude the keyguard.
+ */
+ private final ActivityLaunchAnimator.Controller mOccludeAnimationController =
+ new ActivityLaunchAnimator.Controller() {
+ @Override
+ public void onLaunchAnimationStart(boolean isExpandingFullyAbove) {
+ setOccluded(true /* occluded */, false /* animate */);
+ }
+
+ @Override
+ public void onLaunchAnimationCancelled() {
+ setOccluded(true /* occluded */, false /* animate */);
+ }
+
+ @NonNull
+ @Override
+ public ViewGroup getLaunchContainer() {
+ return ((ViewGroup) mKeyguardViewControllerLazy.get()
+ .getViewRootImpl().getView());
+ }
+
+ @Override
+ public void setLaunchContainer(@NonNull ViewGroup launchContainer) {
+ // No-op, launch container is always the shade.
+ Log.wtf(TAG, "Someone tried to change the launch container for the "
+ + "ActivityLaunchAnimator, which should never happen.");
+ }
+
+ @NonNull
+ @Override
+ public LaunchAnimator.State createAnimatorState() {
+ final int width = getLaunchContainer().getWidth();
+ final int height = getLaunchContainer().getHeight();
+
+ final float initialHeight = height / 3f;
+ final float initialWidth = width / 3f;
+
+ if (mUpdateMonitor.isSecureCameraLaunchedOverKeyguard()) {
+ // Start the animation near the power button, at one-third size, since the
+ // camera was launched from the power button.
+ return new LaunchAnimator.State(
+ (int) (mPowerButtonY - initialHeight / 2f) /* top */,
+ (int) (mPowerButtonY + initialHeight / 2f) /* bottom */,
+ (int) (width - initialWidth) /* left */,
+ width /* right */,
+ mWindowCornerRadius, mWindowCornerRadius);
+ } else {
+ // Start the animation in the center of the screen, scaled down.
+ return new LaunchAnimator.State(
+ height / 2, height / 2, width / 2, width / 2,
+ mWindowCornerRadius, mWindowCornerRadius);
+ }
+ }
+ };
+
+ /**
+ * Animation controller for activities that unocclude the keyguard. This will play the launch
+ * animation in reverse.
+ */
+ private final ActivityLaunchAnimator.Controller mUnoccludeAnimationController =
+ new ActivityLaunchAnimator.Controller() {
+ @Override
+ public void onLaunchAnimationEnd(boolean isExpandingFullyAbove) {
+ setOccluded(false /* isOccluded */, false /* animate */);
+ }
+
+ @Override
+ public void onLaunchAnimationCancelled() {
+ setOccluded(false /* isOccluded */, false /* animate */);
+ }
+
+ @NonNull
+ @Override
+ public ViewGroup getLaunchContainer() {
+ return ((ViewGroup) mKeyguardViewControllerLazy.get()
+ .getViewRootImpl().getView());
+ }
+
+ @Override
+ public void setLaunchContainer(@NonNull ViewGroup launchContainer) {
+ // No-op, launch container is always the shade.
+ Log.wtf(TAG, "Someone tried to change the launch container for the "
+ + "ActivityLaunchAnimator, which should never happen.");
+ }
+
+ @NonNull
+ @Override
+ public LaunchAnimator.State createAnimatorState() {
+ final int width = getLaunchContainer().getWidth();
+ final int height = getLaunchContainer().getHeight();
+
+ // TODO(b/207399883): Unocclude animation. This currently ends instantly.
+ return new LaunchAnimator.State(
+ 0, height, 0, width, mWindowCornerRadius, mWindowCornerRadius);
+ }
+ };
+
+ private IRemoteAnimationRunner mOccludeAnimationRunner =
+ new ActivityLaunchRemoteAnimationRunner(mOccludeAnimationController);
+ private IRemoteAnimationRunner mUnoccludeAnimationRunner =
+ new ActivityLaunchRemoteAnimationRunner(mUnoccludeAnimationController);
+
private DeviceConfigProxy mDeviceConfig;
private DozeParameters mDozeParameters;
@@ -824,6 +936,8 @@
private boolean mWallpaperSupportsAmbientMode;
private ScreenOnCoordinator mScreenOnCoordinator;
+ private Lazy<ActivityLaunchAnimator> mActivityLaunchAnimator;
+
/**
* Injected constructor. See {@link KeyguardModule}.
*/
@@ -850,7 +964,8 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy) {
+ Lazy<NotificationShadeWindowController> notificationShadeWindowControllerLazy,
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
super(context);
mFalsingCollector = falsingCollector;
mLockPatternUtils = lockPatternUtils;
@@ -890,6 +1005,12 @@
mScreenOffAnimationController = screenOffAnimationController;
mInteractionJankMonitor = interactionJankMonitor;
mDreamOverlayStateController = dreamOverlayStateController;
+
+ mActivityLaunchAnimator = activityLaunchAnimator;
+
+ mPowerButtonY = context.getResources().getDimensionPixelSize(
+ R.dimen.physical_power_button_center_screen_location_y);
+ mWindowCornerRadius = ScreenDecorationsUtils.getWindowCornerRadius(context);
}
public void userActivity() {
@@ -1440,6 +1561,14 @@
Trace.endSection();
}
+ public IRemoteAnimationRunner getOccludeAnimationRunner() {
+ return mOccludeAnimationRunner;
+ }
+
+ public IRemoteAnimationRunner getUnoccludeAnimationRunner() {
+ return mUnoccludeAnimationRunner;
+ }
+
public boolean isHiding() {
return mHiding;
}
@@ -2868,4 +2997,36 @@
return mMessage;
}
}
+
+ /**
+ * Implementation of RemoteAnimationRunner that creates a new
+ * {@link ActivityLaunchAnimator.Runner} whenever onAnimationStart is called, delegating the
+ * remote animation methods to that runner.
+ */
+ private class ActivityLaunchRemoteAnimationRunner extends IRemoteAnimationRunner.Stub {
+
+ private final ActivityLaunchAnimator.Controller mActivityLaunchController;
+ @Nullable private ActivityLaunchAnimator.Runner mRunner;
+
+ ActivityLaunchRemoteAnimationRunner(ActivityLaunchAnimator.Controller controller) {
+ mActivityLaunchController = controller;
+ }
+
+ @Override
+ public void onAnimationCancelled() throws RemoteException {
+ if (mRunner != null) {
+ mRunner.onAnimationCancelled();
+ }
+ }
+
+ @Override
+ public void onAnimationStart(int transit, RemoteAnimationTarget[] apps,
+ RemoteAnimationTarget[] wallpapers,
+ RemoteAnimationTarget[] nonApps,
+ IRemoteAnimationFinishedCallback finishedCallback)
+ throws RemoteException {
+ mRunner = mActivityLaunchAnimator.get().createRunner(mActivityLaunchController);
+ mRunner.onAnimationStart(transit, apps, wallpapers, nonApps, finishedCallback);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
index b49b49cb..195ef1a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/dagger/KeyguardModule.java
@@ -32,6 +32,7 @@
import com.android.keyguard.dagger.KeyguardStatusViewComponent;
import com.android.keyguard.dagger.KeyguardUserSwitcherComponent;
import com.android.keyguard.mediator.ScreenOnCoordinator;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.classifier.FalsingModule;
@@ -102,7 +103,8 @@
ScreenOnCoordinator screenOnCoordinator,
InteractionJankMonitor interactionJankMonitor,
DreamOverlayStateController dreamOverlayStateController,
- Lazy<NotificationShadeWindowController> notificationShadeWindowController) {
+ Lazy<NotificationShadeWindowController> notificationShadeWindowController,
+ Lazy<ActivityLaunchAnimator> activityLaunchAnimator) {
return new KeyguardViewMediator(
context,
falsingCollector,
@@ -128,8 +130,8 @@
screenOnCoordinator,
interactionJankMonitor,
dreamOverlayStateController,
- notificationShadeWindowController
- );
+ notificationShadeWindowController,
+ activityLaunchAnimator);
}
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
index b15807c..6d589aa 100644
--- a/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
+++ b/packages/SystemUI/src/com/android/systemui/log/LogBuffer.kt
@@ -176,14 +176,9 @@
buffer.removeFirst()
}
buffer.add(message as LogMessageImpl)
- if (systrace) {
- val messageStr = message.printer(message)
- Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events", "$name - $messageStr")
- }
- if (logcatEchoTracker.isBufferLoggable(name, message.level) ||
- logcatEchoTracker.isTagLoggable(message.tag, message.level)) {
- echo(message)
- }
+ val includeInLogcat = logcatEchoTracker.isBufferLoggable(name, message.level) ||
+ logcatEchoTracker.isTagLoggable(message.tag, message.level)
+ echo(message, toLogcat = includeInLogcat, toSystrace = systrace)
}
/** Converts the entire buffer to a newline-delimited string */
@@ -232,8 +227,24 @@
pw.println(message.printer(message))
}
- private fun echo(message: LogMessage) {
- val strMessage = message.printer(message)
+ private fun echo(message: LogMessage, toLogcat: Boolean, toSystrace: Boolean) {
+ if (toLogcat || toSystrace) {
+ val strMessage = message.printer(message)
+ if (toSystrace) {
+ echoToSystrace(message, strMessage)
+ }
+ if (toLogcat) {
+ echoToLogcat(message, strMessage)
+ }
+ }
+ }
+
+ private fun echoToSystrace(message: LogMessage, strMessage: String) {
+ Trace.instantForTrack(Trace.TRACE_TAG_APP, "UI Events",
+ "$name - ${message.level.shortString} ${message.tag}: $strMessage")
+ }
+
+ private fun echoToLogcat(message: LogMessage, strMessage: String) {
when (message.level) {
LogLevel.VERBOSE -> Log.v(message.tag, strMessage)
LogLevel.DEBUG -> Log.d(message.tag, strMessage)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
index c404f7a..f893f36 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaCarouselController.kt
@@ -411,7 +411,6 @@
// Returns true if new player is added
private fun addOrUpdatePlayer(key: String, oldKey: String?, data: MediaData): Boolean {
- val dataCopy = data.copy(backgroundColor = bgColor)
MediaPlayerData.moveIfExists(oldKey, key)
val existingPlayer = MediaPlayerData.getMediaPlayer(key)
val curVisibleMediaKey = MediaPlayerData.playerKeys()
@@ -431,14 +430,14 @@
val lp = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT)
newPlayer.mediaViewHolder?.player?.setLayoutParams(lp)
- newPlayer.bindPlayer(dataCopy, key)
+ newPlayer.bindPlayer(data, key)
newPlayer.setListening(currentlyExpanded)
- MediaPlayerData.addMediaPlayer(key, dataCopy, newPlayer, systemClock)
+ MediaPlayerData.addMediaPlayer(key, data, newPlayer, systemClock)
updatePlayerToState(newPlayer, noAnimation = true)
reorderAllPlayers(curVisibleMediaKey)
} else {
- existingPlayer.bindPlayer(dataCopy, key)
- MediaPlayerData.addMediaPlayer(key, dataCopy, existingPlayer, systemClock)
+ existingPlayer.bindPlayer(data, key)
+ MediaPlayerData.addMediaPlayer(key, data, existingPlayer, systemClock)
if (visualStabilityManager.isReorderingAllowed || shouldScrollToActivePlayer) {
reorderAllPlayers(curVisibleMediaKey)
} else {
@@ -543,7 +542,11 @@
}
private fun getForegroundColor(): Int {
- return context.getColor(android.R.color.system_accent2_900)
+ return if (mediaFlags.useMediaSessionLayout()) {
+ context.getColor(android.R.color.system_neutral2_200)
+ } else {
+ context.getColor(android.R.color.system_accent2_900)
+ }
}
private fun updatePageIndicator() {
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
index 69a7ec3..b3e6682 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java
@@ -19,6 +19,7 @@
import static android.provider.Settings.ACTION_MEDIA_CONTROLS_SETTINGS;
import android.app.PendingIntent;
+import android.app.WallpaperColors;
import android.app.smartspace.SmartspaceAction;
import android.content.Context;
import android.content.Intent;
@@ -40,6 +41,7 @@
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
+import android.widget.SeekBar;
import android.widget.TextView;
import androidx.annotation.NonNull;
@@ -54,6 +56,7 @@
import com.android.systemui.animation.GhostedViewLaunchAnimatorController;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
+import com.android.systemui.monet.ColorScheme;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.shared.system.SysUiStatsLog;
@@ -405,7 +408,7 @@
seamlessView.setContentDescription(deviceString);
// Dismiss
- mMediaViewHolder.getDismissLabel().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
+ mMediaViewHolder.getDismissText().setAlpha(isDismissible ? 1 : DISABLED_ALPHA);
mMediaViewHolder.getDismiss().setEnabled(isDismissible);
mMediaViewHolder.getDismiss().setOnClickListener(v -> {
if (mFalsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) return;
@@ -438,11 +441,10 @@
ConstraintSet collapsedSet = mMediaViewController.getCollapsedLayout();
// Album art
- PlayerViewHolder playerHolder = (PlayerViewHolder) mMediaViewHolder;
- ImageView albumView = playerHolder.getAlbumView();
+ ImageView albumView = mMediaViewHolder.getAlbumView();
boolean hasArtwork = data.getArtwork() != null;
if (hasArtwork) {
- Drawable artwork = scaleDrawable(data.getArtwork());
+ Drawable artwork = getScaledThumbnail(data.getArtwork());
albumView.setPadding(0, 0, 0, 0);
albumView.setImageDrawable(artwork);
} else {
@@ -548,6 +550,19 @@
/** Bind elements specific to PlayerSessionViewHolder */
private void bindSessionPlayer(@NonNull MediaData data, String key) {
+ // Default colors
+ int surfaceColor = mBackgroundColor;
+ int accentPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+ int textPrimary = com.android.settingslib.Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.textColorPrimary).getDefaultColor();
+ int textPrimaryInverse = com.android.settingslib.Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.textColorPrimaryInverse).getDefaultColor();
+ int textSecondary = com.android.settingslib.Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.textColorSecondary).getDefaultColor();
+ int textTertiary = com.android.settingslib.Utils.getColorAttr(mContext,
+ com.android.internal.R.attr.textColorTertiary).getDefaultColor();
+
// App icon - use launcher icon
ImageView appIconView = mMediaViewHolder.getAppIcon();
appIconView.clearColorFilter();
@@ -567,26 +582,106 @@
appIconView.setColorFilter(color);
}
+ // Album art
+ ColorScheme colorScheme = null;
+ ImageView albumView = mMediaViewHolder.getAlbumView();
+ boolean hasArtwork = data.getArtwork() != null;
+ if (hasArtwork) {
+ colorScheme = new ColorScheme(WallpaperColors.fromBitmap(data.getArtwork().getBitmap()),
+ true);
+
+ // Scale artwork to fit background
+ int width = mMediaViewHolder.getPlayer().getWidth();
+ int height = mMediaViewHolder.getPlayer().getHeight();
+ Drawable artwork = getScaledBackground(data.getArtwork(), width, height);
+ albumView.setPadding(0, 0, 0, 0);
+ albumView.setImageDrawable(artwork);
+ albumView.setClipToOutline(true);
+ } else {
+ // If there's no artwork, use colors from the app icon
+ try {
+ Drawable icon = mContext.getPackageManager().getApplicationIcon(
+ data.getPackageName());
+ colorScheme = new ColorScheme(WallpaperColors.fromDrawable(icon), true);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(TAG, "Cannot find icon for package " + data.getPackageName(), e);
+ }
+ }
+
+ // Get colors for player
+ if (colorScheme != null) {
+ surfaceColor = colorScheme.getAccent2().get(9); // A2-800
+ accentPrimary = colorScheme.getAccent1().get(2); // A1-100
+ textPrimary = colorScheme.getNeutral1().get(1); // N1-50
+ textPrimaryInverse = colorScheme.getNeutral1().get(10); // N1-900
+ textSecondary = colorScheme.getNeutral2().get(3); // N2-200
+ textTertiary = colorScheme.getNeutral2().get(5); // N2-400
+ }
+
+ ColorStateList bgColorList = ColorStateList.valueOf(surfaceColor);
+ ColorStateList accentColorList = ColorStateList.valueOf(accentPrimary);
+ ColorStateList textColorList = ColorStateList.valueOf(textPrimary);
+
+ // Gradient and background (visible when there is no art)
+ albumView.setForegroundTintList(ColorStateList.valueOf(surfaceColor));
+ albumView.setBackgroundTintList(
+ ColorStateList.valueOf(surfaceColor));
+ mMediaViewHolder.getPlayer().setBackgroundTintList(bgColorList);
+
+ // Metadata text
+ mMediaViewHolder.getTitleText().setTextColor(textPrimary);
+ mMediaViewHolder.getArtistText().setTextColor(textSecondary);
+
+ // Seekbar
+ SeekBar seekbar = mMediaViewHolder.getSeekBar();
+ seekbar.getThumb().setTintList(textColorList);
+ seekbar.setProgressTintList(textColorList);
+ seekbar.setProgressBackgroundTintList(ColorStateList.valueOf(textTertiary));
+
+ // Output switcher
+ View seamlessView = mMediaViewHolder.getSeamlessButton();
+ seamlessView.setBackgroundTintList(accentColorList);
+ ImageView seamlessIconView = mMediaViewHolder.getSeamlessIcon();
+ seamlessIconView.setImageTintList(bgColorList);
+ TextView seamlessText = mMediaViewHolder.getSeamlessText();
+ seamlessText.setTextColor(surfaceColor);
+
// Media action buttons
MediaButton semanticActions = data.getSemanticActions();
if (semanticActions != null) {
PlayerSessionViewHolder sessionHolder = (PlayerSessionViewHolder) mMediaViewHolder;
- setSemanticButton(sessionHolder.getActionPlayPause(),
- semanticActions.getPlayOrPause());
- setSemanticButton(sessionHolder.getActionNext(),
- semanticActions.getNextOrCustom());
- setSemanticButton(sessionHolder.getActionPrev(),
- semanticActions.getPrevOrCustom());
- setSemanticButton(sessionHolder.getActionStart(),
- semanticActions.getStartCustom());
- setSemanticButton(sessionHolder.getActionEnd(),
- semanticActions.getEndCustom());
+
+ // Play/pause button has a background
+ sessionHolder.getActionPlayPause().setBackgroundTintList(accentColorList);
+ setSemanticButton(sessionHolder.getActionPlayPause(), semanticActions.getPlayOrPause(),
+ ColorStateList.valueOf(textPrimaryInverse));
+
+ setSemanticButton(sessionHolder.getActionNext(), semanticActions.getNextOrCustom(),
+ textColorList);
+ setSemanticButton(sessionHolder.getActionPrev(), semanticActions.getPrevOrCustom(),
+ textColorList);
+ setSemanticButton(sessionHolder.getActionStart(), semanticActions.getStartCustom(),
+ textColorList);
+ setSemanticButton(sessionHolder.getActionEnd(), semanticActions.getEndCustom(),
+ textColorList);
} else {
Log.w(TAG, "Using semantic player, but did not get buttons");
}
+
+ // Long press buttons
+ mMediaViewHolder.getLongPressText().setTextColor(textColorList);
+ mMediaViewHolder.getSettingsText().setTextColor(textColorList);
+ mMediaViewHolder.getSettingsText().setBackgroundTintList(accentColorList);
+ mMediaViewHolder.getCancelText().setTextColor(textColorList);
+ mMediaViewHolder.getCancelText().setBackgroundTintList(accentColorList);
+ mMediaViewHolder.getDismissText().setTextColor(textColorList);
+ mMediaViewHolder.getDismissText().setBackgroundTintList(accentColorList);
+
}
- private void setSemanticButton(final ImageButton button, MediaAction mediaAction) {
+ private void setSemanticButton(final ImageButton button, MediaAction mediaAction,
+ ColorStateList fgColor) {
+ button.setImageTintList(fgColor);
if (mediaAction != null) {
button.setImageIcon(mediaAction.getIcon());
button.setContentDescription(mediaAction.getContentDescription());
@@ -844,8 +939,11 @@
mMediaViewController.openGuts();
}
+ /**
+ * Scale drawable to fit into the square album art thumbnail
+ */
@UiThread
- private Drawable scaleDrawable(Icon icon) {
+ private Drawable getScaledThumbnail(Icon icon) {
if (icon == null) {
return null;
}
@@ -870,6 +968,25 @@
}
/**
+ * Scale artwork to fill the background of the panel
+ */
+ @UiThread
+ private Drawable getScaledBackground(Icon icon, int width, int height) {
+ if (icon == null) {
+ return null;
+ }
+ Drawable drawable = icon.loadDrawable(mContext);
+ Rect bounds = new Rect(0, 0, width, height);
+ if (bounds.width() > width || bounds.height() > height) {
+ float offsetX = (bounds.width() - width) / 2.0f;
+ float offsetY = (bounds.height() - height) / 2.0f;
+ bounds.offset((int) -offsetX, (int) -offsetY);
+ }
+ drawable.setBounds(bounds);
+ return drawable;
+ }
+
+ /**
* Get the current media controller
*
* @return the controller
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
index d926e7d..0223c60 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaDataManager.kt
@@ -30,9 +30,7 @@
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.graphics.Bitmap
-import android.graphics.Canvas
import android.graphics.ImageDecoder
-import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
import android.media.MediaDescription
import android.media.MediaMetadata
@@ -562,7 +560,7 @@
val mediaController = mediaControllerFactory.create(token)
val metadata = mediaController.metadata
- // Foreground and Background colors computed from album art
+ // Album art
val notif: Notification = sbn.notification
var artworkBitmap = metadata?.getBitmap(MediaMetadata.METADATA_KEY_ART)
if (artworkBitmap == null) {
@@ -576,24 +574,6 @@
} else {
Icon.createWithBitmap(artworkBitmap)
}
- if (artWorkIcon != null) {
- // If we have art, get colors from that
- if (artworkBitmap == null) {
- if (artWorkIcon.type == Icon.TYPE_BITMAP ||
- artWorkIcon.type == Icon.TYPE_ADAPTIVE_BITMAP) {
- artworkBitmap = artWorkIcon.bitmap
- } else {
- val drawable: Drawable = artWorkIcon.loadDrawable(context)
- artworkBitmap = Bitmap.createBitmap(
- drawable.intrinsicWidth,
- drawable.intrinsicHeight,
- Bitmap.Config.ARGB_8888)
- val canvas = Canvas(artworkBitmap)
- drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
- drawable.draw(canvas)
- }
- }
- }
// App name
val builder = Notification.Builder.recoverBuilder(context, notif)
@@ -787,30 +767,28 @@
return when (action) {
PlaybackState.ACTION_PLAY -> {
MediaAction(
- Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_play),
+ Icon.createWithResource(context, R.drawable.ic_media_play),
{ controller.transportControls.play() },
context.getString(R.string.controls_media_button_play)
)
}
PlaybackState.ACTION_PAUSE -> {
MediaAction(
- Icon.createWithResource(context,
- com.android.internal.R.drawable.ic_media_pause),
+ Icon.createWithResource(context, R.drawable.ic_media_pause),
{ controller.transportControls.pause() },
context.getString(R.string.controls_media_button_pause)
)
}
PlaybackState.ACTION_SKIP_TO_PREVIOUS -> {
MediaAction(
- Icon.createWithResource(context,
- com.android.internal.R.drawable.ic_media_previous),
+ Icon.createWithResource(context, R.drawable.ic_media_prev),
{ controller.transportControls.skipToPrevious() },
context.getString(R.string.controls_media_button_prev)
)
}
PlaybackState.ACTION_SKIP_TO_NEXT -> {
MediaAction(
- Icon.createWithResource(context, com.android.internal.R.drawable.ic_media_next),
+ Icon.createWithResource(context, R.drawable.ic_media_next),
{ controller.transportControls.skipToNext() },
context.getString(R.string.controls_media_button_next)
)
@@ -900,7 +878,7 @@
private fun getResumeMediaAction(action: Runnable): MediaAction {
return MediaAction(
- Icon.createWithResource(context, R.drawable.lb_ic_play).setTint(themeText),
+ Icon.createWithResource(context, R.drawable.ic_media_play).setTint(themeText),
action,
context.getString(R.string.controls_media_resume)
)
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
index c333b50..e57b247 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewHolder.kt
@@ -35,6 +35,7 @@
val player = itemView as TransitionLayout
// Player information
+ val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
val appIcon = itemView.requireViewById<ImageView>(R.id.icon)
val titleText = itemView.requireViewById<TextView>(R.id.header_title)
val artistText = itemView.requireViewById<TextView>(R.id.header_artist)
@@ -53,8 +54,9 @@
// Settings screen
val longPressText = itemView.requireViewById<TextView>(R.id.remove_text)
val cancel = itemView.requireViewById<View>(R.id.cancel)
+ val cancelText = itemView.requireViewById<TextView>(R.id.cancel_text)
val dismiss = itemView.requireViewById<ViewGroup>(R.id.dismiss)
- val dismissLabel = dismiss.getChildAt(0)
+ val dismissText = itemView.requireViewById<TextView>(R.id.dismiss_text)
val settings = itemView.requireViewById<View>(R.id.settings)
val settingsText = itemView.requireViewById<TextView>(R.id.settings_text)
diff --git a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
index a1faa40..20b2d4a 100644
--- a/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/PlayerViewHolder.kt
@@ -20,7 +20,6 @@
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
-import android.widget.ImageView
import android.widget.TextView
import com.android.systemui.R
@@ -29,9 +28,6 @@
*/
class PlayerViewHolder private constructor(itemView: View) : MediaViewHolder(itemView) {
- // Player information
- val albumView = itemView.requireViewById<ImageView>(R.id.album_art)
-
// Seek bar
val progressTimes = itemView.requireViewById<ViewGroup>(R.id.notification_media_progress_time)
override val elapsedTimeView = itemView.requireViewById<TextView>(R.id.media_elapsed_time)
diff --git a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
index 29938a05..663877c 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -21,6 +21,7 @@
import android.view.WindowManager;
import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.media.MediaDataManager;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
@@ -31,10 +32,11 @@
import com.android.systemui.media.taptotransfer.MediaTttFlags;
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver;
import com.android.systemui.media.taptotransfer.sender.MediaTttChipControllerSender;
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService;
+import com.android.systemui.statusbar.CommandQueue;
import com.android.systemui.statusbar.commandline.CommandRegistry;
import java.util.Optional;
+import java.util.concurrent.Executor;
import javax.inject.Named;
@@ -99,12 +101,13 @@
@SysUISingleton
static Optional<MediaTttChipControllerSender> providesMediaTttChipControllerSender(
MediaTttFlags mediaTttFlags,
+ CommandQueue commandQueue,
Context context,
WindowManager windowManager) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipControllerSender(context, windowManager));
+ return Optional.of(new MediaTttChipControllerSender(commandQueue, context, windowManager));
}
/** */
@@ -127,6 +130,7 @@
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
Context context,
+ @Main Executor mainExecutor,
MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
@@ -135,15 +139,10 @@
new MediaTttCommandLineHelper(
commandRegistry,
context,
+ mainExecutor,
mediaTttChipControllerReceiver));
}
- /** Inject into MediaTttSenderService. */
- @Binds
- @IntoMap
- @ClassKey(MediaTttSenderService.class)
- Service bindMediaTttSenderService(MediaTttSenderService service);
-
/** Inject into NearbyMediaDevicesService. */
@Binds
@IntoMap
diff --git a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
index 2c35db3..7c04810 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dream/MediaDreamComplication.java
@@ -37,6 +37,11 @@
}
@Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_CAST_INFO;
+ }
+
+ @Override
public ViewHolder createView(ComplicationViewModel model) {
return mComponentFactory.create().getViewHolder();
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
index 3720851..1ea21ce 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelper.kt
@@ -16,34 +16,29 @@
package com.android.systemui.media.taptotransfer
-import android.content.ComponentName
+import android.app.StatusBarManager
import android.content.Context
-import android.content.Intent
-import android.content.ServiceConnection
import android.graphics.Color
import android.graphics.drawable.Icon
import android.media.MediaRoute2Info
-import android.os.IBinder
import android.util.Log
import androidx.annotation.VisibleForTesting
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToEndCast
-import com.android.systemui.media.taptotransfer.sender.MoveCloserToStartCast
+import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
+import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
import com.android.systemui.media.taptotransfer.sender.TransferFailed
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
import java.io.PrintWriter
+import java.util.concurrent.Executor
import javax.inject.Inject
/**
@@ -54,16 +49,36 @@
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
private val context: Context,
+ @Main private val mainExecutor: Executor,
private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
) {
- private var senderService: IDeviceSenderService? = null
- private val senderServiceConnection = SenderServiceConnection()
-
private val appIconDrawable =
Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
it.setTint(Color.YELLOW)
}
+ /**
+ * A map from a display state string typed in the command line to the display int it represents.
+ */
+ private val stateStringToStateInt: Map<String, Int> = mapOf(
+ AlmostCloseToStartCast::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ AlmostCloseToEndCast::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ TransferToReceiverTriggered::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ TransferToThisDeviceTriggered::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ TransferToReceiverSucceeded::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ TransferToThisDeviceSucceeded::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ TransferFailed::class.simpleName!!
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ FAR_FROM_RECEIVER_STATE
+ to StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER
+ )
+
init {
commandRegistry.registerCommand(SENDER_COMMAND) { SenderCommand() }
commandRegistry.registerCommand(
@@ -75,117 +90,63 @@
/** All commands for the sender device. */
inner class SenderCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- val otherDeviceName = args[0]
- val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
- .addFeature("feature")
- .build()
- val otherDeviceInfo = DeviceInfo(otherDeviceName)
+ val routeInfo = MediaRoute2Info.Builder("id", args[0])
+ .addFeature("feature")
+ .build()
- when (args[1]) {
- MOVE_CLOSER_TO_START_CAST_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
- }
- }
- MOVE_CLOSER_TO_END_CAST_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
- }
- }
- TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
- }
- }
- TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
- }
- }
- TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME -> {
- val undoCallback = object : IUndoTransferCallback.Stub() {
- override fun onUndoTriggered() {
- Log.i(TAG, "Undo transfer to receiver callback triggered")
- // The external services that implement this callback would kick off a
- // transfer back to this device, so mimic that here.
- runOnService { senderService ->
- senderService
- .transferToThisDeviceTriggered(mediaInfo, otherDeviceInfo)
- }
- }
- }
- runOnService { senderService ->
- senderService
- .transferToReceiverSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
- }
- }
- TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME -> {
- val undoCallback = object : IUndoTransferCallback.Stub() {
- override fun onUndoTriggered() {
- Log.i(TAG, "Undo transfer to this device callback triggered")
- // The external services that implement this callback would kick off a
- // transfer back to the receiver, so mimic that here.
- runOnService { senderService ->
- senderService
- .transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
- }
- }
- }
- runOnService { senderService ->
- senderService
- .transferToThisDeviceSucceeded(mediaInfo, otherDeviceInfo, undoCallback)
- }
- }
- TRANSFER_FAILED_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.transferFailed(mediaInfo, otherDeviceInfo)
- }
- }
- NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME -> {
- runOnService { senderService ->
- senderService.noLongerCloseToReceiver(mediaInfo, otherDeviceInfo)
- context.unbindService(senderServiceConnection)
- }
- }
- else -> {
- pw.println("Sender command must be one of " +
- "$MOVE_CLOSER_TO_START_CAST_COMMAND_NAME, " +
- "$MOVE_CLOSER_TO_END_CAST_COMMAND_NAME, " +
- "$TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME, " +
- "$TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME, " +
- "$TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME, " +
- "$TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME, " +
- "$TRANSFER_FAILED_COMMAND_NAME, " +
- NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
- )
- }
+ @StatusBarManager.MediaTransferSenderState
+ val displayState = stateStringToStateInt[args[1]]
+ if (displayState == null) {
+ pw.println("Invalid command name")
+ return
}
+
+ val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
+ as StatusBarManager
+ statusBarManager.updateMediaTapToTransferSenderDisplay(
+ displayState,
+ routeInfo,
+ getUndoExecutor(displayState),
+ getUndoCallback(displayState)
+ )
+ }
+
+ private fun getUndoExecutor(
+ @StatusBarManager.MediaTransferSenderState displayState: Int
+ ): Executor? {
+ return if (isSucceededState(displayState)) {
+ mainExecutor
+ } else {
+ null
+ }
+ }
+
+ private fun getUndoCallback(
+ @StatusBarManager.MediaTransferSenderState displayState: Int
+ ): Runnable? {
+ return if (isSucceededState(displayState)) {
+ Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") }
+ } else {
+ null
+ }
+ }
+
+ private fun isSucceededState(
+ @StatusBarManager.MediaTransferSenderState displayState: Int
+ ): Boolean {
+ return displayState ==
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ||
+ displayState ==
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED
}
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipStatus>")
- }
-
- private fun runOnService(command: SenderServiceCommand) {
- val currentService = senderService
- if (currentService != null) {
- command.run(currentService)
- } else {
- bindService(command)
- }
- }
-
- private fun bindService(command: SenderServiceCommand) {
- senderServiceConnection.pendingCommand = command
- val binding = context.bindService(
- Intent(context, MediaTttSenderService::class.java),
- senderServiceConnection,
- Context.BIND_AUTO_CREATE
- )
- Log.i(TAG, "Starting service binding? $binding")
+ pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>")
}
}
+ // TODO(b/216318437): Migrate the receiver callbacks to StatusBarManager.
+
/** A command to DISPLAY the media ttt chip on the RECEIVER device. */
inner class AddChipCommandReceiver : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
@@ -207,57 +168,20 @@
pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG")
}
}
-
- /** A service connection for [IDeviceSenderService]. */
- private inner class SenderServiceConnection : ServiceConnection {
- // A command that should be run when the service gets connected.
- var pendingCommand: SenderServiceCommand? = null
-
- override fun onServiceConnected(className: ComponentName, service: IBinder) {
- val newCallback = IDeviceSenderService.Stub.asInterface(service)
- senderService = newCallback
- pendingCommand?.run(newCallback)
- pendingCommand = null
- }
-
- override fun onServiceDisconnected(className: ComponentName) {
- senderService = null
- }
- }
-
- /** An interface defining a command that should be run on the sender service. */
- private fun interface SenderServiceCommand {
- /** Runs the command on the provided [senderService]. */
- fun run(senderService: IDeviceSenderService)
- }
}
@VisibleForTesting
const val SENDER_COMMAND = "media-ttt-chip-sender"
@VisibleForTesting
-const val REMOVE_CHIP_COMMAND_SENDER_TAG = "media-ttt-chip-remove-sender"
-@VisibleForTesting
const val ADD_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-add-receiver"
@VisibleForTesting
const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
@VisibleForTesting
-val MOVE_CLOSER_TO_START_CAST_COMMAND_NAME = MoveCloserToStartCast::class.simpleName!!
-@VisibleForTesting
-val MOVE_CLOSER_TO_END_CAST_COMMAND_NAME = MoveCloserToEndCast::class.simpleName!!
-@VisibleForTesting
-val TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME = TransferToReceiverTriggered::class.simpleName!!
-@VisibleForTesting
-val TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME =
- TransferToThisDeviceTriggered::class.simpleName!!
-@VisibleForTesting
-val TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME = TransferToReceiverSucceeded::class.simpleName!!
-@VisibleForTesting
-val TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME =
- TransferToThisDeviceSucceeded::class.simpleName!!
-@VisibleForTesting
-val TRANSFER_FAILED_COMMAND_NAME = TransferFailed::class.simpleName!!
-@VisibleForTesting
-val NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME = "NoLongerCloseToReceiver"
+val FAR_FROM_RECEIVER_STATE = "FarFromReceiver"
private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
-private const val TAG = "MediaTapToTransferCli"
+private const val CLI_TAG = "MediaTransferCli"
+
+private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+ .addFeature("feature")
+ .build()
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
index c656df2..05baf78 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/ChipStateSender.kt
@@ -19,9 +19,9 @@
import android.content.Context
import android.graphics.drawable.Drawable
import android.view.View
+import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.media.taptotransfer.common.MediaTttChipState
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
/**
* A class that stores all the information necessary to display the media tap-to-transfer chip on
@@ -59,7 +59,7 @@
*
* @property otherDeviceName the name of the other device involved in the transfer.
*/
-class MoveCloserToStartCast(
+class AlmostCloseToStartCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
private val otherDeviceName: String,
@@ -76,7 +76,7 @@
*
* @property otherDeviceName the name of the other device involved in the transfer.
*/
-class MoveCloserToEndCast(
+class AlmostCloseToEndCast(
appIconDrawable: Drawable,
appIconContentDescription: String,
private val otherDeviceName: String,
@@ -130,7 +130,7 @@
appIconDrawable: Drawable,
appIconContentDescription: String,
private val otherDeviceName: String,
- val undoCallback: IUndoTransferCallback? = null
+ val undoCallback: IUndoMediaTransferCallback? = null
) : ChipStateSender(appIconDrawable, appIconContentDescription) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_different_device, otherDeviceName)
@@ -169,7 +169,7 @@
appIconDrawable: Drawable,
appIconContentDescription: String,
private val otherDeviceName: String,
- val undoCallback: IUndoTransferCallback? = null
+ val undoCallback: IUndoMediaTransferCallback? = null
) : ChipStateSender(appIconDrawable, appIconContentDescription) {
override fun getChipTextString(context: Context): String {
return context.getString(R.string.media_transfer_playing_this_device)
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
index 453e3d6..d1790d2 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSender.kt
@@ -16,14 +16,21 @@
package com.android.systemui.media.taptotransfer.sender
+import android.app.StatusBarManager
import android.content.Context
+import android.graphics.Color
+import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
+import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.TextView
+import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.media.taptotransfer.common.MediaTttChipControllerCommon
+import com.android.systemui.statusbar.CommandQueue
import javax.inject.Inject
/**
@@ -32,11 +39,93 @@
*/
@SysUISingleton
class MediaTttChipControllerSender @Inject constructor(
+ commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
) : MediaTttChipControllerCommon<ChipStateSender>(
context, windowManager, R.layout.media_ttt_chip
) {
+ // TODO(b/216141276): Use app icon from media route info instead of this fake one.
+ private val fakeAppIconDrawable =
+ Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
+ it.setTint(Color.YELLOW)
+ }
+
+ private val commandQueueCallbacks = object : CommandQueue.Callbacks {
+ override fun updateMediaTapToTransferSenderDisplay(
+ @StatusBarManager.MediaTransferSenderState displayState: Int,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback?
+ ) {
+ this@MediaTttChipControllerSender.updateMediaTapToTransferSenderDisplay(
+ displayState, routeInfo, undoCallback
+ )
+ }
+ }
+
+ init {
+ commandQueue.addCallback(commandQueueCallbacks)
+ }
+
+ private fun updateMediaTapToTransferSenderDisplay(
+ @StatusBarManager.MediaTransferSenderState displayState: Int,
+ routeInfo: MediaRoute2Info,
+ undoCallback: IUndoMediaTransferCallback?
+ ) {
+ // TODO(b/217418566): This app icon content description is incorrect --
+ // routeInfo.name is the name of the device, not the name of the app.
+ val appIconContentDescription = routeInfo.name.toString()
+ val otherDeviceName = routeInfo.name.toString()
+ val chipState = when(displayState) {
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST ->
+ AlmostCloseToStartCast(
+ fakeAppIconDrawable, appIconContentDescription, otherDeviceName
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST ->
+ AlmostCloseToEndCast(
+ fakeAppIconDrawable, appIconContentDescription, otherDeviceName
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED ->
+ TransferToReceiverTriggered(
+ fakeAppIconDrawable, appIconContentDescription, otherDeviceName
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED ->
+ TransferToThisDeviceTriggered(
+ fakeAppIconDrawable, appIconContentDescription
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED ->
+ TransferToReceiverSucceeded(
+ fakeAppIconDrawable,
+ appIconContentDescription,
+ otherDeviceName,
+ undoCallback
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED ->
+ TransferToThisDeviceSucceeded(
+ fakeAppIconDrawable,
+ appIconContentDescription,
+ otherDeviceName,
+ undoCallback
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED ->
+ TransferFailed(
+ fakeAppIconDrawable, appIconContentDescription
+ )
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER -> {
+ removeChip()
+ null
+ }
+ else -> {
+ Log.e(SENDER_TAG, "Unhandled MediaTransferSenderState $displayState")
+ null
+ }
+ }
+
+ chipState?.let {
+ displayChip(it)
+ }
+ }
/** Displays the chip view for the given state. */
override fun updateChipView(chipState: ChipStateSender, currentChipView: ViewGroup) {
@@ -64,3 +153,5 @@
if (showFailure) { View.VISIBLE } else { View.GONE }
}
}
+
+const val SENDER_TAG = "MediaTapToTransferSender"
diff --git a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
deleted file mode 100644
index 717752e..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderService.kt
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.systemui.media.taptotransfer.sender
-
-import android.app.Service
-import android.content.Context
-import android.content.Intent
-import android.graphics.Color
-import android.graphics.drawable.Icon
-import android.media.MediaRoute2Info
-import android.os.IBinder
-import com.android.systemui.R
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import javax.inject.Inject
-
-/**
- * Service that allows external handlers to trigger the media chip on the sender device.
- */
-class MediaTttSenderService @Inject constructor(
- context: Context,
- val controller: MediaTttChipControllerSender
-) : Service() {
-
- // TODO(b/203800643): Add logging when callbacks trigger.
- private val binder: IBinder = object : IDeviceSenderService.Stub() {
- override fun closeToReceiverToStartCast(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.closeToReceiverToStartCast(mediaInfo, otherDeviceInfo)
- }
-
- override fun closeToReceiverToEndCast(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.closeToReceiverToEndCast(mediaInfo, otherDeviceInfo)
- }
-
- override fun transferFailed(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.transferFailed(mediaInfo)
- }
-
- override fun transferToReceiverTriggered(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.transferToReceiverTriggered(mediaInfo, otherDeviceInfo)
- }
-
- override fun transferToThisDeviceTriggered(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.transferToThisDeviceTriggered(mediaInfo)
- }
-
- override fun transferToReceiverSucceeded(
- mediaInfo: MediaRoute2Info,
- otherDeviceInfo: DeviceInfo,
- undoCallback: IUndoTransferCallback
- ) {
- this@MediaTttSenderService.transferToReceiverSucceeded(
- mediaInfo, otherDeviceInfo, undoCallback
- )
- }
-
- override fun transferToThisDeviceSucceeded(
- mediaInfo: MediaRoute2Info,
- otherDeviceInfo: DeviceInfo,
- undoCallback: IUndoTransferCallback
- ) {
- this@MediaTttSenderService.transferToThisDeviceSucceeded(
- mediaInfo, otherDeviceInfo, undoCallback
- )
- }
-
- override fun noLongerCloseToReceiver(
- mediaInfo: MediaRoute2Info,
- otherDeviceInfo: DeviceInfo
- ) {
- this@MediaTttSenderService.noLongerCloseToReceiver()
- }
- }
-
- // TODO(b/203800643): Use the app icon from the media info instead of a fake one.
- private val fakeAppIconDrawable =
- Icon.createWithResource(context, R.drawable.ic_avatar_user).loadDrawable(context).also {
- it.setTint(Color.YELLOW)
- }
-
- override fun onBind(intent: Intent?): IBinder = binder
-
- private fun closeToReceiverToStartCast(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- val chipState = MoveCloserToStartCast(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString(),
- otherDeviceName = otherDeviceInfo.name
- )
- controller.displayChip(chipState)
- }
-
- private fun closeToReceiverToEndCast(mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo) {
- val chipState = MoveCloserToEndCast(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString(),
- otherDeviceName = otherDeviceInfo.name
- )
- controller.displayChip(chipState)
- }
-
- private fun transferFailed(mediaInfo: MediaRoute2Info) {
- val chipState = TransferFailed(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString()
- )
- controller.displayChip(chipState)
- }
-
- private fun transferToReceiverTriggered(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo
- ) {
- val chipState = TransferToReceiverTriggered(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString(),
- otherDeviceName = otherDeviceInfo.name
- )
- controller.displayChip(chipState)
- }
-
- private fun transferToThisDeviceTriggered(mediaInfo: MediaRoute2Info) {
- val chipState = TransferToThisDeviceTriggered(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString()
- )
- controller.displayChip(chipState)
- }
-
- private fun transferToReceiverSucceeded(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
- ) {
- val chipState = TransferToReceiverSucceeded(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString(),
- otherDeviceName = otherDeviceInfo.name,
- undoCallback = undoCallback
- )
- controller.displayChip(chipState)
- }
-
- private fun transferToThisDeviceSucceeded(
- mediaInfo: MediaRoute2Info, otherDeviceInfo: DeviceInfo, undoCallback: IUndoTransferCallback
- ) {
- val chipState = TransferToThisDeviceSucceeded(
- appIconDrawable = fakeAppIconDrawable,
- appIconContentDescription = mediaInfo.name.toString(),
- otherDeviceName = otherDeviceInfo.name,
- undoCallback = undoCallback
- )
- controller.displayChip(chipState)
- }
-
- private fun noLongerCloseToReceiver() {
- controller.removeChip()
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
new file mode 100644
index 0000000..eb341563
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt
@@ -0,0 +1,430 @@
+/*
+ * 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.systemui.qs
+
+import android.app.IActivityManager
+import android.app.IForegroundServiceObserver
+import android.content.Context
+import android.content.pm.PackageManager
+import android.graphics.drawable.Drawable
+import android.os.IBinder
+import android.os.PowerExemptionManager
+import android.os.RemoteException
+import android.provider.DeviceConfig.NAMESPACE_SYSTEMUI
+import android.text.format.DateUtils
+import android.util.ArrayMap
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.Button
+import android.widget.ImageView
+import android.widget.TextView
+import androidx.annotation.GuardedBy
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED
+import com.android.systemui.R
+import com.android.systemui.animation.DialogLaunchAnimator
+import com.android.systemui.dagger.qualifiers.Background
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.statusbar.phone.SystemUIDialog
+import com.android.systemui.util.DeviceConfigProxy
+import com.android.systemui.util.time.SystemClock
+import java.util.Objects
+import java.util.concurrent.Executor
+import javax.inject.Inject
+import kotlin.math.max
+
+class FgsManagerController @Inject constructor(
+ private val context: Context,
+ @Main private val mainExecutor: Executor,
+ @Background private val backgroundExecutor: Executor,
+ private val systemClock: SystemClock,
+ private val activityManager: IActivityManager,
+ private val packageManager: PackageManager,
+ private val deviceConfigProxy: DeviceConfigProxy,
+ private val dialogLaunchAnimator: DialogLaunchAnimator
+) : IForegroundServiceObserver.Stub() {
+
+ companion object {
+ private val LOG_TAG = FgsManagerController::class.java.simpleName
+ }
+
+ private var isAvailable = false
+
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ var initialized = false
+
+ @GuardedBy("lock")
+ private val runningServiceTokens = mutableMapOf<UserPackage, StartTimeAndTokens>()
+
+ @GuardedBy("lock")
+ private var dialog: SystemUIDialog? = null
+
+ @GuardedBy("lock")
+ private val appListAdapter: AppListAdapter = AppListAdapter()
+
+ @GuardedBy("lock")
+ private var runningApps: ArrayMap<UserPackage, RunningApp> = ArrayMap()
+
+ interface OnNumberOfPackagesChangedListener {
+ fun onNumberOfPackagesChanged(numPackages: Int)
+ }
+
+ interface OnDialogDismissedListener {
+ fun onDialogDismissed()
+ }
+
+ fun init() {
+ synchronized(lock) {
+ if (initialized) {
+ return
+ }
+ try {
+ activityManager.registerForegroundServiceObserver(this)
+ } catch (e: RemoteException) {
+ e.rethrowFromSystemServer()
+ }
+
+ deviceConfigProxy.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
+ backgroundExecutor) {
+ isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable)
+ }
+
+ isAvailable = deviceConfigProxy
+ .getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, false)
+
+ initialized = true
+ }
+ }
+
+ override fun onForegroundStateChanged(
+ token: IBinder,
+ packageName: String,
+ userId: Int,
+ isForeground: Boolean
+ ) {
+ synchronized(lock) {
+ val numPackagesBefore = getNumRunningPackagesLocked()
+ val userPackageKey = UserPackage(userId, packageName)
+ if (isForeground) {
+ runningServiceTokens.getOrPut(userPackageKey, { StartTimeAndTokens(systemClock) })
+ .addToken(token)
+ } else {
+ if (runningServiceTokens[userPackageKey]?.also {
+ it.removeToken(token) }?.isEmpty() == true) {
+ runningServiceTokens.remove(userPackageKey)
+ }
+ }
+
+ val numPackagesAfter = getNumRunningPackagesLocked()
+
+ if (numPackagesAfter != numPackagesBefore) {
+ onNumberOfPackagesChangedListeners.forEach {
+ backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) }
+ }
+ }
+
+ updateAppItemsLocked()
+ }
+ }
+
+ @GuardedBy("lock")
+ val onNumberOfPackagesChangedListeners: MutableSet<OnNumberOfPackagesChangedListener> =
+ mutableSetOf()
+
+ @GuardedBy("lock")
+ val onDialogDismissedListeners: MutableSet<OnDialogDismissedListener> = mutableSetOf()
+
+ fun addOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
+ synchronized(lock) {
+ onNumberOfPackagesChangedListeners.add(listener)
+ }
+ }
+
+ fun removeOnNumberOfPackagesChangedListener(listener: OnNumberOfPackagesChangedListener) {
+ synchronized(lock) {
+ onNumberOfPackagesChangedListeners.remove(listener)
+ }
+ }
+
+ fun addOnDialogDismissedListener(listener: OnDialogDismissedListener) {
+ synchronized(lock) {
+ onDialogDismissedListeners.add(listener)
+ }
+ }
+
+ fun removeOnDialogDismissedListener(listener: OnDialogDismissedListener) {
+ synchronized(lock) {
+ onDialogDismissedListeners.remove(listener)
+ }
+ }
+
+ fun isAvailable(): Boolean {
+ return isAvailable
+ }
+
+ fun getNumRunningPackages(): Int {
+ synchronized(lock) {
+ return getNumRunningPackagesLocked()
+ }
+ }
+
+ private fun getNumRunningPackagesLocked() =
+ runningServiceTokens.keys.count { it.uiControl != UIControl.HIDE_ENTRY }
+
+ fun shouldUpdateFooterVisibility() = dialog == null
+
+ fun showDialog(viewLaunchedFrom: View?) {
+ synchronized(lock) {
+ if (dialog == null) {
+
+ val dialog = SystemUIDialog(context)
+ dialog.setTitle(R.string.fgs_manager_dialog_title)
+
+ val dialogContext = dialog.context
+
+ val recyclerView = RecyclerView(dialogContext)
+ recyclerView.layoutManager = LinearLayoutManager(dialogContext)
+ recyclerView.adapter = appListAdapter
+
+ dialog.setView(recyclerView)
+
+ this.dialog = dialog
+
+ dialog.setOnDismissListener {
+ synchronized(lock) {
+ this.dialog = null
+ updateAppItemsLocked()
+ }
+ onDialogDismissedListeners.forEach {
+ mainExecutor.execute(it::onDialogDismissed)
+ }
+ }
+
+ mainExecutor.execute {
+ viewLaunchedFrom
+ ?.let { dialogLaunchAnimator.showFromView(dialog, it) } ?: dialog.show()
+ }
+
+ backgroundExecutor.execute {
+ synchronized(lock) {
+ updateAppItemsLocked()
+ }
+ }
+ }
+ }
+ }
+
+ @GuardedBy("lock")
+ private fun updateAppItemsLocked() {
+ if (dialog == null) {
+ runningApps.clear()
+ return
+ }
+
+ val addedPackages = runningServiceTokens.keys.filter {
+ it.uiControl != UIControl.HIDE_ENTRY && runningApps[it]?.stopped != true
+ }
+ val removedPackages = runningApps.keys.filter { !runningServiceTokens.containsKey(it) }
+
+ addedPackages.forEach {
+ val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
+ runningApps[it] = RunningApp(it.userId, it.packageName,
+ runningServiceTokens[it]!!.startTime, it.uiControl,
+ ai.loadLabel(packageManager), ai.loadIcon(packageManager))
+ }
+
+ removedPackages.forEach { pkg ->
+ val ra = runningApps[pkg]!!
+ val ra2 = ra.copy().also {
+ it.stopped = true
+ it.appLabel = ra.appLabel
+ it.icon = ra.icon
+ }
+ runningApps[pkg] = ra2
+ }
+
+ mainExecutor.execute {
+ appListAdapter
+ .setData(runningApps.values.toList().sortedByDescending { it.timeStarted })
+ }
+ }
+
+ private fun stopPackage(userId: Int, packageName: String) {
+ activityManager.stopAppForUser(packageName, userId)
+ }
+
+ private inner class AppListAdapter : RecyclerView.Adapter<AppItemViewHolder>() {
+ private val lock = Any()
+
+ @GuardedBy("lock")
+ private var data: List<RunningApp> = listOf()
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppItemViewHolder {
+ return AppItemViewHolder(LayoutInflater.from(parent.context)
+ .inflate(R.layout.fgs_manager_app_item, parent, false))
+ }
+
+ override fun onBindViewHolder(holder: AppItemViewHolder, position: Int) {
+ var runningApp: RunningApp
+ synchronized(lock) {
+ runningApp = data[position]
+ }
+ with(holder) {
+ iconView.setImageDrawable(runningApp.icon)
+ appLabelView.text = runningApp.appLabel
+ durationView.text = DateUtils.formatDuration(
+ max(systemClock.elapsedRealtime() - runningApp.timeStarted, 60000),
+ DateUtils.LENGTH_MEDIUM)
+ stopButton.setOnClickListener {
+ stopButton.setText(R.string.fgs_manager_app_item_stop_button_stopped_label)
+ stopPackage(runningApp.userId, runningApp.packageName)
+ }
+ if (runningApp.uiControl == UIControl.HIDE_BUTTON) {
+ stopButton.visibility = View.INVISIBLE
+ }
+ if (runningApp.stopped) {
+ stopButton.isEnabled = false
+ stopButton.setText(R.string.fgs_manager_app_item_stop_button_stopped_label)
+ durationView.visibility = View.GONE
+ } else {
+ stopButton.isEnabled = true
+ stopButton.setText(R.string.fgs_manager_app_item_stop_button_label)
+ durationView.visibility = View.VISIBLE
+ }
+ }
+ }
+
+ override fun getItemCount(): Int {
+ return data.size
+ }
+
+ fun setData(newData: List<RunningApp>) {
+ var oldData = data
+ data = newData
+
+ DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun getOldListSize(): Int {
+ return oldData.size
+ }
+
+ override fun getNewListSize(): Int {
+ return newData.size
+ }
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int):
+ Boolean {
+ return oldData[oldItemPosition] == newData[newItemPosition]
+ }
+
+ override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int):
+ Boolean {
+ return oldData[oldItemPosition].stopped == newData[newItemPosition].stopped
+ }
+ }).dispatchUpdatesTo(this)
+ }
+ }
+
+ private inner class UserPackage(
+ val userId: Int,
+ val packageName: String
+ ) {
+ val uiControl: UIControl by lazy {
+ val uid = packageManager.getPackageUidAsUser(packageName, userId)
+
+ when (activityManager.getBackgroundRestrictionExemptionReason(uid)) {
+ PowerExemptionManager.REASON_SYSTEM_UID,
+ PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
+
+ PowerExemptionManager.REASON_DEVICE_OWNER,
+ PowerExemptionManager.REASON_PROFILE_OWNER,
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT,
+ PowerExemptionManager.REASON_PROC_STATE_PERSISTENT_UI,
+ PowerExemptionManager.REASON_ROLE_DIALER,
+ PowerExemptionManager.REASON_SYSTEM_MODULE -> UIControl.HIDE_BUTTON
+ else -> UIControl.NORMAL
+ }
+ }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is UserPackage) {
+ return false
+ }
+ return other.packageName == packageName && other.userId == userId
+ }
+
+ override fun hashCode(): Int = Objects.hash(userId, packageName)
+ }
+
+ private data class StartTimeAndTokens(
+ val systemClock: SystemClock
+ ) {
+ val startTime = systemClock.elapsedRealtime()
+ val tokens = mutableSetOf<IBinder>()
+
+ fun addToken(token: IBinder) {
+ tokens.add(token)
+ }
+
+ fun removeToken(token: IBinder) {
+ tokens.remove(token)
+ }
+
+ fun isEmpty(): Boolean {
+ return tokens.isEmpty()
+ }
+ }
+
+ private class AppItemViewHolder(parent: View) : RecyclerView.ViewHolder(parent) {
+ val appLabelView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_label)
+ val durationView: TextView = parent.requireViewById(R.id.fgs_manager_app_item_duration)
+ val iconView: ImageView = parent.requireViewById(R.id.fgs_manager_app_item_icon)
+ val stopButton: Button = parent.requireViewById(R.id.fgs_manager_app_item_stop_button)
+ }
+
+ private data class RunningApp(
+ val userId: Int,
+ val packageName: String,
+ val timeStarted: Long,
+ val uiControl: UIControl
+ ) {
+ constructor(
+ userId: Int,
+ packageName: String,
+ timeStarted: Long,
+ uiControl: UIControl,
+ appLabel: CharSequence,
+ icon: Drawable
+ ) : this(userId, packageName, timeStarted, uiControl) {
+ this.appLabel = appLabel
+ this.icon = icon
+ }
+
+ // variables to keep out of the generated equals()
+ var appLabel: CharSequence = ""
+ var icon: Drawable? = null
+ var stopped = false
+ }
+
+ private enum class UIControl {
+ NORMAL, HIDE_BUTTON, HIDE_ENTRY
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
index 7ac9205..4aedbc9 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsController.kt
@@ -29,14 +29,16 @@
import com.android.internal.logging.UiEventLogger
import com.android.internal.logging.nano.MetricsProto
import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.settingslib.Utils
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.FooterActionsController.ExpansionState.COLLAPSED
-import com.android.systemui.qs.FooterActionsController.ExpansionState.EXPANDED
import com.android.systemui.qs.dagger.QSFlagsModule.PM_LITE_ENABLED
+import com.android.systemui.qs.dagger.QSScope
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.phone.SettingsButton
@@ -54,13 +56,14 @@
* Main difference between QS and QQS behaviour is condition when buttons should be visible,
* determined by [buttonsVisibleState]
*/
+@QSScope
class FooterActionsController @Inject constructor(
view: FooterActionsView,
+ multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
private val activityStarter: ActivityStarter,
private val userManager: UserManager,
private val userTracker: UserTracker,
private val userInfoController: UserInfoController,
- private val multiUserSwitchController: MultiUserSwitchController,
private val deviceProvisionedController: DeviceProvisionedController,
private val falsingManager: FalsingManager,
private val metricsLogger: MetricsLogger,
@@ -68,20 +71,34 @@
private val globalActionsDialog: GlobalActionsDialogLite,
private val uiEventLogger: UiEventLogger,
@Named(PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
- private val buttonsVisibleState: ExpansionState,
private val globalSetting: GlobalSettings,
- private val handler: Handler
+ private val handler: Handler,
+ private val featureFlags: FeatureFlags
) : ViewController<FooterActionsView>(view) {
- enum class ExpansionState { COLLAPSED, EXPANDED }
-
+ private var lastExpansion = -1f
private var listening: Boolean = false
- var expanded = false
+ private val alphaAnimator = TouchAnimator.Builder()
+ .addFloat(mView, "alpha", 0f, 1f)
+ .setStartDelay(0.9f)
+ .build()
+
+ var visible = true
+ set(value) {
+ field = value
+ updateVisibility()
+ }
+
+ init {
+ view.elevation = resources.displayMetrics.density * 4f
+ view.setBackgroundColor(Utils.getColorAttrDefaultColor(context, R.attr.underSurfaceColor))
+ }
private val settingsButton: SettingsButton = view.findViewById(R.id.settings_button)
private val settingsButtonContainer: View? = view.findViewById(R.id.settings_button_container)
private val powerMenuLite: View = view.findViewById(R.id.pm_lite)
+ private val multiUserSwitchController = multiUserSwitchControllerFactory.create(view)
private val onUserInfoChangedListener = OnUserInfoChangedListener { _, picture, _ ->
val isGuestUser: Boolean = userManager.isGuestUser(KeyguardUpdateMonitor.getCurrentUser())
@@ -99,9 +116,8 @@
}
private val onClickListener = View.OnClickListener { v ->
- // Don't do anything until views are unhidden. Don't do anything if the tap looks
- // suspicious.
- if (!buttonsVisible() || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
+ // Don't do anything if the tap looks suspicious.
+ if (!visible || falsingManager.isFalseTap(FalsingManager.LOW_PENALTY)) {
return@OnClickListener
}
if (v === settingsButton) {
@@ -110,9 +126,7 @@
activityStarter.postQSRunnableDismissingKeyguard {}
return@OnClickListener
}
- metricsLogger.action(
- if (expanded) MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH
- else MetricsProto.MetricsEvent.ACTION_QS_COLLAPSED_SETTINGS_LAUNCH)
+ metricsLogger.action(MetricsProto.MetricsEvent.ACTION_QS_EXPANDED_SETTINGS_LAUNCH)
if (settingsButton.isTunerClick) {
activityStarter.postQSRunnableDismissingKeyguard {
if (isTunerEnabled()) {
@@ -135,24 +149,14 @@
}
}
- private fun buttonsVisible(): Boolean {
- return when (buttonsVisibleState) {
- EXPANDED -> expanded
- COLLAPSED -> !expanded
- }
- }
-
override fun onInit() {
multiUserSwitchController.init()
}
- fun hideFooter() {
- mView.visibility = View.GONE
- }
-
- fun showFooter() {
- mView.visibility = View.VISIBLE
- updateView()
+ private fun updateVisibility() {
+ val previousVisibility = mView.visibility
+ mView.visibility = if (visible) View.VISIBLE else View.INVISIBLE
+ if (previousVisibility != mView.visibility) updateView()
}
private fun startSettingsActivity() {
@@ -204,24 +208,23 @@
}
fun setExpansion(headerExpansionFraction: Float) {
- mView.setExpansion(headerExpansionFraction)
- }
-
- fun updateAnimator(width: Int, numTiles: Int) {
- mView.updateAnimator(width, numTiles)
- }
-
- fun setKeyguardShowing() {
- mView.setKeyguardShowing()
- }
-
- fun refreshVisibility(shouldBeVisible: Boolean) {
- if (shouldBeVisible) {
- showFooter()
+ if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ if (headerExpansionFraction != lastExpansion) {
+ if (headerExpansionFraction >= 1f) {
+ mView.animate().alpha(1f).setDuration(500L).start()
+ } else if (lastExpansion >= 1f && headerExpansionFraction < 1f) {
+ mView.animate().alpha(0f).setDuration(250L).start()
+ }
+ lastExpansion = headerExpansionFraction
+ }
} else {
- hideFooter()
+ alphaAnimator.setPosition(headerExpansionFraction)
}
}
+ fun setKeyguardShowing(showing: Boolean) {
+ setExpansion(lastExpansion)
+ }
+
private fun isTunerEnabled() = tunerService.isTunerEnabled
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
deleted file mode 100644
index 7694be5..0000000
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsControllerBuilder.kt
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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.systemui.qs
-
-import android.os.Handler
-import android.os.UserManager
-import com.android.internal.logging.MetricsLogger
-import com.android.internal.logging.UiEventLogger
-import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.globalactions.GlobalActionsDialogLite
-import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.plugins.FalsingManager
-import com.android.systemui.qs.FooterActionsController.ExpansionState
-import com.android.systemui.qs.dagger.QSFlagsModule
-import com.android.systemui.settings.UserTracker
-import com.android.systemui.statusbar.phone.MultiUserSwitchController
-import com.android.systemui.statusbar.policy.DeviceProvisionedController
-import com.android.systemui.statusbar.policy.UserInfoController
-import com.android.systemui.tuner.TunerService
-import com.android.systemui.util.settings.GlobalSettings
-import javax.inject.Inject
-import javax.inject.Named
-
-class FooterActionsControllerBuilder @Inject constructor(
- private val activityStarter: ActivityStarter,
- private val userManager: UserManager,
- private val userTracker: UserTracker,
- private val userInfoController: UserInfoController,
- private val multiUserSwitchControllerFactory: MultiUserSwitchController.Factory,
- private val deviceProvisionedController: DeviceProvisionedController,
- private val falsingManager: FalsingManager,
- private val metricsLogger: MetricsLogger,
- private val tunerService: TunerService,
- private val globalActionsDialog: GlobalActionsDialogLite,
- private val uiEventLogger: UiEventLogger,
- @Named(QSFlagsModule.PM_LITE_ENABLED) private val showPMLiteButton: Boolean,
- private val globalSettings: GlobalSettings,
- @Main private val handler: Handler
-) {
- private lateinit var view: FooterActionsView
- private lateinit var buttonsVisibleState: ExpansionState
-
- fun withView(view: FooterActionsView): FooterActionsControllerBuilder {
- this.view = view
- return this
- }
-
- fun withButtonsVisibleWhen(state: ExpansionState): FooterActionsControllerBuilder {
- buttonsVisibleState = state
- return this
- }
-
- fun build(): FooterActionsController {
- return FooterActionsController(view, activityStarter, userManager,
- userTracker, userInfoController, multiUserSwitchControllerFactory.create(view),
- deviceProvisionedController, falsingManager, metricsLogger, tunerService,
- globalActionsDialog, uiEventLogger, showPMLiteButton, buttonsVisibleState,
- globalSettings, handler)
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
index e6fa2ae..18e0cfa 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/FooterActionsView.kt
@@ -44,8 +44,6 @@
private lateinit var multiUserAvatar: ImageView
private lateinit var tunerIcon: View
- private var settingsCogAnimator: TouchAnimator? = null
-
private var qsDisabled = false
private var expansionAmount = 0f
@@ -66,19 +64,6 @@
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
}
- fun updateAnimator(width: Int, numTiles: Int) {
- val size = (mContext.resources.getDimensionPixelSize(R.dimen.qs_quick_tile_size) -
- mContext.resources.getDimensionPixelSize(R.dimen.qs_tile_padding))
- val remaining = (width - numTiles * size) / (numTiles - 1)
- val defSpace = mContext.resources.getDimensionPixelOffset(R.dimen.default_gear_space)
- val translation = if (isLayoutRtl) (remaining - defSpace) else -(remaining - defSpace)
- settingsCogAnimator = TouchAnimator.Builder()
- .addFloat(settingsButton, "translationX", translation.toFloat(), 0f)
- .addFloat(settingsButton, "rotation", -120f, 0f)
- .build()
- setExpansion(expansionAmount)
- }
-
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateResources()
@@ -95,15 +80,6 @@
tunerIcon.translationX = if (isLayoutRtl) (-tunerIconTranslation) else tunerIconTranslation
}
- fun setKeyguardShowing() {
- setExpansion(expansionAmount)
- }
-
- fun setExpansion(headerExpansionFraction: Float) {
- expansionAmount = headerExpansionFraction
- if (settingsCogAnimator != null) settingsCogAnimator!!.setPosition(headerExpansionFraction)
- }
-
fun disable(
state2: Int,
isTunerEnabled: Boolean,
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
index ded6ae0..d1b569f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSAnimator.java
@@ -14,9 +14,6 @@
package com.android.systemui.qs;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
-
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.util.Log;
@@ -49,7 +46,6 @@
import java.util.concurrent.Executor;
import javax.inject.Inject;
-import javax.inject.Named;
/** */
@QSScope
@@ -88,8 +84,6 @@
private final QSFgsManagerFooter mFgsManagerFooter;
private final QSSecurityFooter mSecurityFooter;
private final QS mQs;
- private final View mQSFooterActions;
- private final View mQQSFooterActions;
@Nullable
private PagedTileLayout mPagedLayout;
@@ -154,16 +148,12 @@
QuickQSPanelController quickQSPanelController, QSTileHost qsTileHost,
QSFgsManagerFooter fgsManagerFooter, QSSecurityFooter securityFooter,
@Main Executor executor, TunerService tunerService,
- QSExpansionPathInterpolator qsExpansionPathInterpolator,
- @Named(QS_FOOTER) FooterActionsView qsFooterActionsView,
- @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
+ QSExpansionPathInterpolator qsExpansionPathInterpolator) {
mQs = qs;
mQuickQsPanel = quickPanel;
mQsPanelController = qsPanelController;
mQuickQSPanelController = quickQSPanelController;
mQuickStatusBarHeader = quickStatusBarHeader;
- mQQSFooterActions = qqsFooterActionsView;
- mQSFooterActions = qsFooterActionsView;
mFgsManagerFooter = fgsManagerFooter;
mSecurityFooter = securityFooter;
mHost = qsTileHost;
@@ -476,12 +466,6 @@
.setListener(this)
.build();
- if (mQQSFooterActions.getVisibility() != View.GONE) {
- // only when qqs footer is present (which means split shade mode) it needs to
- // be animated
- updateQQSFooterAnimation();
- }
-
// Fade in the security footer and the divider as we reach the final position
Builder builder = new Builder().setStartDelay(EXPANDED_TILE_DELAY);
builder.addFloat(mFgsManagerFooter.getView(), "alpha", 0, 1);
@@ -627,14 +611,6 @@
}
}
- private void updateQQSFooterAnimation() {
- int translationY = getRelativeTranslationY(mQSFooterActions, mQQSFooterActions);
- mQQSFooterActionsAnimator = new TouchAnimator.Builder()
- .addFloat(mQQSFooterActions, "translationY", 0, translationY)
- .build();
- mAnimatedQsViews.add(mQSFooterActions);
- }
-
private int getRelativeTranslationY(View view1, View view2) {
int[] qsPosition = new int[2];
int[] qqsPosition = new int[2];
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
index e230e1b..7800027 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSContainerImpl.java
@@ -19,10 +19,8 @@
import static android.app.StatusBarManager.DISABLE2_QUICK_SETTINGS;
import android.content.Context;
-import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Path;
-import android.graphics.Point;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
@@ -41,7 +39,6 @@
*/
public class QSContainerImpl extends FrameLayout implements Dumpable {
- private final Point mSizePoint = new Point();
private int mFancyClippingTop;
private int mFancyClippingBottom;
private final float[] mFancyClippingRadii = new float[] {0, 0, 0, 0, 0, 0, 0, 0};
@@ -78,12 +75,6 @@
}
@Override
- protected void onConfigurationChanged(Configuration newConfig) {
- super.onConfigurationChanged(newConfig);
- mSizePoint.set(0, 0); // Will be retrieved on next measure pass.
- }
-
- @Override
public boolean performClick() {
// Want to receive clicks so missing QQS tiles doesn't cause collapse, but
// don't want to do anything with them.
@@ -152,10 +143,10 @@
void updateResources(QSPanelController qsPanelController,
QuickStatusBarHeaderController quickStatusBarHeaderController) {
mQSPanelContainer.setPaddingRelative(
- getPaddingStart(),
+ mQSPanelContainer.getPaddingStart(),
Utils.getQsHeaderSystemIconsAreaHeight(mContext),
- getPaddingEnd(),
- getPaddingBottom()
+ mQSPanelContainer.getPaddingEnd(),
+ mQSPanelContainer.getPaddingBottom()
);
int sideMargins = getResources().getDimensionPixelSize(R.dimen.notification_side_paddings);
@@ -241,13 +232,6 @@
}
}
- private int getDisplayHeight() {
- if (mSizePoint.y == 0) {
- getDisplay().getRealSize(mSizePoint);
- }
- return mSizePoint.y;
- }
-
/**
* Clip QS bottom using a concave shape.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
index 082de16..55d4a53 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFgsManagerFooter.java
@@ -16,13 +16,9 @@
package com.android.systemui.qs;
-import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
-
-import static com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FGS_MANAGER_FOOTER_VIEW;
import android.content.Context;
-import android.provider.DeviceConfig;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
@@ -30,8 +26,6 @@
import com.android.systemui.R;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
-import com.android.systemui.fgsmanager.FgsManagerDialogFactory;
-import com.android.systemui.statusbar.policy.RunningFgsController;
import java.util.concurrent.Executor;
@@ -41,24 +35,25 @@
/**
* Footer entry point for the foreground service manager
*/
-public class QSFgsManagerFooter implements View.OnClickListener {
+public class QSFgsManagerFooter implements View.OnClickListener,
+ FgsManagerController.OnDialogDismissedListener,
+ FgsManagerController.OnNumberOfPackagesChangedListener {
private final View mRootView;
private final TextView mFooterText;
private final Context mContext;
private final Executor mMainExecutor;
private final Executor mExecutor;
- private final RunningFgsController mRunningFgsController;
- private final FgsManagerDialogFactory mFgsManagerDialogFactory;
+
+ private final FgsManagerController mFgsManagerController;
private boolean mIsInitialized = false;
- private boolean mIsAvailable = false;
+ private int mNumPackages;
@Inject
QSFgsManagerFooter(@Named(QS_FGS_MANAGER_FOOTER_VIEW) View rootView,
- @Main Executor mainExecutor, RunningFgsController runningFgsController,
- @Background Executor executor,
- FgsManagerDialogFactory fgsManagerDialogFactory) {
+ @Main Executor mainExecutor, @Background Executor executor,
+ FgsManagerController fgsManagerController) {
mRootView = rootView;
mFooterText = mRootView.findViewById(R.id.footer_text);
ImageView icon = mRootView.findViewById(R.id.primary_footer_icon);
@@ -66,8 +61,7 @@
mContext = rootView.getContext();
mMainExecutor = mainExecutor;
mExecutor = executor;
- mRunningFgsController = runningFgsController;
- mFgsManagerDialogFactory = fgsManagerDialogFactory;
+ mFgsManagerController = fgsManagerController;
}
public void init() {
@@ -75,22 +69,28 @@
return;
}
+ mFgsManagerController.init();
+
mRootView.setOnClickListener(this);
- mRunningFgsController.addCallback(packages -> refreshState());
-
- DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI, mExecutor,
- (DeviceConfig.OnPropertiesChangedListener) properties -> {
- mIsAvailable = properties.getBoolean(TASK_MANAGER_ENABLED, mIsAvailable);
- });
- mIsAvailable = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI, TASK_MANAGER_ENABLED, false);
-
mIsInitialized = true;
}
+ public void setListening(boolean listening) {
+ if (listening) {
+ mFgsManagerController.addOnDialogDismissedListener(this);
+ mFgsManagerController.addOnNumberOfPackagesChangedListener(this);
+ mNumPackages = mFgsManagerController.getNumRunningPackages();
+ refreshState();
+ } else {
+ mFgsManagerController.removeOnDialogDismissedListener(this);
+ mFgsManagerController.removeOnNumberOfPackagesChangedListener(this);
+ }
+ }
+
@Override
public void onClick(View view) {
- mFgsManagerDialogFactory.create(mRootView);
+ mFgsManagerController.showDialog(mRootView);
}
public void refreshState() {
@@ -101,17 +101,25 @@
return mRootView;
}
- private boolean isAvailable() {
- return mIsAvailable;
- }
-
public void handleRefreshState() {
- int numPackages = mRunningFgsController.getPackagesWithFgs().size();
mMainExecutor.execute(() -> {
mFooterText.setText(mContext.getResources().getQuantityString(
- R.plurals.fgs_manager_footer_label, numPackages, numPackages));
- mRootView.setVisibility(numPackages > 0 && isAvailable() ? View.VISIBLE : View.GONE);
+ R.plurals.fgs_manager_footer_label, mNumPackages, mNumPackages));
+ if (mFgsManagerController.shouldUpdateFooterVisibility()) {
+ mRootView.setVisibility(mNumPackages > 0
+ && mFgsManagerController.isAvailable() ? View.VISIBLE : View.GONE);
+ }
});
}
+ @Override
+ public void onDialogDismissed() {
+ refreshState();
+ }
+
+ @Override
+ public void onNumberOfPackagesChanged(int numPackages) {
+ mNumPackages = numPackages;
+ refreshState();
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
index 0e0681b..aac5672 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooter.java
@@ -42,11 +42,6 @@
*/
void setExpansion(float expansion);
- /**
- * Sets whether or not this footer should set itself to listen for changes in any callbacks
- * that it has implemented.
- */
- void setListening(boolean listening);
/**
* Sets whether or not the keyguard is currently being shown.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
index 4622660..6c0ca49 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterView.java
@@ -46,7 +46,6 @@
public class QSFooterView extends FrameLayout {
private PageIndicator mPageIndicator;
private TextView mBuildText;
- private View mActionsContainer;
private View mEditButton;
@Nullable
@@ -78,7 +77,6 @@
protected void onFinishInflate() {
super.onFinishInflate();
mPageIndicator = findViewById(R.id.footer_page_indicator);
- mActionsContainer = requireViewById(R.id.qs_footer_actions);
mBuildText = findViewById(R.id.build);
mEditButton = findViewById(android.R.id.edit);
@@ -105,10 +103,6 @@
}
}
- void updateExpansion() {
- setExpansion(mExpansionAmount);
- }
-
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -129,7 +123,6 @@
@Nullable
private TouchAnimator createFooterAnimator() {
TouchAnimator.Builder builder = new TouchAnimator.Builder()
- .addFloat(mActionsContainer, "alpha", 0, 1)
.addFloat(mPageIndicator, "alpha", 0, 1)
.addFloat(mBuildText, "alpha", 0, 1)
.addFloat(mEditButton, "alpha", 0, 1)
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
index 5327b7e..bef4f43 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFooterViewController.java
@@ -16,8 +16,6 @@
package com.android.systemui.qs;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QS_FOOTER;
-
import android.content.ClipData;
import android.content.ClipboardManager;
import android.text.TextUtils;
@@ -33,7 +31,6 @@
import com.android.systemui.util.ViewController;
import javax.inject.Inject;
-import javax.inject.Named;
/**
* Controller for {@link QSFooterView}.
@@ -43,8 +40,6 @@
private final UserTracker mUserTracker;
private final QSPanelController mQsPanelController;
- private final QuickQSPanelController mQuickQSPanelController;
- private final FooterActionsController mFooterActionsController;
private final TextView mBuildText;
private final PageIndicator mPageIndicator;
private final View mEditButton;
@@ -56,14 +51,10 @@
UserTracker userTracker,
FalsingManager falsingManager,
ActivityStarter activityStarter,
- QSPanelController qsPanelController,
- QuickQSPanelController quickQSPanelController,
- @Named(QS_FOOTER) FooterActionsController footerActionsController) {
+ QSPanelController qsPanelController) {
super(view);
mUserTracker = userTracker;
mQsPanelController = qsPanelController;
- mQuickQSPanelController = quickQSPanelController;
- mFooterActionsController = footerActionsController;
mFalsingManager = falsingManager;
mActivityStarter = activityStarter;
@@ -73,21 +64,7 @@
}
@Override
- protected void onInit() {
- super.onInit();
- mFooterActionsController.init();
- }
-
- @Override
protected void onViewAttached() {
- mView.addOnLayoutChangeListener(
- (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
- mView.updateExpansion();
- mFooterActionsController.updateAnimator(right - left,
- mQuickQSPanelController.getNumQuickTiles());
- }
- );
-
mBuildText.setOnLongClickListener(view -> {
CharSequence buildText = mBuildText.getText();
if (!TextUtils.isEmpty(buildText)) {
@@ -114,9 +91,7 @@
}
@Override
- protected void onViewDetached() {
- setListening(false);
- }
+ protected void onViewDetached() {}
@Override
public void setVisibility(int visibility) {
@@ -126,25 +101,17 @@
@Override
public void setExpanded(boolean expanded) {
- mFooterActionsController.setExpanded(expanded);
mView.setExpanded(expanded);
}
@Override
public void setExpansion(float expansion) {
mView.setExpansion(expansion);
- mFooterActionsController.setExpansion(expansion);
- }
-
- @Override
- public void setListening(boolean listening) {
- mFooterActionsController.setListening(listening);
}
@Override
public void setKeyguardShowing(boolean keyguardShowing) {
mView.setKeyguardShowing();
- mFooterActionsController.setKeyguardShowing();
}
/** */
@@ -156,6 +123,5 @@
@Override
public void disable(int state1, int state2, boolean animate) {
mView.disable(state2);
- mFooterActionsController.disable(state2);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
index 259b786..50952bd 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSFragment.java
@@ -118,6 +118,7 @@
private QSPanelController mQSPanelController;
private QuickQSPanelController mQuickQSPanelController;
private QSCustomizerController mQSCustomizerController;
+ private FooterActionsController mQSFooterActionController;
@Nullable
private ScrollListener mScrollListener;
/**
@@ -188,9 +189,11 @@
QSFragmentComponent qsFragmentComponent = mQsComponentFactory.create(this);
mQSPanelController = qsFragmentComponent.getQSPanelController();
mQuickQSPanelController = qsFragmentComponent.getQuickQSPanelController();
+ mQSFooterActionController = qsFragmentComponent.getQSFooterActionController();
mQSPanelController.init();
mQuickQSPanelController.init();
+ mQSFooterActionController.init();
mQSPanelScrollView = view.findViewById(R.id.expanded_qs_scroll_view);
mQSPanelScrollView.addOnLayoutChangeListener(
@@ -380,6 +383,7 @@
mContainer.disable(state1, state2, animate);
mHeader.disable(state1, state2, animate);
mFooter.disable(state1, state2, animate);
+ mQSFooterActionController.disable(state2);
updateQsState();
}
@@ -396,10 +400,10 @@
: View.INVISIBLE);
mHeader.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (expanded && !mStackScrollerOverscrolling), mQuickQSPanelController);
- mFooter.setVisibility(!mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
- || mShowCollapsedOnKeyguard)
- ? View.VISIBLE
- : View.INVISIBLE);
+ boolean footerVisible = !mQsDisabled && (expanded || !keyguardShowing || mHeaderAnimating
+ || mShowCollapsedOnKeyguard);
+ mFooter.setVisibility(footerVisible ? View.VISIBLE : View.INVISIBLE);
+ mQSFooterActionController.setVisible(footerVisible);
mFooter.setExpanded((keyguardShowing && !mHeaderAnimating && !mShowCollapsedOnKeyguard)
|| (expanded && !mStackScrollerOverscrolling));
mQSPanelController.setVisibility(
@@ -465,6 +469,7 @@
}
mFooter.setKeyguardShowing(keyguardShowing);
+ mQSFooterActionController.setKeyguardShowing(keyguardShowing);
updateQsState();
}
@@ -480,14 +485,13 @@
if (DEBUG) Log.d(TAG, "setListening " + listening);
mListening = listening;
mQSContainerImplController.setListening(listening);
- mFooter.setListening(listening);
+ mQSFooterActionController.setListening(listening);
mQSPanelController.setListening(mListening, mQsExpanded);
}
@Override
public void setHeaderListening(boolean listening) {
mQSContainerImplController.setListening(listening);
- mFooter.setListening(listening);
}
@Override
@@ -561,6 +565,7 @@
}
}
mFooter.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
+ mQSFooterActionController.setExpansion(onKeyguardAndExpanded ? 1 : expansion);
mQSPanelController.setRevealExpansion(expansion);
mQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
mQuickQSPanelController.getTileLayout().setExpansion(expansion, proposedTranslation);
@@ -711,6 +716,7 @@
boolean customizing = isCustomizing();
mQSPanelScrollView.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
mFooter.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
+ mQSFooterActionController.setVisible(!customizing);
mHeader.setVisibility(!customizing ? View.VISIBLE : View.INVISIBLE);
// Let the panel know the position changed and it needs to update where notifications
// and whatnot are.
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
index cbfe944..8f268b5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSPanelController.java
@@ -192,6 +192,7 @@
refreshAllTiles();
}
+ mQSFgsManagerFooter.setListening(listening);
mQsSecurityFooter.setListening(listening);
// Set the listening as soon as the QS fragment starts listening regardless of the
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 92690c7d..2d2fa1f 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,7 +17,6 @@
package com.android.systemui.qs;
import static com.android.systemui.media.dagger.MediaModule.QUICK_QS_PANEL;
-import static com.android.systemui.qs.dagger.QSFragmentModule.QQS_FOOTER;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
import com.android.internal.logging.MetricsLogger;
@@ -54,7 +53,6 @@
// brightness is visible only in split shade
private final QuickQSBrightnessController mBrightnessController;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
- private final FooterActionsController mFooterActionsController;
@Inject
QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
@@ -63,14 +61,12 @@
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
MetricsLogger metricsLogger, UiEventLogger uiEventLogger, QSLogger qsLogger,
DumpManager dumpManager,
- QuickQSBrightnessController quickQSBrightnessController,
- @Named(QQS_FOOTER) FooterActionsController footerActionsController
+ QuickQSBrightnessController quickQSBrightnessController
) {
super(view, qsTileHost, qsCustomizerController, usingMediaPlayer, mediaHost, metricsLogger,
uiEventLogger, qsLogger, dumpManager);
mBrightnessController = quickQSBrightnessController;
mBrightnessMirrorHandler = new BrightnessMirrorHandler(mBrightnessController);
- mFooterActionsController = footerActionsController;
}
@Override
@@ -80,8 +76,6 @@
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
mBrightnessController.init(mShouldUseSplitNotificationShade);
- mFooterActionsController.init();
- mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
}
@Override
@@ -102,7 +96,6 @@
void setListening(boolean listening) {
super.setListening(listening);
mBrightnessController.setListening(listening);
- mFooterActionsController.setListening(listening);
}
public boolean isListening() {
@@ -123,7 +116,6 @@
@Override
protected void onConfigurationChanged() {
mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
- mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
index 63cbc21..594f4f8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentComponent.java
@@ -16,6 +16,7 @@
package com.android.systemui.qs.dagger;
+import com.android.systemui.qs.FooterActionsController;
import com.android.systemui.qs.QSAnimator;
import com.android.systemui.qs.QSContainerImplController;
import com.android.systemui.qs.QSFooter;
@@ -61,4 +62,7 @@
/** Construct a {@link QSSquishinessController}. */
QSSquishinessController getQSSquishinessController();
+
+ /** Construct a {@link FooterActionsController}. */
+ FooterActionsController getQSFooterActionController();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
index 1958caf..776ee10 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -21,15 +21,15 @@
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
+import android.view.ViewStub;
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.dagger.qualifiers.RootView;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.privacy.OngoingPrivacyChip;
-import com.android.systemui.qs.FooterActionsController;
-import com.android.systemui.qs.FooterActionsController.ExpansionState;
-import com.android.systemui.qs.FooterActionsControllerBuilder;
import com.android.systemui.qs.FooterActionsView;
import com.android.systemui.qs.QSContainerImpl;
import com.android.systemui.qs.QSFooter;
@@ -55,8 +55,6 @@
public interface QSFragmentModule {
String QS_FGS_MANAGER_FOOTER_VIEW = "qs_fgs_manager_footer";
String QS_SECURITY_FOOTER_VIEW = "qs_security_footer";
- String QQS_FOOTER = "qqs_footer";
- String QS_FOOTER = "qs_footer";
String QS_USING_MEDIA_PLAYER = "qs_using_media_player";
/**
@@ -122,46 +120,26 @@
return view.findViewById(R.id.qs_footer);
}
- /** */
+ /**
+ * Provides a {@link FooterActionsView}.
+ *
+ * This will replace a ViewStub either in {@link QSFooterView} or in {@link QSContainerImpl}.
+ */
@Provides
- @Named(QS_FOOTER)
- static FooterActionsView providesQSFooterActionsView(@RootView View view) {
+ static FooterActionsView providesQSFooterActionsView(@RootView View view,
+ FeatureFlags featureFlags) {
+ ViewStub stub;
+ if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ stub = view.requireViewById(R.id.container_stub);
+ } else {
+ stub = view.requireViewById(R.id.footer_stub);
+ }
+ stub.inflate();
return view.findViewById(R.id.qs_footer_actions);
}
/** */
@Provides
- @Named(QQS_FOOTER)
- static FooterActionsView providesQQSFooterActionsView(@RootView View view) {
- return view.findViewById(R.id.qqs_footer_actions);
- }
-
- /** */
- @Provides
- @Named(QQS_FOOTER)
- static FooterActionsController providesQQSFooterActionsController(
- FooterActionsControllerBuilder footerActionsControllerBuilder,
- @Named(QQS_FOOTER) FooterActionsView qqsFooterActionsView) {
- return footerActionsControllerBuilder
- .withView(qqsFooterActionsView)
- .withButtonsVisibleWhen(ExpansionState.COLLAPSED)
- .build();
- }
-
- /** */
- @Provides
- @Named(QS_FOOTER)
- static FooterActionsController providesQSFooterActionsController(
- FooterActionsControllerBuilder footerActionsControllerBuilder,
- @Named(QS_FOOTER) FooterActionsView qsFooterActionsView) {
- return footerActionsControllerBuilder
- .withView(qsFooterActionsView)
- .withButtonsVisibleWhen(ExpansionState.EXPANDED)
- .build();
- }
-
- /** */
- @Provides
@QSScope
static QSFooter providesQSFooter(QSFooterViewController qsFooterViewController) {
qsFooterViewController.init();
diff --git a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
index 48255b5..6d1bbee 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -34,8 +34,6 @@
import com.android.systemui.statusbar.policy.DataSaverController;
import com.android.systemui.statusbar.policy.DeviceControlsController;
import com.android.systemui.statusbar.policy.HotspotController;
-import com.android.systemui.statusbar.policy.RunningFgsController;
-import com.android.systemui.statusbar.policy.RunningFgsControllerImpl;
import com.android.systemui.statusbar.policy.WalletController;
import com.android.systemui.util.settings.SecureSettings;
@@ -91,9 +89,4 @@
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);
-
- /** */
- @Binds
- RunningFgsController provideRunningFgsController(
- RunningFgsControllerImpl runningFgsController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
index 83d8d19..30456a8 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotController.java
@@ -344,7 +344,7 @@
};
mContext.registerReceiver(mCopyBroadcastReceiver, new IntentFilter(
ClipboardOverlayController.COPY_OVERLAY_ACTION),
- ClipboardOverlayController.SELF_PERMISSION, null);
+ ClipboardOverlayController.SELF_PERMISSION, null, Context.RECEIVER_NOT_EXPORTED);
}
void takeScreenshotFullscreen(ComponentName topComponent, Consumer<Uri> finisher,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
index 9d43d30..2f5eaa6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java
@@ -43,6 +43,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.fingerprint.IUdfpsHbmListener;
import android.inputmethodservice.InputMethodService.BackDispositionMode;
+import android.media.MediaRoute2Info;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -63,6 +64,7 @@
import com.android.internal.os.SomeArgs;
import com.android.internal.statusbar.IAddTileResultCallback;
import com.android.internal.statusbar.IStatusBar;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.StatusBarIcon;
import com.android.internal.util.GcUtils;
import com.android.internal.view.AppearanceRegion;
@@ -156,6 +158,8 @@
private static final int MSG_TILE_SERVICE_REQUEST_ADD = 61 << MSG_SHIFT;
private static final int MSG_TILE_SERVICE_REQUEST_CANCEL = 62 << MSG_SHIFT;
private static final int MSG_SET_BIOMETRICS_LISTENER = 63 << MSG_SHIFT;
+ private static final int MSG_MEDIA_TRANSFER_SENDER_STATE = 64 << MSG_SHIFT;
+ private static final int MSG_MEDIA_TRANSFER_RECEIVER_STATE = 65 << MSG_SHIFT;
public static final int FLAG_EXCLUDE_NONE = 0;
public static final int FLAG_EXCLUDE_SEARCH_PANEL = 1 << 0;
@@ -439,6 +443,17 @@
* @see IStatusBar#cancelRequestAddTile
*/
default void cancelRequestAddTile(@NonNull String packageName) {}
+
+ /** @see IStatusBar#updateMediaTapToTransferSenderDisplay */
+ default void updateMediaTapToTransferSenderDisplay(
+ @StatusBarManager.MediaTransferSenderState int displayState,
+ @NonNull MediaRoute2Info routeInfo,
+ @Nullable IUndoMediaTransferCallback undoCallback) {}
+
+ /** @see IStatusBar#updateMediaTapToTransferReceiverDisplay */
+ default void updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState int displayState,
+ @NonNull MediaRoute2Info routeInfo) {}
}
public CommandQueue(Context context) {
@@ -1177,6 +1192,29 @@
mHandler.obtainMessage(MSG_TILE_SERVICE_REQUEST_CANCEL, s).sendToTarget();
}
+ @Override
+ public void updateMediaTapToTransferSenderDisplay(
+ @StatusBarManager.MediaTransferSenderState int displayState,
+ MediaRoute2Info routeInfo,
+ IUndoMediaTransferCallback undoCallback
+ ) throws RemoteException {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = displayState;
+ args.arg2 = routeInfo;
+ args.arg3 = undoCallback;
+ mHandler.obtainMessage(MSG_MEDIA_TRANSFER_SENDER_STATE, args).sendToTarget();
+ }
+
+ @Override
+ public void updateMediaTapToTransferReceiverDisplay(
+ int displayState,
+ MediaRoute2Info routeInfo) {
+ SomeArgs args = SomeArgs.obtain();
+ args.arg1 = displayState;
+ args.arg2 = routeInfo;
+ mHandler.obtainMessage(MSG_MEDIA_TRANSFER_RECEIVER_STATE, args).sendToTarget();
+ }
+
private final class H extends Handler {
private H(Looper l) {
super(l);
@@ -1574,6 +1612,29 @@
for (int i = 0; i < mCallbacks.size(); i++) {
mCallbacks.get(i).cancelRequestAddTile(packageName);
}
+ break;
+ case MSG_MEDIA_TRANSFER_SENDER_STATE:
+ args = (SomeArgs) msg.obj;
+ int displayState = (int) args.arg1;
+ MediaRoute2Info routeInfo = (MediaRoute2Info) args.arg2;
+ IUndoMediaTransferCallback undoCallback =
+ (IUndoMediaTransferCallback) args.arg3;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).updateMediaTapToTransferSenderDisplay(
+ displayState, routeInfo, undoCallback);
+ }
+ args.recycle();
+ break;
+ case MSG_MEDIA_TRANSFER_RECEIVER_STATE:
+ args = (SomeArgs) msg.obj;
+ int receiverDisplayState = (int) args.arg1;
+ MediaRoute2Info receiverRouteInfo = (MediaRoute2Info) args.arg2;
+ for (int i = 0; i < mCallbacks.size(); i++) {
+ mCallbacks.get(i).updateMediaTapToTransferReceiverDisplay(
+ receiverDisplayState, receiverRouteInfo);
+ }
+ args.recycle();
+ break;
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
index 391525e..092e86d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationViewHierarchyManager.java
@@ -179,6 +179,7 @@
if (!mNotifPipelineFlags.checkLegacyPipelineEnabled()) {
return;
}
+ Trace.beginSection("NotificationViewHierarchyManager.updateNotificationViews");
beginUpdate();
@@ -353,6 +354,7 @@
mListContainer.onNotificationViewUpdateFinished();
endUpdate();
+ Trace.endSection();
}
/**
@@ -362,6 +364,7 @@
* {@link com.android.systemui.statusbar.notification.collection.coordinator.StackCoordinator}
*/
private void updateNotifStats() {
+ Trace.beginSection("NotificationViewHierarchyManager.updateNotifStats");
boolean hasNonClearableAlertingNotifs = false;
boolean hasClearableAlertingNotifs = false;
boolean hasNonClearableSilentNotifs = false;
@@ -403,6 +406,7 @@
hasNonClearableSilentNotifs /* hasNonClearableSilentNotifs */,
hasClearableSilentNotifs /* hasClearableSilentNotifs */
));
+ Trace.endSection();
}
/**
@@ -520,7 +524,7 @@
}
private void updateRowStatesInternal() {
- Trace.beginSection("NotificationViewHierarchyManager#updateRowStates");
+ Trace.beginSection("NotificationViewHierarchyManager.updateRowStates");
final int N = mListContainer.getContainerChildCount();
int visibleNotifications = 0;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
index c331608..ad9f12e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/NotificationEntryManager.java
@@ -24,6 +24,7 @@
import android.app.NotificationChannel;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.Trace;
import android.os.UserHandle;
import android.service.notification.NotificationListenerService;
import android.service.notification.NotificationListenerService.Ranking;
@@ -343,11 +344,14 @@
private final InflationCallback mInflationCallback = new InflationCallback() {
@Override
public void handleInflationException(NotificationEntry entry, Exception e) {
+ Trace.beginSection("NotificationEntryManager.handleInflationException");
NotificationEntryManager.this.handleInflationException(entry.getSbn(), e);
+ Trace.endSection();
}
@Override
public void onAsyncInflationFinished(NotificationEntry entry) {
+ Trace.beginSection("NotificationEntryManager.onAsyncInflationFinished");
mPendingNotifications.remove(entry.getKey());
// If there was an async task started after the removal, we don't want to add it back to
// the list, otherwise we might get leaks.
@@ -369,6 +373,7 @@
}
}
}
+ Trace.endSection();
}
};
@@ -463,6 +468,7 @@
boolean forceRemove,
DismissedByUserStats dismissedByUserStats,
int reason) {
+ Trace.beginSection("NotificationEntryManager.removeNotificationInternal");
final NotificationEntry entry = getActiveNotificationUnfiltered(key);
@@ -470,6 +476,7 @@
if (interceptor.onNotificationRemoveRequested(key, entry, reason)) {
// Remove intercepted; log and skip
mLogger.logRemovalIntercepted(key);
+ Trace.endSection();
return;
}
}
@@ -557,6 +564,7 @@
mLeakDetector.trackGarbage(entry);
}
}
+ Trace.endSection();
}
private void sendNotificationRemovalToServer(
@@ -620,6 +628,7 @@
private void addNotificationInternal(
StatusBarNotification notification,
RankingMap rankingMap) throws InflationException {
+ Trace.beginSection("NotificationEntryManager.addNotificationInternal");
String key = notification.getKey();
if (DEBUG) {
Log.d(TAG, "addNotification key=" + key);
@@ -667,6 +676,7 @@
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingApplied();
}
+ Trace.endSection();
}
public void addNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -679,12 +689,14 @@
private void updateNotificationInternal(StatusBarNotification notification,
RankingMap ranking) throws InflationException {
+ Trace.beginSection("NotificationEntryManager.updateNotificationInternal");
if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
final String key = notification.getKey();
abortExistingInflation(key, "updateNotification");
final NotificationEntry entry = getActiveNotificationUnfiltered(key);
if (entry == null) {
+ Trace.endSection();
return;
}
@@ -721,6 +733,7 @@
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingApplied();
}
+ Trace.endSection();
}
public void updateNotification(StatusBarNotification notification, RankingMap ranking) {
@@ -740,14 +753,17 @@
mLogger.logUseWhileNewPipelineActive("updateNotifications", reason);
return;
}
+ Trace.beginSection("NotificationEntryManager.updateNotifications");
reapplyFilterAndSort(reason);
if (mPresenter != null) {
mPresenter.updateNotificationViews(reason);
}
mNotifLiveDataStore.setActiveNotifList(getVisibleNotifications());
+ Trace.endSection();
}
public void updateNotificationRanking(RankingMap rankingMap) {
+ Trace.beginSection("NotificationEntryManager.updateNotificationRanking");
List<NotificationEntry> entries = new ArrayList<>();
entries.addAll(getVisibleNotifications());
entries.addAll(mPendingNotifications.values());
@@ -788,6 +804,7 @@
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingApplied();
}
+ Trace.endSection();
}
void notifyChannelModified(
@@ -887,6 +904,7 @@
/** @return list of active notifications filtered for the current user */
public List<NotificationEntry> getActiveNotificationsForCurrentUser() {
+ Trace.beginSection("NotificationEntryManager.getActiveNotificationsForCurrentUser");
Assert.isMainThread();
ArrayList<NotificationEntry> filtered = new ArrayList<>();
@@ -898,7 +916,7 @@
}
filtered.add(entry);
}
-
+ Trace.endSection();
return filtered;
}
@@ -908,10 +926,12 @@
* @param reason the reason for calling this method, which will be logged
*/
public void updateRanking(RankingMap rankingMap, String reason) {
+ Trace.beginSection("NotificationEntryManager.updateRanking");
updateRankingAndSort(rankingMap, reason);
for (NotifCollectionListener listener : mNotifCollectionListeners) {
listener.onRankingApplied();
}
+ Trace.endSection();
}
/** Resorts / filters the current notification set with the current RankingMap */
@@ -920,7 +940,9 @@
mLogger.logUseWhileNewPipelineActive("reapplyFilterAndSort", reason);
return;
}
+ Trace.beginSection("NotificationEntryManager.reapplyFilterAndSort");
updateRankingAndSort(mRanker.getRankingMap(), reason);
+ Trace.endSection();
}
/** Calls to NotificationRankingManager and updates mSortedAndFiltered */
@@ -929,9 +951,11 @@
mLogger.logUseWhileNewPipelineActive("updateRankingAndSort", reason);
return;
}
+ Trace.beginSection("NotificationEntryManager.updateRankingAndSort");
mSortedAndFiltered.clear();
mSortedAndFiltered.addAll(mRanker.updateRanking(
rankingMap, mActiveNotifications.values(), reason));
+ Trace.endSection();
}
/** dump the current active notification list. Called from StatusBar */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
index 74c97fd..b328ae8 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilder.java
@@ -226,8 +226,13 @@
mNotifSections.clear();
for (NotifSectioner sectioner : sectioners) {
- mNotifSections.add(new NotifSection(sectioner, mNotifSections.size()));
+ final NotifSection section = new NotifSection(sectioner, mNotifSections.size());
+ final NotifComparator sectionComparator = section.getComparator();
+ mNotifSections.add(section);
sectioner.setInvalidationListener(this::onNotifSectionInvalidated);
+ if (sectionComparator != null) {
+ sectionComparator.setInvalidationListener(this::onNotifComparatorInvalidated);
+ }
}
mNotifSections.add(new NotifSection(DEFAULT_SECTIONER, mNotifSections.size()));
@@ -426,14 +431,18 @@
}
Trace.endSection();
+ Trace.beginSection("ShadeListBuilder.logEndBuildList");
// Step 9: We're done!
mLogger.logEndBuildList(
mIterationCount,
mReadOnlyNotifList.size(),
countChildren(mReadOnlyNotifList));
if (mAlwaysLogList || mIterationCount % 10 == 0) {
+ Trace.beginSection("ShadeListBuilder.logFinalList");
mLogger.logFinalList(mNotifList);
+ Trace.endSection();
}
+ Trace.endSection();
mPipelineState.setState(STATE_IDLE);
mIterationCount++;
Trace.endSection();
@@ -996,16 +1005,20 @@
}
private void freeEmptyGroups() {
+ Trace.beginSection("ShadeListBuilder.freeEmptyGroups");
mGroups.values().removeIf(ge -> ge.getSummary() == null && ge.getChildren().isEmpty());
+ Trace.endSection();
}
private void logChanges() {
+ Trace.beginSection("ShadeListBuilder.logChanges");
for (NotificationEntry entry : mAllEntries) {
logAttachStateChanges(entry);
}
for (GroupEntry group : mGroups.values()) {
logAttachStateChanges(group);
}
+ Trace.endSection();
}
private void logAttachStateChanges(ListEntry entry) {
@@ -1083,16 +1096,23 @@
}
private void cleanupPluggables() {
+ Trace.beginSection("ShadeListBuilder.cleanupPluggables");
callOnCleanup(mNotifPreGroupFilters);
callOnCleanup(mNotifPromoters);
callOnCleanup(mNotifFinalizeFilters);
callOnCleanup(mNotifComparators);
for (int i = 0; i < mNotifSections.size(); i++) {
- mNotifSections.get(i).getSectioner().onCleanup();
+ final NotifSection notifSection = mNotifSections.get(i);
+ notifSection.getSectioner().onCleanup();
+ final NotifComparator comparator = notifSection.getComparator();
+ if (comparator != null) {
+ comparator.onCleanup();
+ }
}
callOnCleanup(List.of(getStabilityManager()));
+ Trace.endSection();
}
private void callOnCleanup(List<? extends Pluggable<?>> pluggables) {
@@ -1101,6 +1121,19 @@
}
}
+ @Nullable
+ private NotifComparator getSectionComparator(
+ @NonNull ListEntry o1, @NonNull ListEntry o2) {
+ final NotifSection section = o1.getSection();
+ if (section != o2.getSection()) {
+ throw new RuntimeException("Entry ordering should only be done within sections");
+ }
+ if (section != null) {
+ return section.getComparator();
+ }
+ return null;
+ }
+
private final Comparator<ListEntry> mTopLevelComparator = (o1, o2) -> {
int cmp = Integer.compare(
o1.getSectionIndex(),
@@ -1112,6 +1145,12 @@
cmp = Integer.compare(index1, index2);
if (cmp != 0) return cmp;
+ NotifComparator sectionComparator = getSectionComparator(o1, o2);
+ if (sectionComparator != null) {
+ cmp = sectionComparator.compare(o1, o2);
+ if (cmp != 0) return cmp;
+ }
+
for (int i = 0; i < mNotifComparators.size(); i++) {
cmp = mNotifComparators.get(i).compare(o1, o2);
if (cmp != 0) return cmp;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
index ba88ad7..a390e9f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinator.kt
@@ -51,23 +51,20 @@
val sectioner = object : NotifSectioner("People", BUCKET_PEOPLE) {
override fun isInSection(entry: ListEntry): Boolean =
isConversation(entry)
+
+ override fun getComparator() = object : NotifComparator("People") {
+ override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
+ val type1 = getPeopleType(entry1)
+ val type2 = getPeopleType(entry2)
+ return type2.compareTo(type1)
+ }
+ }
+
override fun getHeaderNodeController() =
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and peopleHeaderController
if (RankingCoordinator.SHOW_ALL_SECTIONS) peopleHeaderController else null
}
- val comparator = object : NotifComparator("People") {
- override fun compare(entry1: ListEntry, entry2: ListEntry): Int {
- assert(entry1.section === entry2.section)
- if (entry1.section?.sectioner !== sectioner) {
- return 0
- }
- val type1 = getPeopleType(entry1)
- val type2 = getPeopleType(entry2)
- return type2.compareTo(type1)
- }
- }
-
override fun attach(pipeline: NotifPipeline) {
pipeline.addPromoter(notificationPromoter)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
index 0311324..41b0706 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/HeadsUpCoordinator.kt
@@ -26,6 +26,7 @@
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifPromoter
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
@@ -456,6 +457,13 @@
// TODO: This check won't notice if a child of the group is going to HUN...
isGoingToShowHunNoRetract(entry)
+ override fun getComparator(): NotifComparator {
+ return object : NotifComparator("HeadsUp") {
+ override fun compare(o1: ListEntry, o2: ListEntry): Int =
+ mHeadsUpManager.compare(o1.representativeEntry, o2.representativeEntry)
+ }
+ }
+
override fun getHeaderNodeController(): NodeController? =
// TODO: remove SHOW_ALL_SECTIONS, this redundant method, and mIncomingHeaderController
if (RankingCoordinator.SHOW_ALL_SECTIONS) mIncomingHeaderController else null
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 850cb4b..757fb5a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -20,7 +20,6 @@
import com.android.systemui.statusbar.notification.NotifPipelineFlags
import com.android.systemui.statusbar.notification.collection.NotifPipeline
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
-import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import java.io.FileDescriptor
import java.io.PrintWriter
@@ -64,7 +63,6 @@
private val mCoordinators: MutableList<Coordinator> = ArrayList()
private val mOrderedSections: MutableList<NotifSectioner> = ArrayList()
- private val mOrderedComparators: MutableList<NotifComparator> = ArrayList()
/**
* Creates all the coordinators.
@@ -119,9 +117,6 @@
mOrderedSections.add(rankingCoordinator.alertingSectioner) // Alerting
mOrderedSections.add(rankingCoordinator.silentSectioner) // Silent
mOrderedSections.add(rankingCoordinator.minimizedSectioner) // Minimized
-
- // Manually add ordered comparators
- mOrderedComparators.add(conversationCoordinator.comparator)
}
/**
@@ -133,7 +128,6 @@
c.attach(pipeline)
}
pipeline.setSections(mOrderedSections)
- pipeline.setComparators(mOrderedComparators)
}
override fun dump(fd: FileDescriptor, pw: PrintWriter, args: Array<String>) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
index c6a8a69..1c96e8c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/StackCoordinator.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.render.NotifStats
import com.android.systemui.statusbar.notification.stack.BUCKET_SILENT
import com.android.systemui.statusbar.phone.NotificationIconAreaController
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -38,10 +39,11 @@
pipeline.addOnAfterRenderListListener(::onAfterRenderList)
}
- fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) {
- controller.setNotifStats(calculateNotifStats(entries))
- notificationIconAreaController.updateNotificationIcons(entries)
- }
+ fun onAfterRenderList(entries: List<ListEntry>, controller: NotifStackController) =
+ traceSection("StackCoordinator.onAfterRenderList") {
+ controller.setNotifStats(calculateNotifStats(entries))
+ notificationIconAreaController.updateNotificationIcons(entries)
+ }
private fun calculateNotifStats(entries: List<ListEntry>): NotifStats {
var hasNonClearableAlertingNotifs = false
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
index 8444287..263737e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/NotifSection.kt
@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.notification.collection.listbuilder
+import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifComparator
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.render.NodeController
import com.android.systemui.statusbar.notification.stack.PriorityBucket
@@ -29,5 +30,7 @@
val headerController: NodeController? = sectioner.headerNodeController
+ val comparator: NotifComparator? = sectioner.comparator
+
@PriorityBucket val bucket: Int = sectioner.bucket
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
index ef9ee11..8c52c53 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/listbuilder/pluggable/NotifSectioner.java
@@ -55,8 +55,22 @@
public abstract boolean isInSection(ListEntry entry);
/**
+ * Returns an optional {@link NotifComparator} to sort entries only in this section.
+ * {@link ListEntry} instances passed to this comparator are guaranteed to have this section,
+ * and this ordering will take precedence over any global comparators supplied to {@link
+ * com.android.systemui.statusbar.notification.collection.NotifPipeline#setComparators(List)}.
+ *
+ * NOTE: this method is only called once when the Sectioner is attached.
+ */
+ public @Nullable NotifComparator getComparator() {
+ return null;
+ }
+
+ /**
* Returns an optional {@link NodeSpec} for the section header. If {@code null}, no header will
* be used for the section.
+ *
+ * NOTE: this method is only called once when the Sectioner is attached.
*/
public @Nullable NodeController getHeaderNodeController() {
return null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
index 4de8e7a..b76169f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/render/RootNodeController.kt
@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.notification.collection.render
import android.view.View
+import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
import com.android.systemui.statusbar.notification.stack.NotificationListContainer
@@ -41,6 +42,7 @@
override fun addChildAt(child: NodeController, index: Int) {
listContainer.addContainerViewAt(child.view, index)
listContainer.onNotificationViewUpdateFinished()
+ (child.view as? ExpandableNotificationRow)?.isChangingPosition = false
}
override fun moveChildTo(child: NodeController, index: Int) {
@@ -50,6 +52,7 @@
override fun removeChild(child: NodeController, isTransfer: Boolean) {
if (isTransfer) {
listContainer.setChildTransferInProgress(true)
+ (child.view as? ExpandableNotificationRow)?.isChangingPosition = true
}
listContainer.removeContainerView(child.view)
if (isTransfer) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
index 46efef6..2436ffd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/ExpandableNotificationRowController.java
@@ -248,19 +248,25 @@
mView.addChildNotification((ExpandableNotificationRow) child.getView(), index);
mListContainer.notifyGroupChildAdded(childView);
+ childView.setChangingPosition(false);
}
@Override
public void moveChildTo(NodeController child, int index) {
ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
+ childView.setChangingPosition(true);
mView.removeChildNotification(childView);
mView.addChildNotification(childView, index);
+ childView.setChangingPosition(false);
}
@Override
public void removeChild(NodeController child, boolean isTransfer) {
ExpandableNotificationRow childView = (ExpandableNotificationRow) child.getView();
+ if (isTransfer) {
+ childView.setChangingPosition(true);
+ }
mView.removeChildNotification(childView);
if (!isTransfer) {
mListContainer.notifyGroupChildRemoved(childView, mView);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
index b02dc0c..54e26c3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSectionsManager.kt
@@ -39,6 +39,7 @@
import com.android.systemui.util.children
import com.android.systemui.util.foldToSparseArray
import com.android.systemui.util.takeUntil
+import com.android.systemui.util.traceSection
import javax.inject.Inject
/**
@@ -157,7 +158,9 @@
}
}
}
- private fun logShadeContents() = parent.children.forEachIndexed(::logShadeChild)
+ private fun logShadeContents() = traceSection("NotifSectionsManager.logShadeContents") {
+ parent.children.forEachIndexed(::logShadeChild)
+ }
private val isUsingMultipleSections: Boolean
get() = sectionsFeatureManager.getNumberOfBuckets() > 1
@@ -221,10 +224,10 @@
* Should be called whenever notifs are added, removed, or updated. Updates section boundary
* bookkeeping and adds/moves/removes section headers if appropriate.
*/
- fun updateSectionBoundaries(reason: String) {
+ fun updateSectionBoundaries(reason: String) = traceSection("NotifSectionsManager.update") {
notifPipelineFlags.checkLegacyPipelineEnabled()
if (!isUsingMultipleSections) {
- return
+ return@traceSection
}
logger.logStartSectionUpdate(reason)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index dd726157..25b8a65 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -43,7 +43,6 @@
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.Bundle;
-import android.os.UserHandle;
import android.provider.Settings;
import android.util.AttributeSet;
import android.util.IndentingPrintWriter;
@@ -696,8 +695,7 @@
&& mQsExpansionFraction != 1
&& !mScreenOffAnimationController.shouldHideNotificationsFooter()
&& !mIsRemoteInputActive;
- boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+ boolean showHistory = mController.isHistoryEnabled();
updateFooterView(showFooterView, showDismissView, showHistory);
}
@@ -5243,11 +5241,10 @@
R.layout.status_bar_no_notifications, this, false);
view.setText(R.string.empty_shade_text);
view.setOnClickListener(v -> {
- final boolean showHistory = Settings.Secure.getIntForUser(mContext.getContentResolver(),
- Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
- Intent intent = showHistory ? new Intent(
- Settings.ACTION_NOTIFICATION_HISTORY) : new Intent(
- Settings.ACTION_NOTIFICATION_SETTINGS);
+ final boolean showHistory = mController.isHistoryEnabled();
+ Intent intent = showHistory
+ ? new Intent(Settings.ACTION_NOTIFICATION_HISTORY)
+ : new Intent(Settings.ACTION_NOTIFICATION_SETTINGS);
mStatusBar.startActivity(intent, true, true, Intent.FLAG_ACTIVITY_SINGLE_TOP);
});
setEmptyShadeView(view);
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
index 334128a..a2929f0 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutController.java
@@ -35,6 +35,8 @@
import android.content.res.Resources;
import android.graphics.Point;
import android.graphics.PointF;
+import android.os.Trace;
+import android.os.UserHandle;
import android.provider.Settings;
import android.service.notification.NotificationListenerService;
import android.service.notification.StatusBarNotification;
@@ -180,7 +182,6 @@
private final NotificationLockscreenUserManager mLockscreenUserManager;
// TODO: StatusBar should be encapsulated behind a Controller
private final StatusBar mStatusBar;
- private final NotificationGroupManagerLegacy mLegacyGroupManager;
private final SectionHeaderController mSilentHeaderController;
private final LockscreenShadeTransitionController mLockscreenShadeTransitionController;
private final InteractionJankMonitor mJankMonitor;
@@ -191,6 +192,7 @@
private boolean mFadeNotificationsOnDismiss;
private NotificationSwipeHelper mSwipeHelper;
private boolean mShowEmptyShadeView;
+ @Nullable private Boolean mHistoryEnabled;
private int mBarState;
private HeadsUpAppearanceController mHeadsUpAppearanceController;
@@ -340,6 +342,8 @@
@Override
public void onUserChanged(int userId) {
mView.updateSensitiveness(false, mLockscreenUserManager.isAnyProfilePublicMode());
+ mHistoryEnabled = null;
+ updateFooter();
}
};
@@ -699,8 +703,6 @@
}
});
mNotifPipelineFlags = notifPipelineFlags;
- mLegacyGroupManager = mNotifPipelineFlags.isNewPipelineEnabled()
- ? null : legacyGroupManager;
mSilentHeaderController = silentHeaderController;
mNotifPipeline = notifPipeline;
mNotifCollection = notifCollection;
@@ -802,6 +804,7 @@
(key, newValue) -> {
switch (key) {
case Settings.Secure.NOTIFICATION_HISTORY_ENABLED:
+ mHistoryEnabled = null; // invalidate
updateFooter();
break;
case HIGH_PRIORITY:
@@ -1009,6 +1012,20 @@
return mNotifStats.getNumActiveNotifs();
}
+ public boolean isHistoryEnabled() {
+ Boolean historyEnabled = mHistoryEnabled;
+ if (historyEnabled == null) {
+ if (mView == null || mView.getContext() == null) {
+ Log.wtf(TAG, "isHistoryEnabled failed to initialize its value");
+ return false;
+ }
+ mHistoryEnabled = historyEnabled =
+ Settings.Secure.getIntForUser(mView.getContext().getContentResolver(),
+ Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, UserHandle.USER_CURRENT) == 1;
+ }
+ return historyEnabled;
+ }
+
public int getIntrinsicContentHeight() {
return mView.getIntrinsicContentHeight();
}
@@ -1199,6 +1216,7 @@
* are true.
*/
public void updateShowEmptyShadeView() {
+ Trace.beginSection("NSSLC.updateShowEmptyShadeView");
mShowEmptyShadeView = mBarState != KEYGUARD
&& (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
&& getVisibleNotificationCount() == 0;
@@ -1206,6 +1224,7 @@
mView.updateEmptyShadeView(
mShowEmptyShadeView,
mZenModeController.areNotificationsHiddenInShade());
+ Trace.endSection();
}
public boolean areNotificationsHiddenInShade() {
@@ -1323,11 +1342,15 @@
if (mNotifPipelineFlags.isNewPipelineEnabled()) {
return;
}
+ Trace.beginSection("NSSLC.updateSectionBoundaries");
mView.updateSectionBoundaries(reason);
+ Trace.endSection();
}
public void updateFooter() {
+ Trace.beginSection("NSSLC.updateFooter");
mView.updateFooter();
+ Trace.endSection();
}
public void onUpdateRowStates() {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
index 1a88533..b457ebf 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQSContainerController.kt
@@ -1,6 +1,8 @@
package com.android.systemui.statusbar.phone
import android.view.WindowInsets
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.plugins.qs.QS
import com.android.systemui.plugins.qs.QSContainerController
@@ -14,7 +16,8 @@
class NotificationsQSContainerController @Inject constructor(
view: NotificationsQuickSettingsContainer,
private val navigationModeController: NavigationModeController,
- private val overviewProxyService: OverviewProxyService
+ private val overviewProxyService: OverviewProxyService,
+ private val featureFlags: FeatureFlags
) : ViewController<NotificationsQuickSettingsContainer>(view), QSContainerController {
var qsExpanded = false
@@ -108,7 +111,11 @@
}
mView.setPadding(0, 0, 0, containerPadding)
mView.setNotificationsMarginBottom(notificationsMargin)
- mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+ if (featureFlags.isEnabled(Flags.NEW_FOOTER)) {
+ mView.setQSContainerPaddingBottom(notificationsMargin)
+ } else {
+ mView.setQSScrollPaddingBottom(qsScrollPaddingBottom)
+ }
}
private fun calculateBottomSpacing(): Pair<Int, Int> {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
index cecbcdb..95e3b70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -52,6 +52,7 @@
private Consumer<QS> mQSFragmentAttachedListener = qs -> {};
private QS mQs;
private View mQSScrollView;
+ private View mQSContainer;
public NotificationsQuickSettingsContainer(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -70,6 +71,7 @@
mQs = (QS) fragment;
mQSFragmentAttachedListener.accept(mQs);
mQSScrollView = mQs.getView().findViewById(R.id.expanded_qs_scroll_view);
+ mQSContainer = mQs.getView().findViewById(R.id.quick_settings_container);
}
@Override
@@ -89,6 +91,16 @@
mQSScrollView.getPaddingLeft(),
mQSScrollView.getPaddingTop(),
mQSScrollView.getPaddingRight(),
+ paddingBottom);
+ }
+ }
+
+ public void setQSContainerPaddingBottom(int paddingBottom) {
+ if (mQSContainer != null) {
+ mQSContainer.setPadding(
+ mQSContainer.getPaddingLeft(),
+ mQSContainer.getPaddingTop(),
+ mQSContainer.getPaddingRight(),
paddingBottom
);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
index 3084a95..784a5468 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/HeadsUpManager.java
@@ -350,11 +350,14 @@
* @return -1 if the first argument should be ranked higher than the second, 1 if the second
* one should be ranked higher and 0 if they are equal.
*/
- public int compare(@NonNull NotificationEntry a, @NonNull NotificationEntry b) {
+ public int compare(@Nullable NotificationEntry a, @Nullable NotificationEntry b) {
+ if (a == null || b == null) {
+ return Boolean.compare(a == null, b == null);
+ }
AlertEntry aEntry = getHeadsUpEntry(a.getKey());
AlertEntry bEntry = getHeadsUpEntry(b.getKey());
if (aEntry == null || bEntry == null) {
- return aEntry == null ? 1 : -1;
+ return Boolean.compare(aEntry == null, bEntry == null);
}
return aEntry.compareTo(bEntry);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt
deleted file mode 100644
index c6dbdb1..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsController.kt
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * 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.systemui.statusbar.policy
-
-/**
- * Interface for tracking packages with running foreground services and demoting foreground status
- */
-interface RunningFgsController : CallbackController<RunningFgsController.Callback> {
-
- /**
- * @return A list of [UserPackageTime]s which have running foreground service(s)
- */
- fun getPackagesWithFgs(): List<UserPackageTime>
-
- /**
- * Stops all foreground services running as a package
- * @param userId the userId the package is running under
- * @param packageName the packageName
- */
- fun stopFgs(userId: Int, packageName: String)
-
- /**
- * Returns when the list of packages with foreground services changes
- */
- interface Callback {
- /**
- * The thing that
- * @param packages the list of packages
- */
- fun onFgsPackagesChanged(packages: List<UserPackageTime>)
- }
-
- /**
- * A triplet <user, packageName, timeMillis> where each element is a package running
- * under a user that has had at least one foreground service running since timeMillis.
- * Time should be derived from [SystemClock.elapsedRealtime].
- */
- data class UserPackageTime(val userId: Int, val packageName: String, val startTimeMillis: Long)
-}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt
deleted file mode 100644
index 6d3345d..0000000
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RunningFgsControllerImpl.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.systemui.statusbar.policy
-
-import android.app.IActivityManager
-import android.app.IForegroundServiceObserver
-import android.os.IBinder
-import android.os.RemoteException
-import android.util.Log
-import androidx.annotation.GuardedBy
-import androidx.lifecycle.Lifecycle
-import androidx.lifecycle.LifecycleOwner
-import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.dagger.qualifiers.Background
-import com.android.systemui.statusbar.policy.RunningFgsController.Callback
-import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime
-import com.android.systemui.util.time.SystemClock
-import java.util.concurrent.Executor
-import javax.inject.Inject
-
-/**
- * Implementation for [RunningFgsController]
- */
-@SysUISingleton
-class RunningFgsControllerImpl @Inject constructor(
- @Background private val executor: Executor,
- private val systemClock: SystemClock,
- private val activityManager: IActivityManager
-) : RunningFgsController, IForegroundServiceObserver.Stub() {
-
- companion object {
- private val LOG_TAG = RunningFgsControllerImpl::class.java.simpleName
- }
-
- private val lock = Any()
-
- @GuardedBy("lock")
- var initialized = false
-
- @GuardedBy("lock")
- private val runningServiceTokens = mutableMapOf<UserPackageKey, StartTimeAndTokensValue>()
-
- @GuardedBy("lock")
- private val callbacks = mutableSetOf<Callback>()
-
- fun init() {
- synchronized(lock) {
- if (initialized) {
- return
- }
- try {
- activityManager.registerForegroundServiceObserver(this)
- } catch (e: RemoteException) {
- e.rethrowFromSystemServer()
- }
-
- initialized = true
- }
- }
-
- override fun addCallback(listener: Callback) {
- init()
- synchronized(lock) { callbacks.add(listener) }
- }
-
- override fun removeCallback(listener: Callback) {
- init()
- synchronized(lock) {
- if (!callbacks.remove(listener)) {
- Log.e(LOG_TAG, "Callback was not registered.", RuntimeException())
- }
- }
- }
-
- override fun observe(lifecycle: Lifecycle?, listener: Callback?): Callback {
- init()
- return super.observe(lifecycle, listener)
- }
-
- override fun observe(owner: LifecycleOwner?, listener: Callback?): Callback {
- init()
- return super.observe(owner, listener)
- }
-
- override fun getPackagesWithFgs(): List<UserPackageTime> {
- init()
- return synchronized(lock) { getPackagesWithFgsLocked() }
- }
-
- private fun getPackagesWithFgsLocked(): List<UserPackageTime> =
- runningServiceTokens.map {
- UserPackageTime(it.key.userId, it.key.packageName, it.value.fgsStartTime)
- }
-
- override fun stopFgs(userId: Int, packageName: String) {
- init()
- try {
- activityManager.stopAppForUser(packageName, userId)
- } catch (e: RemoteException) {
- e.rethrowFromSystemServer()
- }
- }
-
- private data class UserPackageKey(
- val userId: Int,
- val packageName: String
- )
-
- private class StartTimeAndTokensValue(systemClock: SystemClock) {
- val fgsStartTime = systemClock.elapsedRealtime()
- var tokens = mutableSetOf<IBinder>()
- fun addToken(token: IBinder): Boolean {
- return tokens.add(token)
- }
-
- fun removeToken(token: IBinder): Boolean {
- return tokens.remove(token)
- }
-
- val isEmpty: Boolean
- get() = tokens.isEmpty()
- }
-
- override fun onForegroundStateChanged(
- token: IBinder,
- packageName: String,
- userId: Int,
- isForeground: Boolean
- ) {
- val result = synchronized(lock) {
- val userPackageKey = UserPackageKey(userId, packageName)
- if (isForeground) {
- var addedNew = false
- runningServiceTokens.getOrPut(userPackageKey) {
- addedNew = true
- StartTimeAndTokensValue(systemClock)
- }.addToken(token)
- if (!addedNew) {
- return
- }
- } else {
- val startTimeAndTokensValue = runningServiceTokens[userPackageKey]
- if (startTimeAndTokensValue?.removeToken(token) == false) {
- Log.e(LOG_TAG,
- "Stopped foreground service was not known to be running.")
- return
- }
- if (!startTimeAndTokensValue!!.isEmpty) {
- return
- }
- runningServiceTokens.remove(userPackageKey)
- }
- getPackagesWithFgsLocked().toList()
- }
-
- callbacks.forEach { executor.execute { it.onFgsPackagesChanged(result) } }
- }
-}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
index b951345..61e78f5 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/animation/DialogLaunchAnimatorTest.kt
@@ -43,7 +43,7 @@
@Before
fun setUp() {
dialogLaunchAnimator = DialogLaunchAnimator(
- dreamManager, launchAnimator, isForTesting = true)
+ dreamManager, launchAnimator, forceDisableSynchronization = true)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
index d5bd67a..8adb55b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayServiceTest.java
@@ -36,7 +36,9 @@
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.settingslib.dream.DreamBackend;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.complication.Complication;
import com.android.systemui.dreams.dagger.DreamOverlayComponent;
import com.android.systemui.dreams.touch.DreamOverlayTouchMonitor;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -50,6 +52,10 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class DreamOverlayServiceTest extends SysuiTestCase {
@@ -94,11 +100,13 @@
@Mock
DreamOverlayStateController mStateController;
+ @Mock
+ DreamBackend mDreamBackend;
DreamOverlayService mService;
@Before
- public void setup() throws Exception {
+ public void setup() {
MockitoAnnotations.initMocks(this);
mContext.addMockSystemService(WindowManager.class, mWindowManager);
@@ -110,6 +118,8 @@
.thenReturn(mLifecycleRegistry);
when(mDreamOverlayComponent.getDreamOverlayTouchMonitor())
.thenReturn(mDreamOverlayTouchMonitor);
+ when(mDreamOverlayComponent.getDreamBackend())
+ .thenReturn(mDreamBackend);
when(mDreamOverlayComponentFactory
.create(any(), any()))
.thenReturn(mDreamOverlayComponent);
@@ -120,28 +130,34 @@
mDreamOverlayComponentFactory,
mStateController,
mKeyguardUpdateMonitor);
+ }
+
+ @Test
+ public void testOverlayContainerViewAddedToWindow() throws Exception {
final IBinder proxy = mService.onBind(new Intent());
final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
// Inform the overlay service of dream starting.
overlay.startDream(mWindowParams, mDreamOverlayCallback);
mMainExecutor.runAllReady();
- }
- @Test
- public void testOverlayContainerViewAddedToWindow() {
verify(mWindowManager).addView(any(), any());
}
@Test
- public void testDreamOverlayContainerViewControllerInitialized() {
+ public void testDreamOverlayContainerViewControllerInitialized() throws Exception {
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ // Inform the overlay service of dream starting.
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
verify(mDreamOverlayContainerViewController).init();
}
@Test
public void testShouldShowComplicationsTrueByDefault() {
- assertThat(mService.shouldShowComplications()).isTrue();
-
mService.onBind(new Intent());
assertThat(mService.shouldShowComplications()).isTrue();
@@ -155,4 +171,24 @@
assertThat(mService.shouldShowComplications()).isFalse();
}
+
+ @Test
+ public void testSetAvailableComplicationTypes() throws Exception {
+ final Set<Integer> enabledComplications = new HashSet<>(
+ Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME,
+ DreamBackend.COMPLICATION_TYPE_DATE,
+ DreamBackend.COMPLICATION_TYPE_WEATHER));
+ when(mDreamBackend.getEnabledComplications()).thenReturn(enabledComplications);
+
+ final IBinder proxy = mService.onBind(new Intent());
+ final IDreamOverlay overlay = IDreamOverlay.Stub.asInterface(proxy);
+
+ overlay.startDream(mWindowParams, mDreamOverlayCallback);
+ mMainExecutor.runAllReady();
+
+ final int expectedTypes =
+ Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE
+ | Complication.COMPLICATION_TYPE_WEATHER;
+ verify(mStateController).setAvailableComplicationTypes(expectedTypes);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
index f1978b2..365c529 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationUtilsTest.java
@@ -22,6 +22,7 @@
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_TIME;
import static com.android.systemui.dreams.complication.Complication.COMPLICATION_TYPE_WEATHER;
import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationType;
+import static com.android.systemui.dreams.complication.ComplicationUtils.convertComplicationTypes;
import static com.google.common.truth.Truth.assertThat;
@@ -36,6 +37,11 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
@SmallTest
@RunWith(AndroidTestingRunner.class)
public class ComplicationUtilsTest extends SysuiTestCase {
@@ -52,4 +58,37 @@
assertThat(convertComplicationType(DreamBackend.COMPLICATION_TYPE_CAST_INFO))
.isEqualTo(COMPLICATION_TYPE_CAST_INFO);
}
+
+ @Test
+ public void testConvertComplicationTypesEmpty() {
+ final Set<Integer> input = new HashSet<>();
+ final int expected = Complication.COMPLICATION_TYPE_NONE;
+
+ assertThat(convertComplicationTypes(input)).isEqualTo(expected);
+ }
+
+ @Test
+ public void testConvertComplicationTypesSingleValue() {
+ final Set<Integer> input = new HashSet<>(
+ Collections.singleton(DreamBackend.COMPLICATION_TYPE_WEATHER));
+ final int expected = Complication.COMPLICATION_TYPE_WEATHER;
+
+ assertThat(convertComplicationTypes(input)).isEqualTo(expected);
+ }
+
+ @Test
+ public void testConvertComplicationTypesSingleValueMultipleValues() {
+ final Set<Integer> input = new HashSet<>(
+ Arrays.asList(DreamBackend.COMPLICATION_TYPE_TIME,
+ DreamBackend.COMPLICATION_TYPE_DATE,
+ DreamBackend.COMPLICATION_TYPE_WEATHER,
+ DreamBackend.COMPLICATION_TYPE_AIR_QUALITY,
+ DreamBackend.COMPLICATION_TYPE_CAST_INFO));
+ final int expected =
+ Complication.COMPLICATION_TYPE_TIME | Complication.COMPLICATION_TYPE_DATE
+ | Complication.COMPLICATION_TYPE_WEATHER | COMPLICATION_TYPE_AIR_QUALITY
+ | COMPLICATION_TYPE_CAST_INFO;
+
+ assertThat(convertComplicationTypes(input)).isEqualTo(expected);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
index d94e2ee..210cb82 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardViewMediatorTest.java
@@ -46,6 +46,7 @@
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.mediator.ScreenOnCoordinator;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.dreams.DreamOverlayStateController;
@@ -100,6 +101,7 @@
private @Mock ScreenOnCoordinator mScreenOnCoordinator;
private @Mock Lazy<NotificationShadeWindowController> mNotificationShadeWindowControllerLazy;
private @Mock DreamOverlayStateController mDreamOverlayStateController;
+ private @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
private DeviceConfigProxy mDeviceConfig = new DeviceConfigProxyFake();
private FakeExecutor mUiBgExecutor = new FakeExecutor(new FakeSystemClock());
@@ -205,7 +207,8 @@
mScreenOnCoordinator,
mInteractionJankMonitor,
mDreamOverlayStateController,
- mNotificationShadeWindowControllerLazy);
+ mNotificationShadeWindowControllerLazy,
+ () -> mActivityLaunchAnimator);
mViewMediator.start();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
index 140a395..609291a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/MediaControlPanelTest.kt
@@ -125,8 +125,9 @@
private lateinit var settings: View
private lateinit var settingsText: TextView
private lateinit var cancel: View
+ private lateinit var cancelText: TextView
private lateinit var dismiss: FrameLayout
- private lateinit var dismissLabel: View
+ private lateinit var dismissText: TextView
private lateinit var session: MediaSession
private val device = MediaDeviceData(true, null, DEVICE_NAME)
@@ -163,8 +164,9 @@
settings = View(context)
settingsText = TextView(context)
cancel = View(context)
+ cancelText = TextView(context)
dismiss = FrameLayout(context)
- dismissLabel = View(context)
+ dismissText = TextView(context)
initPlayerHolderMocks()
initSessionHolderMocks()
@@ -244,13 +246,15 @@
whenever(holder.settings).thenReturn(settings)
whenever(holder.settingsText).thenReturn(settingsText)
whenever(holder.cancel).thenReturn(cancel)
+ whenever(holder.cancelText).thenReturn(cancelText)
whenever(holder.dismiss).thenReturn(dismiss)
- whenever(holder.dismissLabel).thenReturn(dismissLabel)
+ whenever(holder.dismissText).thenReturn(dismissText)
}
/** Mock view holder for session player */
private fun initSessionHolderMocks() {
whenever(sessionHolder.player).thenReturn(view)
+ whenever(sessionHolder.albumView).thenReturn(albumView)
whenever(sessionHolder.appIcon).thenReturn(appIcon)
whenever(sessionHolder.titleText).thenReturn(titleText)
whenever(sessionHolder.artistText).thenReturn(artistText)
@@ -284,8 +288,9 @@
whenever(sessionHolder.settings).thenReturn(settings)
whenever(sessionHolder.settingsText).thenReturn(settingsText)
whenever(sessionHolder.cancel).thenReturn(cancel)
+ whenever(sessionHolder.cancelText).thenReturn(cancelText)
whenever(sessionHolder.dismiss).thenReturn(dismiss)
- whenever(sessionHolder.dismissLabel).thenReturn(dismissLabel)
+ whenever(sessionHolder.dismissText).thenReturn(dismissText)
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
index 81ae209..c3d8d24 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/MediaTttCommandLineHelperTest.kt
@@ -16,27 +16,35 @@
package com.android.systemui.media.taptotransfer
-import android.content.ComponentName
+import android.app.StatusBarManager
+import android.content.Context
+import android.media.MediaRoute2Info
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.media.taptotransfer.receiver.ChipStateReceiver
import com.android.systemui.media.taptotransfer.receiver.MediaTttChipControllerReceiver
-import com.android.systemui.media.taptotransfer.sender.MediaTttSenderService
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
+import com.android.systemui.media.taptotransfer.sender.AlmostCloseToEndCast
+import com.android.systemui.media.taptotransfer.sender.AlmostCloseToStartCast
+import com.android.systemui.media.taptotransfer.sender.TransferFailed
+import com.android.systemui.media.taptotransfer.sender.TransferToReceiverTriggered
+import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceSucceeded
+import com.android.systemui.media.taptotransfer.sender.TransferToThisDeviceTriggered
+import com.android.systemui.media.taptotransfer.sender.TransferToReceiverSucceeded
import com.android.systemui.statusbar.commandline.Command
import com.android.systemui.statusbar.commandline.CommandRegistry
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.capture
+import com.android.systemui.util.mockito.eq
+import com.android.systemui.util.mockito.nullable
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.mockito.Mock
-import org.mockito.Mockito.anyString
import org.mockito.Mockito.verify
-import org.mockito.Mockito.`when` as whenever
import org.mockito.MockitoAnnotations
import java.io.PrintWriter
import java.io.StringWriter
@@ -53,24 +61,19 @@
private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
@Mock
- private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
+ private lateinit var statusBarManager: StatusBarManager
@Mock
- private lateinit var mediaSenderService: IDeviceSenderService.Stub
- private lateinit var mediaSenderServiceComponentName: ComponentName
+ private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
-
- mediaSenderServiceComponentName = ComponentName(context, MediaTttSenderService::class.java)
- context.addMockService(mediaSenderServiceComponentName, mediaSenderService)
- whenever(mediaSenderService.queryLocalInterface(anyString())).thenReturn(mediaSenderService)
- whenever(mediaSenderService.asBinder()).thenReturn(mediaSenderService)
-
+ context.addMockSystemService(Context.STATUS_BAR_SERVICE, statusBarManager)
mediaTttCommandLineHelper =
MediaTttCommandLineHelper(
commandRegistry,
context,
+ FakeExecutor(FakeSystemClock()),
mediaTttChipControllerReceiver,
)
}
@@ -101,85 +104,113 @@
}
@Test
- fun sender_moveCloserToStartCast_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getMoveCloserToStartCastCommand())
+ fun sender_almostCloseToStartCast_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(AlmostCloseToStartCast::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
-
- val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
- verify(mediaSenderService).closeToReceiverToStartCast(any(), capture(deviceInfoCaptor))
- assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST),
+ capture(routeInfoCaptor),
+ nullable(),
+ nullable())
+ assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
- fun sender_moveCloserToEndCast_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getMoveCloserToEndCastCommand())
+ fun sender_almostCloseToEndCast_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(AlmostCloseToEndCast::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
-
- val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
- verify(mediaSenderService).closeToReceiverToEndCast(any(), capture(deviceInfoCaptor))
- assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST),
+ capture(routeInfoCaptor),
+ nullable(),
+ nullable())
+ assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_transferToReceiverTriggered_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferToReceiverTriggeredCommand())
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(TransferToReceiverTriggered::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
-
- val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
- verify(mediaSenderService).transferToReceiverTriggered(any(), capture(deviceInfoCaptor))
- assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED),
+ capture(routeInfoCaptor),
+ nullable(),
+ nullable())
+ assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_transferToThisDeviceTriggered_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferToThisDeviceTriggeredCommand())
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(TransferToThisDeviceTriggered::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
- verify(mediaSenderService).transferToThisDeviceTriggered(any(), any())
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED),
+ any(),
+ nullable(),
+ nullable())
}
@Test
fun sender_transferToReceiverSucceeded_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferToReceiverSucceededCommand())
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(TransferToReceiverSucceeded::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
-
- val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
- verify(mediaSenderService)
- .transferToReceiverSucceeded(any(), capture(deviceInfoCaptor), any())
- assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED),
+ capture(routeInfoCaptor),
+ nullable(),
+ nullable())
+ assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_transferToThisDeviceSucceeded_chipDisplayWithCorrectState() {
- commandRegistry.onShellCommand(pw, getTransferToThisDeviceSucceededCommand())
+ commandRegistry.onShellCommand(
+ pw, getSenderCommand(TransferToThisDeviceSucceeded::class.simpleName!!)
+ )
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
-
- val deviceInfoCaptor = argumentCaptor<DeviceInfo>()
- verify(mediaSenderService)
- .transferToThisDeviceSucceeded(any(), capture(deviceInfoCaptor), any())
- assertThat(deviceInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
+ val routeInfoCaptor = argumentCaptor<MediaRoute2Info>()
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED),
+ capture(routeInfoCaptor),
+ nullable(),
+ nullable())
+ assertThat(routeInfoCaptor.value!!.name).isEqualTo(DEVICE_NAME)
}
@Test
fun sender_transferFailed_serviceCallbackCalled() {
- commandRegistry.onShellCommand(pw, getTransferFailedCommand())
+ commandRegistry.onShellCommand(pw, getSenderCommand(TransferFailed::class.simpleName!!))
- assertThat(context.isBound(mediaSenderServiceComponentName)).isTrue()
- verify(mediaSenderService).transferFailed(any(), any())
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED),
+ any(),
+ nullable(),
+ nullable())
}
@Test
- fun sender_noLongerCloseToReceiver_serviceCallbackCalledAndServiceUnbound() {
- commandRegistry.onShellCommand(pw, getNoLongerCloseToReceiverCommand())
+ fun sender_farFromReceiver_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getSenderCommand(FAR_FROM_RECEIVER_STATE))
- // Once we're no longer close to the receiver, we should unbind the service.
- assertThat(context.isBound(mediaSenderServiceComponentName)).isFalse()
- verify(mediaSenderService).noLongerCloseToReceiver(any(), any())
+ verify(statusBarManager).updateMediaTapToTransferSenderDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER),
+ any(),
+ nullable(),
+ nullable())
}
@Test
@@ -196,61 +227,8 @@
verify(mediaTttChipControllerReceiver).removeChip()
}
- private fun getMoveCloserToStartCastCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
- )
-
- private fun getMoveCloserToEndCastCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- MOVE_CLOSER_TO_END_CAST_COMMAND_NAME
- )
-
- private fun getTransferToReceiverTriggeredCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- TRANSFER_TO_RECEIVER_TRIGGERED_COMMAND_NAME
- )
-
- private fun getTransferToThisDeviceTriggeredCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- TRANSFER_TO_THIS_DEVICE_TRIGGERED_COMMAND_NAME
- )
-
- private fun getTransferToReceiverSucceededCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- TRANSFER_TO_RECEIVER_SUCCEEDED_COMMAND_NAME
- )
-
- private fun getTransferToThisDeviceSucceededCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- TRANSFER_TO_THIS_DEVICE_SUCCEEDED_COMMAND_NAME
- )
-
- private fun getTransferFailedCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- TRANSFER_FAILED_COMMAND_NAME
- )
-
- private fun getNoLongerCloseToReceiverCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- NO_LONGER_CLOSE_TO_RECEIVER_COMMAND_NAME
- )
+ private fun getSenderCommand(displayState: String): Array<String> =
+ arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState)
class EmptyCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
index 6b4eebe..c74ac64 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttChipControllerSenderTest.kt
@@ -16,17 +16,20 @@
package com.android.systemui.media.taptotransfer.sender
+import android.app.StatusBarManager
import android.graphics.drawable.Drawable
import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
import android.view.View
import android.view.WindowManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.test.filters.SmallTest
+import com.android.internal.statusbar.IUndoMediaTransferCallback
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -34,6 +37,7 @@
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
+import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@@ -46,17 +50,150 @@
@Mock
private lateinit var windowManager: WindowManager
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+ private lateinit var commandQueueCallback: CommandQueue.Callbacks
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
appIconDrawable = Icon.createWithResource(context, R.drawable.ic_cake).loadDrawable(context)
- controllerSender = MediaTttChipControllerSender(context, windowManager)
+ controllerSender = MediaTttChipControllerSender(commandQueue, context, windowManager)
+
+ val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ commandQueueCallback = callbackCaptor.value!!
}
@Test
- fun moveCloserToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
- val state = moveCloserToStartCast()
+ fun commandQueueCallback_almostCloseToStartCast_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(almostCloseToStartCast().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_almostCloseToEndCast_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_END_CAST,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(almostCloseToEndCast().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToReceiverTriggered_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_TRIGGERED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToReceiverTriggered().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToThisDeviceTriggered_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_TRIGGERED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToThisDeviceTriggered().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToReceiverSucceeded_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_SUCCEEDED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToReceiverSucceeded().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToThisDeviceSucceeded_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_SUCCEEDED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferToThisDeviceSucceeded().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToReceiverFailed_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_RECEIVER_FAILED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferFailed().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_transferToThisDeviceFailed_triggersCorrectChip() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_TRANSFER_TO_THIS_DEVICE_FAILED,
+ routeInfo,
+ null
+ )
+
+ assertThat(getChipView().getChipText())
+ .isEqualTo(transferFailed().getChipTextString(context))
+ }
+
+ @Test
+ fun commandQueueCallback_farFromReceiver_noChipShown() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+
+ @Test
+ fun commandQueueCallback_almostCloseThenFarFromReceiver_chipShownThenHidden() {
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_ALMOST_CLOSE_TO_START_CAST,
+ routeInfo,
+ null
+ )
+
+ commandQueueCallback.updateMediaTapToTransferSenderDisplay(
+ StatusBarManager.MEDIA_TRANSFER_SENDER_STATE_FAR_FROM_RECEIVER,
+ routeInfo,
+ null
+ )
+
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ verify(windowManager).addView(viewCaptor.capture(), any())
+ verify(windowManager).removeView(viewCaptor.value)
+ }
+
+ @Test
+ fun almostCloseToStartCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+ val state = almostCloseToStartCast()
controllerSender.displayChip(state)
val chipView = getChipView()
@@ -69,8 +206,8 @@
}
@Test
- fun moveCloserToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
- val state = moveCloserToEndCast()
+ fun almostCloseToEndCast_appIcon_deviceName_noLoadingIcon_noUndo_noFailureIcon() {
+ val state = almostCloseToEndCast()
controllerSender.displayChip(state)
val chipView = getChipView()
@@ -133,7 +270,7 @@
@Test
fun transferToReceiverSucceeded_withUndoRunnable_undoWithClick() {
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
@@ -146,7 +283,7 @@
@Test
fun transferToReceiverSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
var undoCallbackCalled = false
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
@@ -160,7 +297,7 @@
@Test
fun transferToReceiverSucceeded_undoButtonClick_switchesToTransferToThisDeviceTriggered() {
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
controllerSender.displayChip(transferToReceiverSucceeded(undoCallback))
@@ -194,7 +331,7 @@
@Test
fun transferToThisDeviceSucceeded_withUndoRunnable_undoWithClick() {
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
@@ -207,7 +344,7 @@
@Test
fun transferToThisDeviceSucceeded_withUndoRunnable_undoButtonClickRunsRunnable() {
var undoCallbackCalled = false
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {
undoCallbackCalled = true
}
@@ -221,7 +358,7 @@
@Test
fun transferToThisDeviceSucceeded_undoButtonClick_switchesToTransferToReceiverTriggered() {
- val undoCallback = object : IUndoTransferCallback.Stub() {
+ val undoCallback = object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
controllerSender.displayChip(transferToThisDeviceSucceeded(undoCallback))
@@ -247,8 +384,8 @@
}
@Test
- fun changeFromCloserToStartToTransferTriggered_loadingIconAppears() {
- controllerSender.displayChip(moveCloserToStartCast())
+ fun changeFromAlmostCloseToStartToTransferTriggered_loadingIconAppears() {
+ controllerSender.displayChip(almostCloseToStartCast())
controllerSender.displayChip(transferToReceiverTriggered())
assertThat(getChipView().getLoadingIconVisibility()).isEqualTo(View.VISIBLE)
@@ -267,7 +404,7 @@
controllerSender.displayChip(transferToReceiverTriggered())
controllerSender.displayChip(
transferToReceiverSucceeded(
- object : IUndoTransferCallback.Stub() {
+ object : IUndoMediaTransferCallback.Stub() {
override fun onUndoTriggered() {}
}
)
@@ -277,9 +414,9 @@
}
@Test
- fun changeFromTransferSucceededToMoveCloserToStart_undoButtonDisappears() {
+ fun changeFromTransferSucceededToAlmostCloseToStart_undoButtonDisappears() {
controllerSender.displayChip(transferToReceiverSucceeded())
- controllerSender.displayChip(moveCloserToStartCast())
+ controllerSender.displayChip(almostCloseToStartCast())
assertThat(getChipView().getUndoButton().visibility).isEqualTo(View.GONE)
}
@@ -311,12 +448,12 @@
}
/** Helper method providing default parameters to not clutter up the tests. */
- private fun moveCloserToStartCast() =
- MoveCloserToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+ private fun almostCloseToStartCast() =
+ AlmostCloseToStartCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
- private fun moveCloserToEndCast() =
- MoveCloserToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
+ private fun almostCloseToEndCast() =
+ AlmostCloseToEndCast(appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME)
/** Helper method providing default parameters to not clutter up the tests. */
private fun transferToReceiverTriggered() =
@@ -327,13 +464,13 @@
TransferToThisDeviceTriggered(appIconDrawable, APP_ICON_CONTENT_DESC)
/** Helper method providing default parameters to not clutter up the tests. */
- private fun transferToReceiverSucceeded(undoCallback: IUndoTransferCallback? = null) =
+ private fun transferToReceiverSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToReceiverSucceeded(
appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
)
/** Helper method providing default parameters to not clutter up the tests. */
- private fun transferToThisDeviceSucceeded(undoCallback: IUndoTransferCallback? = null) =
+ private fun transferToThisDeviceSucceeded(undoCallback: IUndoMediaTransferCallback? = null) =
TransferToThisDeviceSucceeded(
appIconDrawable, APP_ICON_CONTENT_DESC, DEVICE_NAME, undoCallback
)
@@ -344,3 +481,7 @@
private const val DEVICE_NAME = "My Tablet"
private const val APP_ICON_CONTENT_DESC = "Content description"
+
+private val routeInfo = MediaRoute2Info.Builder("id", "Test Name")
+ .addFeature("feature")
+ .build()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
deleted file mode 100644
index 64542cb..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/sender/MediaTttSenderServiceTest.kt
+++ /dev/null
@@ -1,126 +0,0 @@
-package com.android.systemui.media.taptotransfer.sender
-
-import android.media.MediaRoute2Info
-import androidx.test.filters.SmallTest
-import com.android.systemui.SysuiTestCase
-import com.android.systemui.shared.mediattt.DeviceInfo
-import com.android.systemui.shared.mediattt.IDeviceSenderService
-import com.android.systemui.shared.mediattt.IUndoTransferCallback
-import com.android.systemui.util.mockito.any
-import com.android.systemui.util.mockito.argumentCaptor
-import com.android.systemui.util.mockito.capture
-import com.google.common.truth.Truth.assertThat
-import org.junit.Before
-import org.junit.Ignore
-import org.junit.Test
-import org.mockito.Mock
-import org.mockito.Mockito.verify
-import org.mockito.MockitoAnnotations
-
-@SmallTest
-@Ignore("b/216286227")
-class MediaTttSenderServiceTest : SysuiTestCase() {
-
- private lateinit var service: IDeviceSenderService
-
- @Mock
- private lateinit var controller: MediaTttChipControllerSender
-
- private val mediaInfo = MediaRoute2Info.Builder("id", "Test Name")
- .addFeature("feature")
- .build()
-
- @Before
- fun setUp() {
- MockitoAnnotations.initMocks(this)
- val mediaTttSenderService = MediaTttSenderService(context, controller)
- service = IDeviceSenderService.Stub.asInterface(mediaTttSenderService.onBind(null))
- }
-
- @Test
- fun closeToReceiverToStartCast_controllerTriggeredWithCorrectState() {
- val name = "Fake name"
- service.closeToReceiverToStartCast(mediaInfo, DeviceInfo(name))
-
- val chipStateCaptor = argumentCaptor<MoveCloserToStartCast>()
- verify(controller).displayChip(capture(chipStateCaptor))
-
- val chipState = chipStateCaptor.value!!
- assertThat(chipState.getChipTextString(context)).contains(name)
- }
-
- @Test
- fun closeToReceiverToEndCast_controllerTriggeredWithCorrectState() {
- val name = "Fake name"
- service.closeToReceiverToEndCast(mediaInfo, DeviceInfo(name))
-
- val chipStateCaptor = argumentCaptor<MoveCloserToEndCast>()
- verify(controller).displayChip(capture(chipStateCaptor))
-
- val chipState = chipStateCaptor.value!!
- assertThat(chipState.getChipTextString(context)).contains(name)
- }
-
- @Test
- fun transferToThisDeviceTriggered_controllerTriggeredWithCorrectState() {
- service.transferToThisDeviceTriggered(mediaInfo, DeviceInfo("Fake name"))
-
- verify(controller).displayChip(any<TransferToThisDeviceTriggered>())
- }
-
- @Test
- fun transferToReceiverTriggered_controllerTriggeredWithCorrectState() {
- val name = "Fake name"
- service.transferToReceiverTriggered(mediaInfo, DeviceInfo(name))
-
- val chipStateCaptor = argumentCaptor<TransferToReceiverTriggered>()
- verify(controller).displayChip(capture(chipStateCaptor))
-
- val chipState = chipStateCaptor.value!!
- assertThat(chipState.getChipTextString(context)).contains(name)
- }
-
- @Test
- fun transferToReceiverSucceeded_controllerTriggeredWithCorrectState() {
- val name = "Fake name"
- val undoCallback = object : IUndoTransferCallback.Stub() {
- override fun onUndoTriggered() {}
- }
- service.transferToReceiverSucceeded(mediaInfo, DeviceInfo(name), undoCallback)
-
- val chipStateCaptor = argumentCaptor<TransferToReceiverSucceeded>()
- verify(controller).displayChip(capture(chipStateCaptor))
-
- val chipState = chipStateCaptor.value!!
- assertThat(chipState.getChipTextString(context)).contains(name)
- assertThat(chipState.undoCallback).isEqualTo(undoCallback)
- }
-
- @Test
- fun transferToThisDeviceSucceeded_controllerTriggeredWithCorrectState() {
- val undoCallback = object : IUndoTransferCallback.Stub() {
- override fun onUndoTriggered() {}
- }
- service.transferToThisDeviceSucceeded(mediaInfo, DeviceInfo("name"), undoCallback)
-
- val chipStateCaptor = argumentCaptor<TransferToThisDeviceSucceeded>()
- verify(controller).displayChip(capture(chipStateCaptor))
-
- val chipState = chipStateCaptor.value!!
- assertThat(chipState.undoCallback).isEqualTo(undoCallback)
- }
-
- @Test
- fun transferFailed_controllerTriggeredWithTransferFailedState() {
- service.transferFailed(mediaInfo, DeviceInfo("Fake name"))
-
- verify(controller).displayChip(any<TransferFailed>())
- }
-
- @Test
- fun noLongerCloseToReceiver_controllerRemoveChipTriggered() {
- service.noLongerCloseToReceiver(mediaInfo, DeviceInfo("Fake name"))
-
- verify(controller).removeChip()
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
index 354bb51..f5fa0d0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/FooterActionsControllerTest.kt
@@ -15,9 +15,10 @@
import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.classifier.FalsingManagerFake
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.globalactions.GlobalActionsDialogLite
import com.android.systemui.plugins.ActivityStarter
-import com.android.systemui.qs.FooterActionsController.ExpansionState
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.MultiUserSwitchController
import com.android.systemui.statusbar.policy.DeviceProvisionedController
@@ -54,12 +55,16 @@
@Mock
private lateinit var userInfoController: UserInfoController
@Mock
+ private lateinit var multiUserSwitchControllerFactory: MultiUserSwitchController.Factory
+ @Mock
private lateinit var multiUserSwitchController: MultiUserSwitchController
@Mock
private lateinit var globalActionsDialog: GlobalActionsDialogLite
@Mock
private lateinit var uiEventLogger: UiEventLogger
@Mock
+ private lateinit var featureFlags: FeatureFlags
+
private lateinit var controller: FooterActionsController
private val metricsLogger: MetricsLogger = FakeMetricsLogger()
@@ -76,15 +81,18 @@
injectLeakCheckedDependencies(*LeakCheckedTest.ALL_SUPPORTED_CLASSES)
val fakeTunerService = Dependency.get(TunerService::class.java) as FakeTunerService
+ whenever(multiUserSwitchControllerFactory.create(any()))
+ .thenReturn(multiUserSwitchController)
+ whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(false)
+
view = LayoutInflater.from(context)
.inflate(R.layout.footer_actions, null) as FooterActionsView
- controller = FooterActionsController(view, activityStarter,
- userManager, userTracker, userInfoController, multiUserSwitchController,
+ controller = FooterActionsController(view, multiUserSwitchControllerFactory,
+ activityStarter, userManager, userTracker, userInfoController,
deviceProvisionedController, falsingManager, metricsLogger, fakeTunerService,
- globalActionsDialog, uiEventLogger, showPMLiteButton = true,
- buttonsVisibleState = ExpansionState.EXPANDED, fakeSettings,
- Handler(testableLooper.looper))
+ globalActionsDialog, uiEventLogger, showPMLiteButton = true, fakeSettings,
+ Handler(testableLooper.looper), featureFlags)
controller.init()
ViewUtils.attachView(view)
// View looper is the testable looper associated with the test
@@ -98,7 +106,7 @@
@Test
fun testLogPowerMenuClick() {
- controller.expanded = true
+ controller.visible = true
falsingManager.setFalseTap(false)
view.findViewById<View>(R.id.pm_lite).performClick()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
index f43e68f..26aa37d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFooterViewControllerTest.java
@@ -61,12 +61,8 @@
@Mock
private ClipboardManager mClipboardManager;
@Mock
- private QuickQSPanelController mQuickQSPanelController;
- @Mock
private TextView mBuildText;
@Mock
- private FooterActionsController mFooterActionsController;
- @Mock
private FalsingManager mFalsingManager;
@Mock
private ActivityStarter mActivityStarter;
@@ -93,8 +89,7 @@
when(mView.findViewById(android.R.id.edit)).thenReturn(mEditButton);
mController = new QSFooterViewController(mView, mUserTracker, mFalsingManager,
- mActivityStarter, mQSPanelController, mQuickQSPanelController,
- mFooterActionsController);
+ mActivityStarter, mQSPanelController);
mController.init();
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
index 59948d3..09c6d9e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -68,8 +68,6 @@
private lateinit var tileView: QSTileView
@Mock
private lateinit var quickQsBrightnessController: QuickQSBrightnessController
- @Mock
- private lateinit var footerActionsController: FooterActionsController
@Captor
private lateinit var captor: ArgumentCaptor<QSPanel.OnConfigurationChangedListener>
@@ -95,8 +93,7 @@
uiEventLogger,
qsLogger,
dumpManager,
- quickQsBrightnessController,
- footerActionsController
+ quickQsBrightnessController
)
controller.init()
@@ -128,14 +125,12 @@
}
@Test
- fun testBrightnessAndFooterVisibilityRefreshedWhenConfigurationChanged() {
+ fun testBrightnessRefreshedWhenConfigurationChanged() {
// times(2) because both controller and base controller are registering their listeners
verify(quickQSPanel, times(2)).addOnConfigurationChangedListener(captor.capture())
captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
verify(quickQsBrightnessController).refreshVisibility(anyBoolean())
- // times(2) because footer visibility is also refreshed on controller init
- verify(footerActionsController, times(2)).refreshVisibility(anyBoolean())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
deleted file mode 100644
index 6059afe..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java
+++ /dev/null
@@ -1,340 +0,0 @@
-/*
- * 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.systemui.statusbar;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.Mockito.atLeastOnce;
-import static org.mockito.Mockito.times;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import android.app.IActivityManager;
-import android.app.IForegroundServiceObserver;
-import android.os.Binder;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.testing.AndroidTestingRunner;
-import android.util.Pair;
-
-import androidx.lifecycle.Lifecycle;
-import androidx.lifecycle.LifecycleOwner;
-import androidx.test.filters.MediumTest;
-
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.statusbar.policy.RunningFgsController;
-import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime;
-import com.android.systemui.statusbar.policy.RunningFgsControllerImpl;
-import com.android.systemui.util.concurrency.FakeExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.Mockito;
-import org.mockito.MockitoAnnotations;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Objects;
-import java.util.Random;
-import java.util.function.Consumer;
-
-@MediumTest
-@RunWith(AndroidTestingRunner.class)
-public class RunningFgsControllerTest extends SysuiTestCase {
-
- private RunningFgsController mController;
-
- private FakeSystemClock mSystemClock = new FakeSystemClock();
- private FakeExecutor mExecutor = new FakeExecutor(mSystemClock);
- private TestCallback mCallback = new TestCallback();
-
- @Mock
- private IActivityManager mActivityManager;
- @Mock
- private Lifecycle mLifecycle;
- @Mock
- private LifecycleOwner mLifecycleOwner;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle);
- mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager);
- }
-
- @Test
- public void testInitRegistersListenerInImpl() throws RemoteException {
- ((RunningFgsControllerImpl) mController).init();
- verify(mActivityManager, times(1)).registerForegroundServiceObserver(any());
- }
-
- @Test
- public void testAddCallbackCallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.addCallback(mCallback));
- }
-
- @Test
- public void testRemoveCallbackCallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.removeCallback(mCallback));
- }
-
- @Test
- public void testObserve1CallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback));
- }
-
- @Test
- public void testObserve2CallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback));
- }
-
- @Test
- public void testGetPackagesWithFgsCallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.getPackagesWithFgs());
- }
-
- @Test
- public void testStopFgsCallsInitInImpl() {
- verifyInitIsCalled(controller -> controller.stopFgs(0, ""));
- }
-
- /**
- * Tests that callbacks can be added
- */
- @Test
- public void testAddCallback() throws RemoteException {
- String testPackageName = "testPackageName";
- int testUserId = 0;
-
- IForegroundServiceObserver observer = prepareObserver();
- mController.addCallback(mCallback);
-
- observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
-
- mExecutor.advanceClockToLast();
- mExecutor.runAllReady();
-
- assertEquals("Callback should have been invoked exactly once.",
- 1, mCallback.mInvocations.size());
-
- List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0);
- assertEquals("There should have only been one package in callback. packages="
- + userPackageTimes,
- 1, userPackageTimes.size());
-
- UserPackageTime upt = userPackageTimes.get(0);
- assertEquals(testPackageName, upt.getPackageName());
- assertEquals(testUserId, upt.getUserId());
- }
-
- /**
- * Tests that callbacks can be removed. This test is only meaningful if
- * {@link #testAddCallback()} can pass.
- */
- @Test
- public void testRemoveCallback() throws RemoteException {
- String testPackageName = "testPackageName";
- int testUserId = 0;
-
- IForegroundServiceObserver observer = prepareObserver();
- mController.addCallback(mCallback);
- mController.removeCallback(mCallback);
-
- observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true);
-
- mExecutor.advanceClockToLast();
- mExecutor.runAllReady();
-
- assertEquals("Callback should not have been invoked.",
- 0, mCallback.mInvocations.size());
- }
-
- /**
- * Tests packages are added when the controller receives a callback from activity manager for
- * a foreground service start.
- */
- @Test
- public void testGetPackagesWithFgsAddingPackages() throws RemoteException {
- int numPackages = 20;
- int numUsers = 3;
-
- IForegroundServiceObserver observer = prepareObserver();
-
- assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size());
-
- List<Pair<Integer, String>> addedPackages = new ArrayList<>();
- for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
- for (int userId = 0; userId < numUsers; userId++) {
- String packageName = "package.name." + pkgNumber;
- addedPackages.add(new Pair(userId, packageName));
-
- observer.onForegroundStateChanged(new Binder(), packageName, userId, true);
-
- containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs());
- }
- }
- }
-
- /**
- * Tests packages are removed when the controller receives a callback from activity manager for
- * a foreground service ending.
- */
- @Test
- public void testGetPackagesWithFgsRemovingPackages() throws RemoteException {
- int numPackages = 20;
- int numUsers = 3;
- int arrayLength = numPackages * numUsers;
-
- String[] packages = new String[arrayLength];
- int[] users = new int[arrayLength];
- IBinder[] tokens = new IBinder[arrayLength];
- for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) {
- for (int userId = 0; userId < numUsers; userId++) {
- int i = pkgNumber * numUsers + userId;
- packages[i] = "package.name." + pkgNumber;
- users[i] = userId;
- tokens[i] = new Binder();
- }
- }
-
- IForegroundServiceObserver observer = prepareObserver();
-
- for (int i = 0; i < packages.length; i++) {
- observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true);
- }
-
- assertEquals(packages.length, mController.getPackagesWithFgs().size());
-
- List<Integer> removeOrder = new ArrayList<>();
- for (int i = 0; i < packages.length; i++) {
- removeOrder.add(i);
- }
- Collections.shuffle(removeOrder, new Random(12345));
-
- for (int idx : removeOrder) {
- removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]);
- }
-
- assertEquals(0, mController.getPackagesWithFgs().size());
- }
-
- /**
- * Tests a call on stopFgs forwards to activity manager.
- */
- @Test
- public void testStopFgs() throws RemoteException {
- String pkgName = "package.name";
- mController.stopFgs(0, pkgName);
- verify(mActivityManager).stopAppForUser(pkgName, 0);
- }
-
- /**
- * Tests a package which starts multiple services is only listed once and is only removed once
- * all services are stopped.
- */
- @Test
- public void testSinglePackageWithMultipleServices() throws RemoteException {
- String packageName = "package.name";
- int userId = 0;
- IBinder serviceToken1 = new Binder();
- IBinder serviceToken2 = new Binder();
-
- IForegroundServiceObserver observer = prepareObserver();
-
- assertEquals(0, mController.getPackagesWithFgs().size());
-
- observer.onForegroundStateChanged(serviceToken1, packageName, userId, true);
- assertSinglePackage(packageName, userId);
-
- observer.onForegroundStateChanged(serviceToken2, packageName, userId, true);
- assertSinglePackage(packageName, userId);
-
- observer.onForegroundStateChanged(serviceToken2, packageName, userId, false);
- assertSinglePackage(packageName, userId);
-
- observer.onForegroundStateChanged(serviceToken1, packageName, userId, false);
- assertEquals(0, mController.getPackagesWithFgs().size());
- }
-
- private IForegroundServiceObserver prepareObserver()
- throws RemoteException {
- mController.getPackagesWithFgs();
-
- ArgumentCaptor<IForegroundServiceObserver> argumentCaptor =
- ArgumentCaptor.forClass(IForegroundServiceObserver.class);
- verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture());
-
- return argumentCaptor.getValue();
- }
-
- private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) {
- RunningFgsControllerImpl spiedController = Mockito.spy(
- ((RunningFgsControllerImpl) mController));
- c.accept(spiedController);
- verify(spiedController, atLeastOnce()).init();
- }
-
- private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages,
- List<UserPackageTime> runningFgsPackages) {
- for (Pair<Integer, String> userPkg : addedPackages) {
- assertTrue(userPkg + " was not found in returned list",
- runningFgsPackages.stream().anyMatch(
- upt -> userPkg.first == upt.getUserId()
- && Objects.equals(upt.getPackageName(), userPkg.second)));
- }
- for (UserPackageTime upt : runningFgsPackages) {
- int userId = upt.getUserId();
- String packageName = upt.getPackageName();
- assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">"
- + " in returned list",
- addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId
- && Objects.equals(packageName, userPkg.second)));
- }
- }
-
- private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer,
- IBinder token, String pkg, int userId) throws RemoteException {
- observer.onForegroundStateChanged(token, pkg, userId, false);
- List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs();
- assertFalse("Package \"" + pkg + "\" was not removed",
- packagesWithFgs.stream().anyMatch(upt ->
- Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId));
- }
-
- private void assertSinglePackage(String packageName, int userId) {
- assertEquals(1, mController.getPackagesWithFgs().size());
- assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName());
- assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId());
- }
-
- private static class TestCallback implements RunningFgsController.Callback {
-
- private List<List<UserPackageTime>> mInvocations = new ArrayList<>();
-
- @Override
- public void onFgsPackagesChanged(List<UserPackageTime> packages) {
- mInvocations.add(packages);
- }
- }
-}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
index 8fb066b..cb248b0 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/ShadeListBuilderTest.java
@@ -29,6 +29,7 @@
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.inOrder;
@@ -46,6 +47,7 @@
import android.util.ArrayMap;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -872,6 +874,73 @@
}
@Test
+ public void testThatSectionComparatorsAreCalled() {
+ // GIVEN a section with a comparator that elevates some packages over others
+ NotifComparator comparator = spy(new HypeComparator(PACKAGE_2, PACKAGE_4));
+ NotifSectioner sectioner = new PackageSectioner(
+ List.of(PACKAGE_1, PACKAGE_2, PACKAGE_4, PACKAGE_5), comparator);
+ mListBuilder.setSectioners(List.of(sectioner));
+
+ // WHEN the pipeline is kicked off on a bunch of notifications
+ addNotif(0, PACKAGE_0);
+ addNotif(1, PACKAGE_1);
+ addNotif(2, PACKAGE_2);
+ addNotif(3, PACKAGE_3);
+ addNotif(4, PACKAGE_4);
+ addNotif(5, PACKAGE_5);
+ dispatchBuild();
+
+ // THEN the notifs are sorted according to both sectioning and the section's comparator
+ verifyBuiltList(
+ notif(2),
+ notif(4),
+ notif(1),
+ notif(5),
+ notif(0),
+ notif(3)
+ );
+
+ // VERIFY that the comparator is invoked at least 3 times
+ verify(comparator, atLeast(3)).compare(any(), any());
+
+ // VERIFY that the comparator is never invoked with the entry from package 0 or 3.
+ final NotificationEntry package0Entry = mEntrySet.get(0);
+ verify(comparator, never()).compare(eq(package0Entry), any());
+ verify(comparator, never()).compare(any(), eq(package0Entry));
+ final NotificationEntry package3Entry = mEntrySet.get(3);
+ verify(comparator, never()).compare(eq(package3Entry), any());
+ verify(comparator, never()).compare(any(), eq(package3Entry));
+ }
+
+ @Test
+ public void testThatSectionComparatorsAreNotCalledForSectionWithSingleEntry() {
+ // GIVEN a section with a comparator that will have only 1 element
+ NotifComparator comparator = spy(new HypeComparator(PACKAGE_3));
+ NotifSectioner sectioner = new PackageSectioner(List.of(PACKAGE_3), comparator);
+ mListBuilder.setSectioners(List.of(sectioner));
+
+ // WHEN the pipeline is kicked off on a bunch of notifications
+ addNotif(0, PACKAGE_1);
+ addNotif(1, PACKAGE_2);
+ addNotif(2, PACKAGE_3);
+ addNotif(3, PACKAGE_4);
+ addNotif(4, PACKAGE_5);
+ dispatchBuild();
+
+ // THEN the notifs are sorted according to the sectioning
+ verifyBuiltList(
+ notif(2),
+ notif(0),
+ notif(1),
+ notif(3),
+ notif(4)
+ );
+
+ // VERIFY that the comparator is never invoked
+ verify(comparator, never()).compare(any(), any());
+ }
+
+ @Test
public void testListenersAndPluggablesAreFiredInOrder() {
// GIVEN a bunch of registered listeners and pluggables
NotifFilter preGroupFilter = spy(new PackageFilter(PACKAGE_1));
@@ -934,7 +1003,8 @@
// GIVEN a variety of pluggables
NotifFilter packageFilter = new PackageFilter(PACKAGE_1);
NotifPromoter idPromoter = new IdPromoter(4);
- NotifSectioner section = new PackageSectioner(PACKAGE_1);
+ NotifComparator sectionComparator = new HypeComparator(PACKAGE_1);
+ NotifSectioner section = new PackageSectioner(List.of(PACKAGE_1), sectionComparator);
NotifComparator hypeComparator = new HypeComparator(PACKAGE_2);
Invalidator preRenderInvalidator = new Invalidator("PreRenderInvalidator") {};
@@ -969,6 +1039,10 @@
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
+ sectionComparator.invalidateList();
+ verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
preRenderInvalidator.invalidateList();
verify(mOnRenderListListener).onRenderList(anyList());
}
@@ -2037,16 +2111,30 @@
/** Represents a section for the passed pkg */
private static class PackageSectioner extends NotifSectioner {
- private final String mPackage;
+ private final List<String> mPackages;
+ private final NotifComparator mComparator;
+
+ PackageSectioner(List<String> pkgs, NotifComparator comparator) {
+ super("PackageSection_" + pkgs, 0);
+ mPackages = pkgs;
+ mComparator = comparator;
+ }
PackageSectioner(String pkg) {
super("PackageSection_" + pkg, 0);
- mPackage = pkg;
+ mPackages = List.of(pkg);
+ mComparator = null;
+ }
+
+ @Nullable
+ @Override
+ public NotifComparator getComparator() {
+ return mComparator;
}
@Override
public boolean isInSection(ListEntry entry) {
- return entry.getRepresentativeEntry().getSbn().getPackageName().equals(mPackage);
+ return mPackages.contains(entry.getRepresentativeEntry().getSbn().getPackageName());
}
}
@@ -2157,6 +2245,7 @@
}
}
+ private static final String PACKAGE_0 = "com.test0";
private static final String PACKAGE_1 = "com.test1";
private static final String PACKAGE_2 = "com.test2";
private static final String PACKAGE_3 = "org.test3";
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
index 8deac94..7692a05 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/ConversationCoordinatorTest.kt
@@ -32,7 +32,6 @@
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_IMPORTANT_PERSON
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier.Companion.TYPE_PERSON
-import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.withArgCaptor
import com.google.common.truth.Truth.assertThat
import org.junit.Assert.assertFalse
@@ -41,7 +40,6 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
-import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
import org.mockito.Mockito.`when` as whenever
@@ -79,7 +77,7 @@
}
peopleSectioner = coordinator.sectioner
- peopleComparator = coordinator.comparator
+ peopleComparator = peopleSectioner.comparator!!
entry = NotificationEntryBuilder().setChannel(channel).build()
@@ -108,16 +106,6 @@
}
@Test
- fun testComparatorIgnoresFromOtherSection() {
- val e1 = NotificationEntryBuilder().setId(1).setChannel(channel).build()
- val e2 = NotificationEntryBuilder().setId(2).setChannel(channel).build()
-
- // wrong section -- never classify
- assertThat(peopleComparator.compare(e1, e2)).isEqualTo(0)
- verify(peopleNotificationIdentifier, never()).getPeopleNotificationType(any())
- }
-
- @Test
fun testComparatorPutsImportantPeopleFirst() {
whenever(peopleNotificationIdentifier.getPeopleNotificationType(entryA))
.thenReturn(TYPE_IMPORTANT_PERSON)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
index bdcbbbc..4f731ed 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayoutTest.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.notification.stack;
-import static android.provider.Settings.Secure.NOTIFICATION_HISTORY_ENABLED;
import static android.view.View.GONE;
import static com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.ROWS_ALL;
@@ -41,8 +40,6 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.os.UserHandle;
-import android.provider.Settings;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.util.MathUtils;
@@ -112,10 +109,6 @@
public void setUp() throws Exception {
allowTestableLooperAsMainThread();
- Settings.Secure.putIntForUser(mContext.getContentResolver(), NOTIFICATION_HISTORY_ENABLED,
- 1, UserHandle.USER_CURRENT);
-
-
// Interact with real instance of AmbientState.
mAmbientState = new AmbientState(mContext, mNotificationSectionsManager, mBypassController);
@@ -150,6 +143,7 @@
mStackScroller.setShelfController(notificationShelfController);
mStackScroller.setStatusBar(mBar);
mStackScroller.setEmptyShadeView(mEmptyShadeView);
+ when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(true);
when(mStackScrollLayoutController.getNoticationRoundessManager())
.thenReturn(mNotificationRoundnessManager);
mStackScroller.setController(mStackScrollLayoutController);
@@ -404,6 +398,22 @@
}
@Test
+ public void testUpdateFooter_withoutHistory() {
+ setBarStateForTest(StatusBarState.SHADE);
+ mStackScroller.setCurrentUserSetup(true);
+
+ when(mStackScrollLayoutController.isHistoryEnabled()).thenReturn(false);
+ when(mStackScrollLayoutController.getVisibleNotificationCount()).thenReturn(1);
+ when(mStackScrollLayoutController.hasActiveClearableNotifications(eq(ROWS_ALL)))
+ .thenReturn(true);
+
+ FooterView view = mock(FooterView.class);
+ mStackScroller.setFooterView(view);
+ mStackScroller.updateFooter();
+ verify(mStackScroller).updateFooterView(true, true, false);
+ }
+
+ @Test
public void testUpdateFooter_oneClearableNotification_beforeUserSetup() {
setBarStateForTest(StatusBarState.SHADE);
mStackScroller.setCurrentUserSetup(false);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
index 77e9025..bbb2346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationQSContainerControllerTest.kt
@@ -6,6 +6,8 @@
import android.view.WindowManagerPolicyConstants
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.navigationbar.NavigationModeController
import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener
import com.android.systemui.recents.OverviewProxyService
@@ -46,6 +48,8 @@
private lateinit var overviewProxyService: OverviewProxyService
@Mock
private lateinit var notificationsQSContainer: NotificationsQuickSettingsContainer
+ @Mock
+ private lateinit var featureFlags: FeatureFlags
@Captor
lateinit var navigationModeCaptor: ArgumentCaptor<ModeChangedListener>
@Captor
@@ -64,7 +68,8 @@
notificationsQSContainerController = NotificationsQSContainerController(
notificationsQSContainer,
navigationModeController,
- overviewProxyService
+ overviewProxyService,
+ featureFlags
)
whenever(notificationsQSContainer.defaultNotificationsMarginBottom)
.thenReturn(NOTIFICATIONS_MARGIN)
@@ -85,6 +90,26 @@
@Test
fun testTaskbarVisibleInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(false)
+
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // taskbar should disappear when shade is expanded
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM,
+ expectedNotificationsMargin = NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarVisibleInSplitShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(true)
+
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
@@ -102,6 +127,26 @@
fun testTaskbarNotVisibleInSplitShade() {
// when taskbar is not visible, it means we're on the home screen
notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(false)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, // qs goes full height as it's not obscuring nav buttons
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShade_newFooter() {
+ // when taskbar is not visible, it means we're on the home screen
+ notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(true)
+
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
@@ -117,6 +162,25 @@
@Test
fun testTaskbarNotVisibleInSplitShadeWithCutout() {
notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(false)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = STABLE_INSET_BOTTOM + NOTIFICATIONS_MARGIN)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSplitShadeWithCutout_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(true)
+
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withCutout())
@@ -132,6 +196,24 @@
@Test
fun testTaskbarVisibleInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
+ useNewFooter(false)
+
+ given(taskbarVisible = true,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = true,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testTaskbarVisibleInSinglePaneShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ useNewFooter(true)
+
given(taskbarVisible = true,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
@@ -146,6 +228,8 @@
@Test
fun testTaskbarNotVisibleInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
+ useNewFooter(false)
+
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = emptyInsets())
@@ -159,14 +243,56 @@
given(taskbarVisible = false,
navigationMode = BUTTONS_NAVIGATION,
insets = windowInsets().withStableBottom())
- then(expectedContainerPadding = 0,
- expectedQsPadding = STABLE_INSET_BOTTOM)
+ then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
+ }
+
+ @Test
+ fun testTaskbarNotVisibleInSinglePaneShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ useNewFooter(true)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withCutout().withStableBottom())
+ then(expectedContainerPadding = CUTOUT_HEIGHT)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0, expectedQsPadding = STABLE_INSET_BOTTOM)
}
@Test
fun testCustomizingInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
notificationsQSContainerController.setCustomizerShowing(true)
+ useNewFooter(false)
+
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testCustomizingInSinglePaneShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setCustomizerShowing(true)
+ useNewFooter(true)
+
// always sets spacings to 0
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
@@ -185,6 +311,28 @@
fun testDetailShowingInSinglePaneShade() {
notificationsQSContainerController.splitShadeEnabled = false
notificationsQSContainerController.setDetailShowing(true)
+ useNewFooter(false)
+
+ // always sets spacings to 0
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0,
+ expectedNotificationsMargin = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSinglePaneShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = false
+ notificationsQSContainerController.setDetailShowing(true)
+ useNewFooter(true)
+
// always sets spacings to 0
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
@@ -202,6 +350,26 @@
@Test
fun testDetailShowingInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(false)
+
+ given(taskbarVisible = false,
+ navigationMode = GESTURES_NAVIGATION,
+ insets = windowInsets().withStableBottom())
+ then(expectedContainerPadding = 0)
+
+ notificationsQSContainerController.setDetailShowing(true)
+ // should not influence spacing
+ given(taskbarVisible = false,
+ navigationMode = BUTTONS_NAVIGATION,
+ insets = emptyInsets())
+ then(expectedContainerPadding = 0)
+ }
+
+ @Test
+ fun testDetailShowingInSplitShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(true)
+
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
@@ -246,7 +414,13 @@
verify(notificationsQSContainer)
.setPadding(anyInt(), anyInt(), anyInt(), eq(expectedContainerPadding))
verify(notificationsQSContainer).setNotificationsMarginBottom(expectedNotificationsMargin)
- verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+ val newFooter = featureFlags.isEnabled(Flags.NEW_FOOTER)
+ if (newFooter) {
+ verify(notificationsQSContainer)
+ .setQSContainerPaddingBottom(expectedNotificationsMargin)
+ } else {
+ verify(notificationsQSContainer).setQSScrollPaddingBottom(expectedQsPadding)
+ }
Mockito.clearInvocations(notificationsQSContainer)
}
@@ -263,4 +437,8 @@
whenever(stableInsetBottom).thenReturn(STABLE_INSET_BOTTOM)
return this
}
+
+ private fun useNewFooter(useNewFooter: Boolean) {
+ whenever(featureFlags.isEnabled(Flags.NEW_FOOTER)).thenReturn(useNewFooter)
+ }
}
\ No newline at end of file
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
index d325840..424a40058 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/policy/HeadsUpManagerTest.java
@@ -128,6 +128,28 @@
}
@Test
+ public void testCompareTo_withNullEntries() {
+ NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
+ mHeadsUpManager.showNotification(alertEntry);
+
+ assertThat(mHeadsUpManager.compare(alertEntry, null)).isLessThan(0);
+ assertThat(mHeadsUpManager.compare(null, alertEntry)).isGreaterThan(0);
+ assertThat(mHeadsUpManager.compare(null, null)).isEqualTo(0);
+ }
+
+ @Test
+ public void testCompareTo_withNonAlertEntries() {
+ NotificationEntry nonAlertEntry1 = new NotificationEntryBuilder().setTag("nae1").build();
+ NotificationEntry nonAlertEntry2 = new NotificationEntryBuilder().setTag("nae2").build();
+ NotificationEntry alertEntry = new NotificationEntryBuilder().setTag("alert").build();
+ mHeadsUpManager.showNotification(alertEntry);
+
+ assertThat(mHeadsUpManager.compare(alertEntry, nonAlertEntry1)).isLessThan(0);
+ assertThat(mHeadsUpManager.compare(nonAlertEntry1, alertEntry)).isGreaterThan(0);
+ assertThat(mHeadsUpManager.compare(nonAlertEntry1, nonAlertEntry2)).isEqualTo(0);
+ }
+
+ @Test
public void testAlertEntryCompareTo_ongoingCallLessThanActiveRemoteInput() {
HeadsUpManager.HeadsUpEntry ongoingCall = mHeadsUpManager.new HeadsUpEntry();
ongoingCall.setEntry(new NotificationEntryBuilder()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
index 9c49e98..ca37a404 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/BubblesTest.java
@@ -1339,6 +1339,22 @@
assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
}
+ @Test
+ public void testStackViewOnBackPressed_updatesBubbleDataExpandState() {
+ mBubbleController.updateBubble(mBubbleEntry);
+
+ // Expand the stack
+ mBubbleData.setExpanded(true);
+ assertStackExpanded();
+
+ // Hit back
+ BubbleStackView stackView = mBubbleController.getStackView();
+ stackView.onBackPressed();
+
+ // Make sure we're collapsed
+ assertStackCollapsed();
+ }
+
/** Creates a bubble using the userId and package. */
private Bubble createBubble(int userId, String pkg) {
final UserHandle userHandle = new UserHandle(userId);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
index e12a82a..d82671d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/wmshell/NewNotifPipelineBubblesTest.java
@@ -1158,6 +1158,22 @@
assertThat(mBubbleData.hasOverflowBubbleWithKey(entry.getKey())).isFalse();
}
+ @Test
+ public void testStackViewOnBackPressed_updatesBubbleDataExpandState() {
+ mBubbleController.updateBubble(mBubbleEntry);
+
+ // Expand the stack
+ mBubbleData.setExpanded(true);
+ assertStackExpanded();
+
+ // Hit back
+ BubbleStackView stackView = mBubbleController.getStackView();
+ stackView.onBackPressed();
+
+ // Make sure we're collapsed
+ assertStackCollapsed();
+ }
+
/**
* Sets the bubble metadata flags for this entry. These flags are normally set by
* NotificationManagerService when the notification is sent, however, these tests do not
diff --git a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
index e93ac47..8b62a64 100644
--- a/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
+++ b/services/accessibility/java/com/android/server/accessibility/AbstractAccessibilityServiceConnection.java
@@ -66,6 +66,7 @@
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
+import android.provider.Settings;
import android.util.Slog;
import android.util.SparseArray;
import android.view.Display;
@@ -269,6 +270,7 @@
void onDoubleTap(int displayId);
void onDoubleTapAndHold(int displayId);
+
}
public AbstractAccessibilityServiceConnection(Context context, ComponentName componentName,
@@ -2164,4 +2166,23 @@
public void onDoubleTapAndHold(int displayId) {
mSystemSupport.onDoubleTapAndHold(displayId);
}
-}
+
+ /**
+ * Sets the scaling factor for animations.
+ */
+ public void setAnimationScale(float scale) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Settings.Global.putFloat(
+ mContext.getContentResolver(), Settings.Global.WINDOW_ANIMATION_SCALE, scale);
+ Settings.Global.putFloat(
+ mContext.getContentResolver(),
+ Settings.Global.TRANSITION_ANIMATION_SCALE,
+ scale);
+ Settings.Global.putFloat(
+ mContext.getContentResolver(), Settings.Global.ANIMATOR_DURATION_SCALE, scale);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+}
\ No newline at end of file
diff --git a/services/companion/TEST_MAPPING b/services/companion/TEST_MAPPING
index 63f54fa..4a37cb8 100644
--- a/services/companion/TEST_MAPPING
+++ b/services/companion/TEST_MAPPING
@@ -1,6 +1,12 @@
{
"presubmit": [
{
+ "name": "CtsCompanionDeviceManagerCoreTestCases"
+ },
+ {
+ "name": "CtsCompanionDeviceManagerUiAutomationTestCases"
+ },
+ {
"name": "CtsOsTestCases",
"options": [
{
diff --git a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
index f1d98f0..0509e0c 100644
--- a/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
+++ b/services/companion/java/com/android/server/companion/AssociationCleanUpService.java
@@ -36,7 +36,6 @@
* will be killed if association/role are revoked.
*/
public class AssociationCleanUpService extends JobService {
- private static final String TAG = LOG_TAG + ".AssociationCleanUpService";
private static final int JOB_ID = AssociationCleanUpService.class.hashCode();
private static final long ONE_DAY_INTERVAL = 3 * 24 * 60 * 60 * 1000; // 1 Day
private CompanionDeviceManagerServiceInternal mCdmServiceInternal = LocalServices.getService(
@@ -56,7 +55,7 @@
@Override
public boolean onStopJob(final JobParameters params) {
- Slog.d(TAG, "Association cleanup job stopped; id=" + params.getJobId()
+ Slog.i(LOG_TAG, "Association cleanup job stopped; id=" + params.getJobId()
+ ", reason="
+ JobParameters.getInternalReasonCodeDescription(
params.getInternalStopReasonCode()));
diff --git a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
index 3ccabaa..21a677b8 100644
--- a/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
+++ b/services/companion/java/com/android/server/companion/AssociationStoreImpl.java
@@ -18,6 +18,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
import android.net.MacAddress;
@@ -52,9 +53,10 @@
* Other system component (both inside and outside if the com.android.server.companion package)
* should use public {@link AssociationStore} interface.
*/
+@SuppressLint("LongLogTag")
class AssociationStoreImpl implements AssociationStore {
private static final boolean DEBUG = false;
- private static final String TAG = "AssociationStore";
+ private static final String TAG = "CompanionDevice_AssociationStore";
private final Object mLock = new Object();
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index f2e66077..fd13085 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -16,16 +16,17 @@
package com.android.server.companion;
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
-
import android.companion.AssociationInfo;
+import android.os.ShellCommand;
import android.util.Log;
import android.util.Slog;
import java.io.PrintWriter;
import java.util.List;
-class CompanionDeviceShellCommand extends android.os.ShellCommand {
+class CompanionDeviceShellCommand extends ShellCommand {
+ private static final String TAG = "CompanionDevice_ShellCommand";
+
private final CompanionDeviceManagerService mService;
private final AssociationStore mAssociationStore;
@@ -84,7 +85,7 @@
}
return 0;
} catch (Throwable t) {
- Slog.e(LOG_TAG, "Error running a command: $ " + cmd, t);
+ Slog.e(TAG, "Error running a command: $ " + cmd, t);
getErrPrintWriter().println(Log.getStackTraceString(t));
return 1;
}
diff --git a/services/companion/java/com/android/server/companion/DataStoreUtils.java b/services/companion/java/com/android/server/companion/DataStoreUtils.java
index 6055a81..8ac741a 100644
--- a/services/companion/java/com/android/server/companion/DataStoreUtils.java
+++ b/services/companion/java/com/android/server/companion/DataStoreUtils.java
@@ -25,7 +25,7 @@
import android.util.AtomicFile;
import android.util.Slog;
-import com.android.internal.util.FunctionalUtils;
+import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -34,8 +34,7 @@
import java.io.FileOutputStream;
final class DataStoreUtils {
-
- private static final String LOG_TAG = DataStoreUtils.class.getSimpleName();
+ private static final String TAG = "CompanionDevice_DataStoreUtils";
static boolean isStartOfTag(@NonNull XmlPullParser parser, @NonNull String tag)
throws XmlPullParserException {
@@ -71,12 +70,12 @@
* Writing to file could fail, for example, if the user has been recently removed and so was
* their DE (/data/system_de/<user-id>/) directory.
*/
- static void writeToFileSafely(@NonNull AtomicFile file,
- @NonNull FunctionalUtils.ThrowingConsumer<FileOutputStream> consumer) {
+ static void writeToFileSafely(
+ @NonNull AtomicFile file, @NonNull ThrowingConsumer<FileOutputStream> consumer) {
try {
file.write(consumer);
} catch (Exception e) {
- Slog.e(LOG_TAG, "Error while writing to file " + file, e);
+ Slog.e(TAG, "Error while writing to file " + file, e);
}
}
diff --git a/services/companion/java/com/android/server/companion/PersistentDataStore.java b/services/companion/java/com/android/server/companion/PersistentDataStore.java
index da33b44..d0cc122 100644
--- a/services/companion/java/com/android/server/companion/PersistentDataStore.java
+++ b/services/companion/java/com/android/server/companion/PersistentDataStore.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.companion.AssociationInfo;
import android.content.pm.UserInfo;
@@ -39,6 +40,7 @@
import android.os.Environment;
import android.util.ArrayMap;
import android.util.AtomicFile;
+import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
import android.util.TypedXmlPullParser;
@@ -146,8 +148,9 @@
* </state>
* }</pre>
*/
+@SuppressLint("LongLogTag")
final class PersistentDataStore {
- private static final String LOG_TAG = CompanionDeviceManagerService.LOG_TAG + ".DataStore";
+ private static final String TAG = "CompanionDevice_PersistentDataStore";
private static final boolean DEBUG = CompanionDeviceManagerService.DEBUG;
private static final int CURRENT_PERSISTENCE_VERSION = 1;
@@ -208,10 +211,9 @@
void readStateForUser(@UserIdInt int userId,
@NonNull Collection<AssociationInfo> associationsOut,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackageOut) {
- Slog.i(LOG_TAG, "Reading associations for user " + userId + " from disk");
-
+ Slog.i(TAG, "Reading associations for user " + userId + " from disk");
final AtomicFile file = getStorageFileForUser(userId);
- if (DEBUG) Slog.d(LOG_TAG, " > File=" + file.getBaseFile().getPath());
+ if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath());
// getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
// accesses to the file on the file system using this AtomicFile object.
@@ -220,12 +222,12 @@
final AtomicFile readFrom;
final String rootTag;
if (!file.getBaseFile().exists()) {
- if (DEBUG) Slog.d(LOG_TAG, " > File does not exist -> Try to read legacy file");
+ if (DEBUG) Log.d(TAG, " > File does not exist -> Try to read legacy file");
legacyBaseFile = getBaseLegacyStorageFileForUser(userId);
- if (DEBUG) Slog.d(LOG_TAG, " > Legacy file=" + legacyBaseFile.getPath());
+ if (DEBUG) Log.d(TAG, " > Legacy file=" + legacyBaseFile.getPath());
if (!legacyBaseFile.exists()) {
- if (DEBUG) Slog.d(LOG_TAG, " > Legacy file does not exist -> Abort");
+ if (DEBUG) Log.d(TAG, " > Legacy file does not exist -> Abort");
return;
}
@@ -236,13 +238,13 @@
rootTag = XML_TAG_STATE;
}
- if (DEBUG) Slog.d(LOG_TAG, " > Reading associations...");
+ if (DEBUG) Log.d(TAG, " > Reading associations...");
final int version = readStateFromFileLocked(userId, readFrom, rootTag,
associationsOut, previouslyUsedIdsPerPackageOut);
if (DEBUG) {
- Slog.d(LOG_TAG, " > Done reading: " + associationsOut);
+ Log.d(TAG, " > Done reading: " + associationsOut);
if (version < CURRENT_PERSISTENCE_VERSION) {
- Slog.d(LOG_TAG, " > File used old format: v." + version + " -> Re-write");
+ Log.d(TAG, " > File used old format: v." + version + " -> Re-write");
}
}
@@ -250,13 +252,13 @@
// The data is either in the legacy file or in the legacy format, or both.
// Save the data to right file in using the current format.
if (DEBUG) {
- Slog.d(LOG_TAG, " > Writing the data to " + file.getBaseFile().getPath());
+ Log.d(TAG, " > Writing the data to " + file.getBaseFile().getPath());
}
persistStateToFileLocked(file, associationsOut, previouslyUsedIdsPerPackageOut);
if (legacyBaseFile != null) {
// We saved the data to the right file, can delete the old file now.
- if (DEBUG) Slog.d(LOG_TAG, " > Deleting legacy file");
+ if (DEBUG) Log.d(TAG, " > Deleting legacy file");
legacyBaseFile.delete();
}
}
@@ -273,11 +275,11 @@
void persistStateForUser(@UserIdInt int userId,
@NonNull Collection<AssociationInfo> associations,
@NonNull Map<String, Set<Integer>> previouslyUsedIdsPerPackage) {
- Slog.i(LOG_TAG, "Writing associations for user " + userId + " to disk");
- if (DEBUG) Slog.d(LOG_TAG, " > " + associations);
+ Slog.i(TAG, "Writing associations for user " + userId + " to disk");
+ if (DEBUG) Slog.d(TAG, " > " + associations);
final AtomicFile file = getStorageFileForUser(userId);
- if (DEBUG) Slog.d(LOG_TAG, " > File=" + file.getBaseFile().getPath());
+ if (DEBUG) Log.d(TAG, " > File=" + file.getBaseFile().getPath());
// getStorageFileForUser() ALWAYS returns the SAME OBJECT, which allows us to synchronize
// accesses to the file on the file system using this AtomicFile object.
synchronized (file) {
@@ -312,7 +314,7 @@
}
return version;
} catch (XmlPullParserException | IOException e) {
- Slog.e(LOG_TAG, "Error while reading associations file", e);
+ Slog.e(TAG, "Error while reading associations file", e);
return -1;
}
}
@@ -528,7 +530,7 @@
associationInfo = new AssociationInfo(associationId, userId, appPackage, macAddress,
displayName, profile, selfManaged, notify, timeApproved, lastTimeConnected);
} catch (Exception e) {
- if (DEBUG) Slog.w(LOG_TAG, "Could not create AssociationInfo", e);
+ if (DEBUG) Log.w(TAG, "Could not create AssociationInfo", e);
}
return associationInfo;
}
diff --git a/services/companion/java/com/android/server/companion/RolesUtils.java b/services/companion/java/com/android/server/companion/RolesUtils.java
index 76340fc..904283f 100644
--- a/services/companion/java/com/android/server/companion/RolesUtils.java
+++ b/services/companion/java/com/android/server/companion/RolesUtils.java
@@ -19,20 +19,23 @@
import static android.app.role.RoleManager.MANAGE_HOLDERS_FLAG_DONT_KILL_APP;
import static com.android.server.companion.CompanionDeviceManagerService.DEBUG;
-import static com.android.server.companion.CompanionDeviceManagerService.LOG_TAG;
import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.role.RoleManager;
import android.companion.AssociationInfo;
import android.content.Context;
import android.os.UserHandle;
+import android.util.Log;
import android.util.Slog;
import java.util.List;
/** Utility methods for accessing {@link RoleManager} APIs. */
+@SuppressLint("LongLogTag")
final class RolesUtils {
+ private static final String TAG = CompanionDeviceManagerService.LOG_TAG;
static boolean isRoleHolder(@NonNull Context context, @UserIdInt int userId,
@NonNull String packageName, @NonNull String role) {
@@ -45,7 +48,7 @@
static void addRoleHolderForAssociation(
@NonNull Context context, @NonNull AssociationInfo associationInfo) {
if (DEBUG) {
- Slog.d(LOG_TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
+ Log.d(TAG, "addRoleHolderForAssociation() associationInfo=" + associationInfo);
}
final String deviceProfile = associationInfo.getDeviceProfile();
@@ -61,7 +64,7 @@
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
success -> {
if (!success) {
- Slog.e(LOG_TAG, "Failed to add u" + userId + "\\" + packageName
+ Slog.e(TAG, "Failed to add u" + userId + "\\" + packageName
+ " to the list of " + deviceProfile + " holders.");
}
});
@@ -70,7 +73,7 @@
static void removeRoleHolderForAssociation(
@NonNull Context context, @NonNull AssociationInfo associationInfo) {
if (DEBUG) {
- Slog.d(LOG_TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
+ Log.d(TAG, "removeRoleHolderForAssociation() associationInfo=" + associationInfo);
}
final String deviceProfile = associationInfo.getDeviceProfile();
@@ -86,7 +89,7 @@
MANAGE_HOLDERS_FLAG_DONT_KILL_APP, userHandle, context.getMainExecutor(),
success -> {
if (!success) {
- Slog.e(LOG_TAG, "Failed to remove u" + userId + "\\" + packageName
+ Slog.e(TAG, "Failed to remove u" + userId + "\\" + packageName
+ " from the list of " + deviceProfile + " holders.");
}
});
diff --git a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
index 627b0be..a771e7b 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -33,6 +33,7 @@
import static android.bluetooth.le.ScanSettings.CALLBACK_TYPE_MATCH_LOST;
import static android.bluetooth.le.ScanSettings.SCAN_MODE_LOW_POWER;
+import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
import static com.android.server.companion.presence.Utils.btDeviceToString;
import static java.util.Objects.requireNonNull;
@@ -70,7 +71,6 @@
@SuppressLint("LongLogTag")
class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
- private static final boolean DEBUG = false;
private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
/**
diff --git a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
index 93cbe97..1ba198a 100644
--- a/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
+++ b/services/companion/java/com/android/server/companion/presence/BluetoothCompanionDeviceConnectionListener.java
@@ -16,6 +16,7 @@
package com.android.server.companion.presence;
+import static com.android.server.companion.presence.CompanionDevicePresenceMonitor.DEBUG;
import static com.android.server.companion.presence.Utils.btDeviceToString;
import android.annotation.NonNull;
@@ -39,7 +40,6 @@
class BluetoothCompanionDeviceConnectionListener
extends BluetoothAdapter.BluetoothConnectionCallback
implements AssociationStore.OnChangeListener {
- private static final boolean DEBUG = false;
private static final String TAG = "CompanionDevice_PresenceMonitor_BT";
interface Callback {
diff --git a/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
new file mode 100644
index 0000000..6371b25
--- /dev/null
+++ b/services/companion/java/com/android/server/companion/presence/CompanionDevicePresenceMonitor.java
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.companion.presence;
+
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
+import android.bluetooth.BluetoothAdapter;
+import android.companion.AssociationInfo;
+import android.content.Context;
+import android.util.Log;
+
+import com.android.server.companion.AssociationStore;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Class responsible for monitoring companion devices' "presence" status (i.e.
+ * connected/disconnected for Bluetooth devices; nearby or not for BLE devices).
+ *
+ * <p>
+ * Should only be used by
+ * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+ * to which it provides the following API:
+ * <ul>
+ * <li> {@link #onSelfManagedDeviceConnected(int)}
+ * <li> {@link #onSelfManagedDeviceDisconnected(int)}
+ * <li> {@link #isDevicePresent(int)}
+ * <li> {@link Callback#onDeviceAppeared(int) Callback.onDeviceAppeared(int)}
+ * <li> {@link Callback#onDeviceDisappeared(int) Callback.onDeviceDisappeared(int)}
+ * </ul>
+ */
+@SuppressLint("LongLogTag")
+public class CompanionDevicePresenceMonitor implements AssociationStore.OnChangeListener,
+ BluetoothCompanionDeviceConnectionListener.Callback, BleCompanionDeviceScanner.Callback {
+ static final boolean DEBUG = false;
+ private static final String TAG = "CompanionDevice_PresenceMonitor";
+
+ /** Callback for notifying about changes to status of companion devices. */
+ public interface Callback {
+ /** Invoked when companion device is found nearby or connects. */
+ void onDeviceAppeared(int associationId);
+
+ /** Invoked when a companion device no longer seen nearby or disconnects. */
+ void onDeviceDisappeared(int associationId);
+ }
+
+ private final @NonNull AssociationStore mAssociationStore;
+ private final @NonNull Callback mCallback;
+ private final @NonNull BluetoothCompanionDeviceConnectionListener mBtConnectionListener;
+ private final @NonNull BleCompanionDeviceScanner mBleScanner;
+
+ // NOTE: Same association may appear in more than one of the following sets at the same time.
+ // (E.g. self-managed devices that have MAC addresses, could be reported as present by their
+ // companion applications, while at the same be connected via BT, or detected nearby by BLE
+ // scanner)
+ private final @NonNull Set<Integer> mConnectedBtDevices = new HashSet<>();
+ private final @NonNull Set<Integer> mNearbyBleDevices = new HashSet<>();
+ private final @NonNull Set<Integer> mReportedSelfManagedDevices = new HashSet<>();
+
+ public CompanionDevicePresenceMonitor(@NonNull AssociationStore associationStore,
+ @NonNull Callback callback) {
+ mAssociationStore = associationStore;
+ mCallback = callback;
+
+ mBtConnectionListener = new BluetoothCompanionDeviceConnectionListener(associationStore,
+ /* BluetoothCompanionDeviceConnectionListener.Callback */ this);
+ mBleScanner = new BleCompanionDeviceScanner(associationStore,
+ /* BleCompanionDeviceScanner.Callback */ this);
+ }
+
+ /** Initialize {@link CompanionDevicePresenceMonitor} */
+ public void init(Context context) {
+ if (DEBUG) Log.i(TAG, "init()");
+
+ final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
+ if (btAdapter != null) {
+ mBtConnectionListener.init(btAdapter);
+ mBleScanner.init(context, btAdapter);
+ } else {
+ Log.w(TAG, "BluetoothAdapter is NOT available.");
+ }
+
+ mAssociationStore.registerListener(this);
+ }
+
+ /**
+ * @return whether the associated companion devices is present. I.e. device is nearby (for BLE);
+ * or devices is connected (for Bluetooth); or reported (by the application) to be
+ * nearby (for "self-managed" associations).
+ */
+ public boolean isDevicePresent(int associationId) {
+ return mReportedSelfManagedDevices.contains(associationId)
+ || mConnectedBtDevices.contains(associationId)
+ || mNearbyBleDevices.contains(associationId);
+ }
+
+ /**
+ * Marks a "self-managed" device as connected.
+ *
+ * <p>
+ * Must ONLY be invoked by the
+ * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+ * when an application invokes
+ * {@link android.companion.CompanionDeviceManager#notifyDeviceAppeared(int) notifyDeviceAppeared()}
+ */
+ public void onSelfManagedDeviceConnected(int associationId) {
+ onDevicePresent(mReportedSelfManagedDevices, associationId, "application-reported");
+ }
+
+ /**
+ * Marks a "self-managed" device as disconnected.
+ *
+ * <p>
+ * Must ONLY be invoked by the
+ * {@link com.android.server.companion.CompanionDeviceManagerService CompanionDeviceManagerService}
+ * when an application invokes
+ * {@link android.companion.CompanionDeviceManager#notifyDeviceDisappeared(int) notifyDeviceDisappeared()}
+ */
+ public void onSelfManagedDeviceDisconnected(int associationId) {
+ onDeviceGone(mReportedSelfManagedDevices, associationId, "application-reported");
+ }
+
+ @Override
+ public void onBluetoothCompanionDeviceConnected(int associationId) {
+ onDevicePresent(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt");
+ }
+
+ @Override
+ public void onBluetoothCompanionDeviceDisconnected(int associationId) {
+ onDeviceGone(mConnectedBtDevices, associationId, /* sourceLoggingTag */ "bt");
+ }
+
+ @Override
+ public void onBleCompanionDeviceFound(int associationId) {
+ onDevicePresent(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
+ }
+
+ @Override
+ public void onBleCompanionDeviceLost(int associationId) {
+ onDeviceGone(mNearbyBleDevices, associationId, /* sourceLoggingTag */ "ble");
+ }
+
+ private void onDevicePresent(@NonNull Set<Integer> presentDevicesForSource,
+ int newDeviceAssociationId, @NonNull String sourceLoggingTag) {
+ if (DEBUG) {
+ Log.i(TAG, "onDevice_Present() id=" + newDeviceAssociationId
+ + ", source=" + sourceLoggingTag);
+ Log.d(TAG, " > association="
+ + mAssociationStore.getAssociationById(newDeviceAssociationId));
+ }
+
+ final boolean alreadyPresent = isDevicePresent(newDeviceAssociationId);
+ if (DEBUG && alreadyPresent) Log.i(TAG, "Device is already present.");
+
+ final boolean added = presentDevicesForSource.add(newDeviceAssociationId);
+ if (DEBUG && !added) {
+ Log.w(TAG, "Association with id " + newDeviceAssociationId + " is ALREADY reported as "
+ + "present by this source (" + sourceLoggingTag + ")");
+ }
+
+ if (alreadyPresent) return;
+
+ mCallback.onDeviceAppeared(newDeviceAssociationId);
+ }
+
+ private void onDeviceGone(@NonNull Set<Integer> presentDevicesForSource,
+ int goneDeviceAssociationId, @NonNull String sourceLoggingTag) {
+ if (DEBUG) {
+ Log.i(TAG, "onDevice_Gone() id=" + goneDeviceAssociationId
+ + ", source=" + sourceLoggingTag);
+ Log.d(TAG, " > association="
+ + mAssociationStore.getAssociationById(goneDeviceAssociationId));
+ }
+
+ final boolean removed = presentDevicesForSource.remove(goneDeviceAssociationId);
+ if (!removed) {
+ if (DEBUG) {
+ Log.w(TAG, "Association with id " + goneDeviceAssociationId + " was NOT reported "
+ + "as present by this source (" + sourceLoggingTag + ")");
+ }
+ return;
+ }
+
+ final boolean stillPresent = isDevicePresent(goneDeviceAssociationId);
+ if (stillPresent) {
+ if (DEBUG) Log.i(TAG, " Device is still present.");
+ return;
+ }
+
+ mCallback.onDeviceDisappeared(goneDeviceAssociationId);
+ }
+
+ /**
+ * Implements
+ * {@link AssociationStore.OnChangeListener#onAssociationRemoved(AssociationInfo)}
+ */
+ @Override
+ public void onAssociationRemoved(@NonNull AssociationInfo association) {
+ final int id = association.getId();
+ if (DEBUG) {
+ Log.i(TAG, "onAssociationRemoved() id=" + id);
+ Log.d(TAG, " > association=" + association);
+ }
+
+ mConnectedBtDevices.remove(id);
+ mNearbyBleDevices.remove(id);
+ mReportedSelfManagedDevices.remove(id);
+
+ // Do NOT call mCallback.onDeviceDisappeared()!
+ // CompanionDeviceManagerService will know that the association is removed, and will do
+ // what's needed.
+ }
+}
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index db510cb..7714dbc 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -33,6 +33,7 @@
import android.util.Slog;
import com.android.internal.os.IBinaryTransparencyService;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.File;
import java.io.FileDescriptor;
@@ -373,6 +374,7 @@
private void getVBMetaDigestInformation() {
mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
+ FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
}
@NonNull
@@ -437,6 +439,13 @@
} else {
mBinaryHashes.put(packageName, sha256digest);
}
+
+ if (packageInfo.isApex) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
+ packageInfo.packageName,
+ packageInfo.getLongVersionCode(),
+ mBinaryHashes.get(packageInfo.packageName));
+ }
}
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Could not find package with name " + packageName);
@@ -466,6 +475,8 @@
} else {
mBinaryHashes.put(packageInfo.packageName, sha256digest);
}
+ FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED, packageInfo.packageName,
+ packageInfo.getLongVersionCode(), mBinaryHashes.get(packageInfo.packageName));
Slog.d(TAG, String.format("Last update time for %s: %d", packageInfo.packageName,
packageInfo.lastUpdateTime));
mBinaryLastUpdateTimes.put(packageInfo.packageName, packageInfo.lastUpdateTime);
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ce30f03..d719d77 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -509,8 +509,7 @@
throw new IllegalArgumentException(onWhat + " what?");
}
} catch (Exception ex) {
- Slog.wtf(TAG, "Failure reporting " + onWhat + " of user " + curUser
- + " to service " + serviceName, ex);
+ logFailure(onWhat, curUser, serviceName, ex);
}
if (!submitToThreadPool) {
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
@@ -584,8 +583,7 @@
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
"on" + USER_STARTING + "User-" + curUserId);
} catch (Exception e) {
- Slog.wtf(TAG, "Failure reporting " + USER_STARTING + " of user " + curUser
- + " to service " + serviceName, e);
+ logFailure(USER_STARTING, curUser, serviceName, e);
Slog.e(TAG, "Disabling thread pool - please capture a bug report.");
sUseLifecycleThreadPool = false;
} finally {
@@ -608,8 +606,7 @@
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
"on" + USER_COMPLETED_EVENT + "User-" + curUserId);
} catch (Exception e) {
- Slog.wtf(TAG, "Failure reporting " + USER_COMPLETED_EVENT + " of user " + curUser
- + " to service " + serviceName, e);
+ logFailure(USER_COMPLETED_EVENT, curUser, serviceName, e);
throw e;
} finally {
t.traceEnd();
@@ -617,6 +614,12 @@
};
}
+ /** Logs the failure. That's all. Tests may rely on parsing it, so only modify carefully. */
+ private void logFailure(String onWhat, TargetUser curUser, String serviceName, Exception ex) {
+ Slog.wtf(TAG, "SystemService failure: Failure reporting " + onWhat + " of user "
+ + curUser + " to service " + serviceName, ex);
+ }
+
/** Sets the safe mode flag for services to query. */
void setSafeMode(boolean safeMode) {
mSafeMode = safeMode;
diff --git a/services/core/java/com/android/server/VcnManagementService.java b/services/core/java/com/android/server/VcnManagementService.java
index 6a7afd9..2d328d8 100644
--- a/services/core/java/com/android/server/VcnManagementService.java
+++ b/services/core/java/com/android/server/VcnManagementService.java
@@ -539,7 +539,13 @@
@GuardedBy("mLock")
private void notifyAllPolicyListenersLocked() {
for (final PolicyListenerBinderDeath policyListener : mRegisteredPolicyListeners.values()) {
- Binder.withCleanCallingIdentity(() -> policyListener.mListener.onPolicyChanged());
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ policyListener.mListener.onPolicyChanged();
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
@@ -548,8 +554,13 @@
@NonNull ParcelUuid subGroup, @VcnStatusCode int statusCode) {
for (final VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, subGroup)) {
- Binder.withCleanCallingIdentity(
- () -> cbInfo.mCallback.onVcnStatusChanged(statusCode));
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ cbInfo.mCallback.onVcnStatusChanged(statusCode);
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
}
@@ -1222,13 +1233,17 @@
// Notify all registered StatusCallbacks for this subGroup
for (VcnStatusCallbackInfo cbInfo : mRegisteredStatusCallbacks.values()) {
if (isCallbackPermissioned(cbInfo, mSubGroup)) {
- Binder.withCleanCallingIdentity(
- () ->
- cbInfo.mCallback.onGatewayConnectionError(
- gatewayConnectionName,
- errorCode,
- exceptionClass,
- exceptionMessage));
+ Binder.withCleanCallingIdentity(() -> {
+ try {
+ cbInfo.mCallback.onGatewayConnectionError(
+ gatewayConnectionName,
+ errorCode,
+ exceptionClass,
+ exceptionMessage);
+ } catch (RemoteException e) {
+ logDbg("VcnStatusCallback threw on VCN status change", e);
+ }
+ });
}
}
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 442b9de..e2204e2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2901,16 +2901,31 @@
mActivityTaskManager.setPackageScreenCompatMode(packageName, mode);
}
- private boolean hasUsageStatsPermission(String callingPackage) {
+ private boolean hasUsageStatsPermission(String callingPackage, int callingUid, int callingPid) {
final int mode = mAppOpsService.noteOperation(AppOpsManager.OP_GET_USAGE_STATS,
- Binder.getCallingUid(), callingPackage, null, false, "", false).getOpMode();
+ callingUid, callingPackage, null, false, "", false).getOpMode();
if (mode == AppOpsManager.MODE_DEFAULT) {
- return checkCallingPermission(Manifest.permission.PACKAGE_USAGE_STATS)
+ return checkPermission(Manifest.permission.PACKAGE_USAGE_STATS, callingPid, callingUid)
== PackageManager.PERMISSION_GRANTED;
}
return mode == AppOpsManager.MODE_ALLOWED;
}
+ private boolean hasUsageStatsPermission(String callingPackage) {
+ return hasUsageStatsPermission(callingPackage,
+ Binder.getCallingUid(), Binder.getCallingPid());
+ }
+
+ private void enforceUsageStatsPermission(String callingPackage,
+ int callingUid, int callingPid, String operation) {
+ if (!hasUsageStatsPermission(callingPackage, callingUid, callingPid)) {
+ final String errorMsg = "Permission denial for <" + operation + "> from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
+ + " which requires PACKAGE_USAGE_STATS permission";
+ throw new SecurityException(errorMsg);
+ }
+ }
+
@Override
public int getPackageProcessState(String packageName, String callingPackage) {
if (!hasUsageStatsPermission(callingPackage)) {
@@ -12783,7 +12798,7 @@
noAction.add(null);
actions = noAction.iterator();
}
- boolean onlyProtectedBroadcasts = actions.hasNext();
+ boolean onlyProtectedBroadcasts = true;
// Collect stickies of users and check if broadcast is only registered for protected
// broadcasts
@@ -12857,6 +12872,8 @@
// Change is not enabled, thus not targeting T+. Assume exported.
flags |= Context.RECEIVER_EXPORTED;
}
+ } else if ((flags & Context.RECEIVER_NOT_EXPORTED) == 0) {
+ flags |= Context.RECEIVER_EXPORTED;
}
}
@@ -13349,6 +13366,13 @@
backgroundActivityStartsToken = null;
}
}
+
+ // TODO (206518114): We need to use the "real" package name which sent the broadcast,
+ // in case the broadcast is sent via PendingIntent.
+ if (brOptions.getIdForResponseEvent() > 0) {
+ enforceUsageStatsPermission(callerPackage, realCallingUid, realCallingPid,
+ "recordResponseEventWhileInBackground()");
+ }
}
// Verify that protected broadcasts are only being sent by system code,
@@ -16065,6 +16089,23 @@
}
/**
+ * Returns package name by pid.
+ */
+ @Override
+ @Nullable
+ public String getPackageNameByPid(int pid) {
+ synchronized (mPidsSelfLocked) {
+ final ProcessRecord app = mPidsSelfLocked.get(pid);
+
+ if (app != null && app.info != null) {
+ return app.info.packageName;
+ }
+
+ return null;
+ }
+ }
+
+ /**
* Sets if the given pid has an overlay UI or not.
*
* @param pid The pid we are setting overlay UI for.
@@ -17864,6 +17905,11 @@
}
}
+ @Override
+ public boolean isAppFreezerEnabled() {
+ return mOomAdjuster.mCachedAppOptimizer.useFreezer();
+ }
+
/**
* Resets the state of the {@link com.android.server.am.AppErrors} instance.
* This is intended for testing within the CTS only and is protected by
diff --git a/services/core/java/com/android/server/am/AppRestrictionController.java b/services/core/java/com/android/server/am/AppRestrictionController.java
index 1315293..465623f 100644
--- a/services/core/java/com/android/server/am/AppRestrictionController.java
+++ b/services/core/java/com/android/server/am/AppRestrictionController.java
@@ -54,6 +54,7 @@
import static android.content.pm.PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
import static android.content.pm.PackageManager.MATCH_DISABLED_UNTIL_USED_COMPONENTS;
import static android.os.PowerExemptionManager.REASON_ALLOWLISTED_PACKAGE;
+import static android.os.PowerExemptionManager.REASON_CARRIER_PRIVILEGED_APP;
import static android.os.PowerExemptionManager.REASON_COMPANION_DEVICE_MANAGER;
import static android.os.PowerExemptionManager.REASON_DENIED;
import static android.os.PowerExemptionManager.REASON_DEVICE_DEMO_MODE;
@@ -65,6 +66,7 @@
import static android.os.PowerExemptionManager.REASON_PROFILE_OWNER;
import static android.os.PowerExemptionManager.REASON_ROLE_DIALER;
import static android.os.PowerExemptionManager.REASON_ROLE_EMERGENCY;
+import static android.os.PowerExemptionManager.REASON_SYSTEM_ALLOW_LISTED;
import static android.os.PowerExemptionManager.REASON_SYSTEM_MODULE;
import static android.os.PowerExemptionManager.REASON_SYSTEM_UID;
import static android.os.Process.SYSTEM_UID;
@@ -125,6 +127,7 @@
import android.provider.DeviceConfig.Properties;
import android.provider.Settings;
import android.provider.Settings.Global;
+import android.telephony.TelephonyManager;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
@@ -138,6 +141,7 @@
import com.android.internal.util.function.TriConsumer;
import com.android.server.AppStateTracker;
import com.android.server.LocalServices;
+import com.android.server.SystemConfig;
import com.android.server.apphibernation.AppHibernationManagerInternal;
import com.android.server.pm.UserManagerInternal;
import com.android.server.usage.AppStandbyInternal;
@@ -229,6 +233,24 @@
@GuardedBy("mLock")
private final HashMap<String, Boolean> mSystemModulesCache = new HashMap<>();
+ /**
+ * The pre-config packages that are exempted from the background restrictions.
+ */
+ private ArraySet<String> mBgRestrictionExemptioFromSysConfig;
+
+ /**
+ * Lock specifically for bookkeeping around the carrier-privileged app set.
+ * Do not acquire any other locks while holding this one. Methods that
+ * require this lock to be held are named with a "CPL" suffix.
+ */
+ private final Object mCarrierPrivilegedLock = new Object();
+
+ /**
+ * List of carrier-privileged apps that should be excluded from standby.
+ */
+ @GuardedBy("mCarrierPrivilegedLock")
+ private List<String> mCarrierPrivilegedApps;
+
final ActivityManagerService mActivityManagerService;
/**
@@ -690,6 +712,7 @@
DeviceConfig.addOnPropertiesChangedListener(DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
ActivityThread.currentApplication().getMainExecutor(), mConstantsObserver);
mConstantsObserver.start();
+ initBgRestrictionExemptioFromSysConfig();
initRestrictionStates();
initSystemModuleNames();
registerForUidObservers();
@@ -711,6 +734,22 @@
initRestrictionStates();
}
+ private void initBgRestrictionExemptioFromSysConfig() {
+ mBgRestrictionExemptioFromSysConfig =
+ SystemConfig.getInstance().getBgRestrictionExemption();
+ if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+ final ArraySet<String> exemptedPkgs = mBgRestrictionExemptioFromSysConfig;
+ for (int i = exemptedPkgs.size() - 1; i >= 0; i--) {
+ Slog.i(TAG, "bg-restriction-exemption: " + exemptedPkgs.valueAt(i));
+ }
+ }
+ }
+
+ private boolean isExemptedFromSysConfig(String packageName) {
+ return mBgRestrictionExemptioFromSysConfig != null
+ && mBgRestrictionExemptioFromSysConfig.contains(packageName);
+ }
+
private void initRestrictionStates() {
final int[] allUsers = mInjector.getUserManagerInternal().getUserIds();
for (int userId : allUsers) {
@@ -1542,14 +1581,11 @@
}
}
- boolean isOnDeviceIdleAllowlist(int uid, boolean allowExceptIdle) {
+ boolean isOnDeviceIdleAllowlist(int uid) {
final int appId = UserHandle.getAppId(uid);
- final int[] allowlist = allowExceptIdle
- ? mDeviceIdleExceptIdleAllowlist
- : mDeviceIdleAllowlist;
-
- return Arrays.binarySearch(allowlist, appId) >= 0;
+ return Arrays.binarySearch(mDeviceIdleAllowlist, appId) >= 0
+ || Arrays.binarySearch(mDeviceIdleExceptIdleAllowlist, appId) >= 0;
}
void setDeviceIdleAllowlist(int[] allAppids, int[] exceptIdleAppids) {
@@ -1570,7 +1606,7 @@
if (UserHandle.isCore(uid)) {
return REASON_SYSTEM_UID;
}
- if (isOnDeviceIdleAllowlist(uid, false)) {
+ if (isOnDeviceIdleAllowlist(uid)) {
return REASON_ALLOWLISTED_PACKAGE;
}
final ActivityManagerInternal am = mInjector.getActivityManagerInternal();
@@ -1604,6 +1640,10 @@
return REASON_OP_ACTIVATE_PLATFORM_VPN;
} else if (isSystemModule(pkg)) {
return REASON_SYSTEM_MODULE;
+ } else if (isCarrierApp(pkg)) {
+ return REASON_CARRIER_PRIVILEGED_APP;
+ } else if (isExemptedFromSysConfig(pkg)) {
+ return REASON_SYSTEM_ALLOW_LISTED;
}
}
}
@@ -1616,6 +1656,37 @@
return REASON_DENIED;
}
+ private boolean isCarrierApp(String packageName) {
+ synchronized (mCarrierPrivilegedLock) {
+ if (mCarrierPrivilegedApps == null) {
+ fetchCarrierPrivilegedAppsCPL();
+ }
+ if (mCarrierPrivilegedApps != null) {
+ return mCarrierPrivilegedApps.contains(packageName);
+ }
+ return false;
+ }
+ }
+
+ private void clearCarrierPrivilegedApps() {
+ if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+ Slog.i(TAG, "Clearing carrier privileged apps list");
+ }
+ synchronized (mCarrierPrivilegedLock) {
+ mCarrierPrivilegedApps = null; // Need to be refetched.
+ }
+ }
+
+ @GuardedBy("mCarrierPrivilegedLock")
+ private void fetchCarrierPrivilegedAppsCPL() {
+ final TelephonyManager telephonyManager = mInjector.getTelephonyManager();
+ mCarrierPrivilegedApps =
+ telephonyManager.getCarrierPrivilegedPackagesForAllActiveSubscriptions();
+ if (DEBUG_BG_RESTRICTION_CONTROLLER) {
+ Slog.d(TAG, "apps with carrier privilege " + mCarrierPrivilegedApps);
+ }
+ }
+
private boolean isRoleHeldByUid(@NonNull String roleName, int uid) {
synchronized (mLock) {
final ArrayList<String> roles = mUidRolesMapping.get(uid);
@@ -1791,6 +1862,7 @@
private AppBatteryExemptionTracker mAppBatteryExemptionTracker;
private AppFGSTracker mAppFGSTracker;
private AppMediaSessionTracker mAppMediaSessionTracker;
+ private TelephonyManager mTelephonyManager;
Injector(Context context) {
mContext = context;
@@ -1890,6 +1962,13 @@
return mRoleManager;
}
+ TelephonyManager getTelephonyManager() {
+ if (mTelephonyManager == null) {
+ mTelephonyManager = getContext().getSystemService(TelephonyManager.class);
+ }
+ return mTelephonyManager;
+ }
+
AppFGSTracker getAppFGSTracker() {
return mAppFGSTracker;
}
@@ -1939,6 +2018,19 @@
onUidAdded(uid);
}
}
+ }
+ // fall through.
+ case Intent.ACTION_PACKAGE_CHANGED: {
+ final String pkgName = intent.getData().getSchemeSpecificPart();
+ final String[] cmpList = intent.getStringArrayExtra(
+ Intent.EXTRA_CHANGED_COMPONENT_NAME_LIST);
+ // If this is PACKAGE_ADDED (cmpList == null), or if it's a whole-package
+ // enable/disable event (cmpList is just the package name itself), drop
+ // our carrier privileged app & system-app caches and let them refresh
+ if (cmpList == null
+ || (cmpList.length == 1 && pkgName.equals(cmpList[0]))) {
+ clearCarrierPrivilegedApps();
+ }
} break;
case Intent.ACTION_PACKAGE_FULLY_REMOVED: {
final int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
@@ -1986,6 +2078,7 @@
};
final IntentFilter packageFilter = new IntentFilter();
packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
packageFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED);
packageFilter.addDataScheme("package");
mContext.registerReceiverForAllUsers(broadcastReceiver, packageFilter, null, mBgHandler);
diff --git a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
index 8561b61..1131fa8 100644
--- a/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
+++ b/services/core/java/com/android/server/am/BatteryExternalStatsWorker.java
@@ -451,7 +451,7 @@
mUidsToRemove.clear();
mCurrentFuture = null;
mUseLatestStates = true;
- if (updateFlags == UPDATE_ALL) {
+ if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
cancelSyncDueToBatteryLevelChangeLocked();
}
if ((updateFlags & UPDATE_CPU) != 0) {
@@ -496,7 +496,11 @@
Slog.wtf(TAG, "Error updating external stats: ", e);
}
- if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
+ if ((updateFlags & RESET) != 0) {
+ synchronized (BatteryExternalStatsWorker.this) {
+ mLastCollectionTimeStamp = 0;
+ }
+ } else if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
synchronized (BatteryExternalStatsWorker.this) {
mLastCollectionTimeStamp = SystemClock.elapsedRealtime();
}
@@ -658,7 +662,7 @@
mStats.updateCpuTimeLocked(onBattery, onBatteryScreenOff, cpuClusterChargeUC);
}
- if (updateFlags == UPDATE_ALL) {
+ if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
mStats.updateKernelWakelocksLocked(elapsedRealtimeUs);
mStats.updateKernelMemoryBandwidthLocked(elapsedRealtimeUs);
}
@@ -731,7 +735,7 @@
uptime, networkStatsManager);
}
- if (updateFlags == UPDATE_ALL) {
+ if ((updateFlags & UPDATE_ALL) == UPDATE_ALL) {
// This helps mStats deal with ignoring data from prior to resets.
mStats.informThatAllExternalStatsAreFlushed();
}
diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java
index 5da461d..2f7249e 100644
--- a/services/core/java/com/android/server/am/BatteryStatsService.java
+++ b/services/core/java/com/android/server/am/BatteryStatsService.java
@@ -797,12 +797,19 @@
final BatteryUsageStats bus;
switch (atomTag) {
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET:
- bus = getBatteryUsageStats(List.of(BatteryUsageStatsQuery.DEFAULT)).get(0);
+ final BatteryUsageStatsQuery querySinceReset =
+ new BatteryUsageStatsQuery.Builder()
+ .includeProcessStateData()
+ .build();
+ bus = getBatteryUsageStats(List.of(querySinceReset)).get(0);
break;
case FrameworkStatsLog.BATTERY_USAGE_STATS_SINCE_RESET_USING_POWER_PROFILE_MODEL:
- final BatteryUsageStatsQuery powerProfileQuery =
- new BatteryUsageStatsQuery.Builder().powerProfileModeledOnly().build();
- bus = getBatteryUsageStats(List.of(powerProfileQuery)).get(0);
+ final BatteryUsageStatsQuery queryPowerProfile =
+ new BatteryUsageStatsQuery.Builder()
+ .includeProcessStateData()
+ .powerProfileModeledOnly()
+ .build();
+ bus = getBatteryUsageStats(List.of(queryPowerProfile)).get(0);
break;
case FrameworkStatsLog.BATTERY_USAGE_STATS_BEFORE_RESET:
if (!BATTERY_USAGE_STORE_ENABLED) {
@@ -812,10 +819,12 @@
final long sessionStart = mBatteryUsageStatsStore
.getLastBatteryUsageStatsBeforeResetAtomPullTimestamp();
final long sessionEnd = mStats.getStartClockTime();
- final BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder()
- .aggregateSnapshots(sessionStart, sessionEnd)
- .build();
- bus = getBatteryUsageStats(List.of(query)).get(0);
+ final BatteryUsageStatsQuery queryBeforeReset =
+ new BatteryUsageStatsQuery.Builder()
+ .includeProcessStateData()
+ .aggregateSnapshots(sessionStart, sessionEnd)
+ .build();
+ bus = getBatteryUsageStats(List.of(queryBeforeReset)).get(0);
mBatteryUsageStatsStore
.setLastBatteryUsageStatsBeforeResetAtomPullTimestamp(sessionEnd);
break;
diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java
index e2921e9..a83fdd0 100644
--- a/services/core/java/com/android/server/am/BroadcastQueue.java
+++ b/services/core/java/com/android/server/am/BroadcastQueue.java
@@ -56,6 +56,7 @@
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.PowerExemptionManager;
import android.os.PowerExemptionManager.ReasonCode;
import android.os.PowerExemptionManager.TempAllowListType;
import android.os.Process;
@@ -870,7 +871,7 @@
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- // skip = true;
+ skip = true;
}
if (skip) {
@@ -1857,22 +1858,36 @@
}
private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r, int targetUid) {
+ // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
+ // getIdForResponseEvent.
+ // TODO (217251579): Temporarily use temp-allowlist reason to identify
+ // push messages and record response events.
+ useTemporaryAllowlistReasonAsSignal(r);
+ if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
+ return;
+ }
final String targetPackage = getTargetPackage(r);
// Ignore non-explicit broadcasts
if (targetPackage == null) {
return;
}
- // TODO (206518114): Only allow apps with ACCESS_PACKAGE_USAGE_STATS to set
- // getIdForResponseEvent.
- if (r.options == null || r.options.getIdForResponseEvent() <= 0) {
- return;
- }
getUsageStatsManagerInternal().reportBroadcastDispatched(
r.callingUid, targetPackage, UserHandle.of(r.userId),
r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime(),
mService.getUidStateLocked(targetUid));
}
+ private void useTemporaryAllowlistReasonAsSignal(BroadcastRecord r) {
+ if (r.options == null || r.options.getIdForResponseEvent() > 0) {
+ return;
+ }
+ final int reasonCode = r.options.getTemporaryAppAllowlistReasonCode();
+ if (reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING
+ || reasonCode == PowerExemptionManager.REASON_PUSH_MESSAGING_OVER_QUOTA) {
+ r.options.recordResponseEventWhileInBackground(reasonCode);
+ }
+ }
+
@NonNull
private UsageStatsManagerInternal getUsageStatsManagerInternal() {
final UsageStatsManagerInternal usageStatsManagerInternal =
diff --git a/services/core/java/com/android/server/am/CachedAppOptimizer.java b/services/core/java/com/android/server/am/CachedAppOptimizer.java
index 6f22c61..7af73eb 100644
--- a/services/core/java/com/android/server/am/CachedAppOptimizer.java
+++ b/services/core/java/com/android/server/am/CachedAppOptimizer.java
@@ -977,7 +977,7 @@
Slog.d(TAG_AM, "pid " + pid + " " + app.processName
+ " received sync transactions while frozen, killing");
app.killLocked("Sync transaction while in frozen state",
- ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.REASON_FREEZER,
ApplicationExitInfo.SUBREASON_FREEZER_BINDER_TRANSACTION, true);
processKilled = true;
}
@@ -990,7 +990,7 @@
Slog.d(TAG_AM, "Unable to query binder frozen info for pid " + pid + " "
+ app.processName + ". Killing it. Exception: " + e);
app.killLocked("Unable to query binder frozen stats",
- ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.REASON_FREEZER,
ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
processKilled = true;
}
@@ -1007,7 +1007,7 @@
Slog.e(TAG_AM, "Unable to unfreeze binder for " + pid + " " + app.processName
+ ". Killing it");
app.killLocked("Unable to unfreeze",
- ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.REASON_FREEZER,
ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
return;
}
@@ -1423,7 +1423,7 @@
mFreezeHandler.post(() -> {
synchronized (mAm) {
proc.killLocked("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.REASON_FREEZER,
ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
}
});
@@ -1477,7 +1477,7 @@
mFreezeHandler.post(() -> {
synchronized (mAm) {
proc.killLocked("Unable to freeze binder interface",
- ApplicationExitInfo.REASON_OTHER,
+ ApplicationExitInfo.REASON_FREEZER,
ApplicationExitInfo.SUBREASON_FREEZER_BINDER_IOCTL, true);
}
});
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index 960fbf1..145a298 100644
--- a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
+++ b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
@@ -43,6 +43,7 @@
import android.service.games.IGameSessionController;
import android.service.games.IGameSessionService;
import android.util.Slog;
+import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost.SurfacePackage;
import com.android.internal.annotations.GuardedBy;
@@ -237,7 +238,7 @@
mTaskSystemBarsVisibilityListener);
for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
- destroyGameSessionFromRecord(gameSessionRecord);
+ destroyGameSessionFromRecordLocked(gameSessionRecord);
}
mGameSessions.clear();
@@ -510,10 +511,11 @@
}
return;
}
- destroyGameSessionFromRecord(gameSessionRecord);
+ destroyGameSessionFromRecordLocked(gameSessionRecord);
}
- private void destroyGameSessionFromRecord(@NonNull GameSessionRecord gameSessionRecord) {
+ @GuardedBy("mLock")
+ private void destroyGameSessionFromRecordLocked(@NonNull GameSessionRecord gameSessionRecord) {
SurfacePackage surfacePackage = gameSessionRecord.getSurfacePackage();
if (surfacePackage != null) {
try {
@@ -586,17 +588,29 @@
@VisibleForTesting
void takeScreenshot(int taskId, @NonNull AndroidFuture callback) {
+ GameSessionRecord gameSessionRecord;
synchronized (mLock) {
- boolean isTaskAssociatedWithGameSession = mGameSessions.containsKey(taskId);
- if (!isTaskAssociatedWithGameSession) {
+ gameSessionRecord = mGameSessions.get(taskId);
+ if (gameSessionRecord == null) {
Slog.w(TAG, "No game session found for id: " + taskId);
callback.complete(GameScreenshotResult.createInternalErrorResult());
return;
}
}
+ final SurfacePackage overlaySurfacePackage = gameSessionRecord.getSurfacePackage();
+ final SurfaceControl overlaySurfaceControl =
+ overlaySurfacePackage != null ? overlaySurfacePackage.getSurfaceControl() : null;
mBackgroundExecutor.execute(() -> {
- final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId);
+ final SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder =
+ new SurfaceControl.LayerCaptureArgs.Builder(/* layer */ null);
+ if (overlaySurfaceControl != null) {
+ SurfaceControl[] excludeLayers = new SurfaceControl[1];
+ excludeLayers[0] = overlaySurfaceControl;
+ layerCaptureArgsBuilder.setExcludeLayers(excludeLayers);
+ }
+ final Bitmap bitmap = mWindowManagerService.captureTaskBitmap(taskId,
+ layerCaptureArgsBuilder);
if (bitmap == null) {
Slog.w(TAG, "Could not get bitmap for id: " + taskId);
callback.complete(GameScreenshotResult.createInternalErrorResult());
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 65be5f0..5330845 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceBroker.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
@@ -29,7 +29,7 @@
import android.media.AudioManager;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioRoutesObserver;
import android.media.ICapturePresetDevicesRoleDispatcher;
import android.media.ICommunicationDeviceDispatcher;
@@ -531,12 +531,12 @@
/*package*/ static final class BtDeviceChangedData {
final @Nullable BluetoothDevice mNewDevice;
final @Nullable BluetoothDevice mPreviousDevice;
- final @NonNull BtProfileConnectionInfo mInfo;
+ final @NonNull BluetoothProfileConnectionInfo mInfo;
final @NonNull String mEventSource;
BtDeviceChangedData(@Nullable BluetoothDevice newDevice,
@Nullable BluetoothDevice previousDevice,
- @NonNull BtProfileConnectionInfo info, @NonNull String eventSource) {
+ @NonNull BluetoothProfileConnectionInfo info, @NonNull String eventSource) {
mNewDevice = newDevice;
mPreviousDevice = previousDevice;
mInfo = info;
@@ -568,9 +568,9 @@
mDevice = device;
mState = state;
mProfile = d.mInfo.getProfile();
- mSupprNoisy = d.mInfo.getSuppressNoisyIntent();
+ mSupprNoisy = d.mInfo.isSuppressNoisyIntent();
mVolume = d.mInfo.getVolume();
- mIsLeOutput = d.mInfo.getIsLeOutput();
+ mIsLeOutput = d.mInfo.isLeOutput();
mEventSource = d.mEventSource;
mAudioSystemDevice = audioDevice;
mMusicDevice = AudioSystem.DEVICE_NONE;
@@ -641,7 +641,7 @@
audioDevice = AudioSystem.DEVICE_OUT_HEARING_AID;
break;
case BluetoothProfile.LE_AUDIO:
- if (d.mInfo.getIsLeOutput()) {
+ if (d.mInfo.isLeOutput()) {
audioDevice = AudioSystem.DEVICE_OUT_BLE_HEADSET;
} else {
audioDevice = AudioSystem.DEVICE_IN_BLE_HEADSET;
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 407d42e..4494d96 100644
--- a/services/core/java/com/android/server/audio/AudioService.java
+++ b/services/core/java/com/android/server/audio/AudioService.java
@@ -85,7 +85,7 @@
import android.media.AudioRecordingConfiguration;
import android.media.AudioRoutesInfo;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.media.IAudioFocusDispatcher;
import android.media.IAudioModeDispatcher;
import android.media.IAudioRoutesObserver;
@@ -6434,14 +6434,14 @@
* See AudioManager.handleBluetoothActiveDeviceChanged(...)
*/
public void handleBluetoothActiveDeviceChanged(BluetoothDevice newDevice,
- BluetoothDevice previousDevice, @NonNull BtProfileConnectionInfo info) {
+ BluetoothDevice previousDevice, @NonNull BluetoothProfileConnectionInfo info) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.BLUETOOTH_STACK)
!= PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Bluetooth is the only caller allowed");
}
if (info == null) {
- throw new IllegalArgumentException("Illegal null BtProfileConnectionInfo for device "
- + previousDevice + " -> " + newDevice);
+ throw new IllegalArgumentException("Illegal null BluetoothProfileConnectionInfo for"
+ + " device " + previousDevice + " -> " + newDevice);
}
final int profile = info.getProfile();
if (profile != BluetoothProfile.A2DP && profile != BluetoothProfile.A2DP_SINK
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 47f31d5..a006b91 100644
--- a/services/core/java/com/android/server/audio/BtHelper.java
+++ b/services/core/java/com/android/server/audio/BtHelper.java
@@ -31,7 +31,7 @@
import android.media.AudioDeviceAttributes;
import android.media.AudioManager;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.os.Binder;
import android.os.UserHandle;
import android.provider.Settings;
@@ -40,6 +40,7 @@
import com.android.internal.annotations.GuardedBy;
import java.io.PrintWriter;
+import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -489,11 +490,13 @@
if (proxy.getConnectionState(btDevice) == BluetoothProfile.STATE_CONNECTED) {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(btDevice, null,
- new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
} else {
mDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(null, btDevice,
- new BtProfileConnectionInfo(profile), "mBluetoothProfileServiceListener"));
+ new BluetoothProfileConnectionInfo(profile),
+ "mBluetoothProfileServiceListener"));
}
}
@@ -503,7 +506,12 @@
// Discard timeout message
mDeviceBroker.handleCancelFailureToConnectToBtHeadsetService();
mBluetoothHeadset = headset;
- setBtScoActiveDevice(headset != null ? headset.getActiveDevice() : null);
+ BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+ List<BluetoothDevice> activeDevices = Collections.emptyList();
+ if (adapter != null) {
+ activeDevices = adapter.getActiveDevices(BluetoothProfile.HEADSET);
+ }
+ setBtScoActiveDevice((activeDevices.size() > 0) ? activeDevices.get(0) : null);
// Refresh SCO audio state
checkScoAudioState();
if (mScoAudioState != SCO_STATE_ACTIVATE_REQ
diff --git a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
index a1d722b..c46ae85 100644
--- a/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
+++ b/services/core/java/com/android/server/display/BrightnessMappingStrategy.java
@@ -69,8 +69,10 @@
*/
@Nullable
public static BrightnessMappingStrategy create(Resources resources,
- DisplayDeviceConfig displayDeviceConfig) {
- return create(resources, displayDeviceConfig, /* isForIdleMode= */ false, null);
+ DisplayDeviceConfig displayDeviceConfig,
+ DisplayWhiteBalanceController displayWhiteBalanceController) {
+ return create(resources, displayDeviceConfig, /* isForIdleMode= */ false,
+ displayWhiteBalanceController);
}
/**
@@ -845,7 +847,7 @@
float nits = mBrightnessSpline.interpolate(lux);
// Adjust nits to compensate for display white balance colour strength.
- if (mDisplayWhiteBalanceController != null && isForIdleMode()) {
+ if (mDisplayWhiteBalanceController != null) {
nits = mDisplayWhiteBalanceController.calculateAdjustedBrightnessNits(nits);
}
diff --git a/services/core/java/com/android/server/display/DisplayDevice.java b/services/core/java/com/android/server/display/DisplayDevice.java
index d0ce9ef..5de162c 100644
--- a/services/core/java/com/android/server/display/DisplayDevice.java
+++ b/services/core/java/com/android/server/display/DisplayDevice.java
@@ -222,6 +222,22 @@
}
/**
+ * Returns the system preferred display mode.
+ */
+ public Display.Mode getSystemPreferredDisplayModeLocked() {
+ return EMPTY_DISPLAY_MODE;
+ }
+
+ /**
+ * Returns the display mode that was being used when this display was first found by
+ * display manager.
+ * @hide
+ */
+ public Display.Mode getActiveDisplayModeAtStartLocked() {
+ return EMPTY_DISPLAY_MODE;
+ }
+
+ /**
* Sets the requested color mode.
*/
public void setRequestedColorModeLocked(int colorMode) {
diff --git a/services/core/java/com/android/server/display/DisplayDeviceConfig.java b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
index a4a6eb4..6866137 100644
--- a/services/core/java/com/android/server/display/DisplayDeviceConfig.java
+++ b/services/core/java/com/android/server/display/DisplayDeviceConfig.java
@@ -30,6 +30,8 @@
import android.util.Spline;
import android.view.DisplayAddress;
+import com.android.internal.annotations.VisibleForTesting;
+
import com.android.internal.R;
import com.android.internal.display.BrightnessSynchronizer;
import com.android.server.display.config.BrightnessThresholds;
@@ -40,9 +42,12 @@
import com.android.server.display.config.DisplayQuirks;
import com.android.server.display.config.HbmTiming;
import com.android.server.display.config.HighBrightnessMode;
+import com.android.server.display.config.Interpolation;
import com.android.server.display.config.NitsMap;
import com.android.server.display.config.Point;
import com.android.server.display.config.RefreshRateRange;
+import com.android.server.display.config.SdrHdrRatioMap;
+import com.android.server.display.config.SdrHdrRatioPoint;
import com.android.server.display.config.SensorDetails;
import com.android.server.display.config.ThermalStatus;
import com.android.server.display.config.ThermalThrottling;
@@ -66,10 +71,126 @@
import javax.xml.datatype.DatatypeConfigurationException;
/**
- * Reads and stores display-specific configurations.
+ * Reads and stores display-specific configurations.
+ * File format:
+ * <pre>
+ * {@code
+ * <displayConfiguration>
+ * <densityMap>
+ * <density>
+ * <height>480</height>
+ * <width>720</width>
+ * <density>120</density>
+ * </density>
+ * <density>
+ * <height>720</height>
+ * <width>1280</width>
+ * <density>213</density>
+ * </density>
+ * <density>
+ * <height>1080</height>
+ * <width>1920</width>
+ * <density>320</density>
+ * </density>
+ * <density>
+ * <height>2160</height>
+ * <width>3840</width>
+ * <density>640</density>
+ * </density>
+ * </densityMap>
+ *
+ * <screenBrightnessMap>
+ * <point>
+ * <value>0.0</value>
+ * <nits>2.0</nits>
+ * </point>
+ * <point>
+ * <value>0.62</value>
+ * <nits>500.0</nits>
+ * </point>
+ * <point>
+ * <value>1.0</value>
+ * <nits>800.0</nits>
+ * </point>
+ * </screenBrightnessMap>
+ *
+ * <screenBrightnessDefault>0.65</screenBrightnessDefault>
+ *
+ * <thermalThrottling>
+ * <brightnessThrottlingMap>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>severe</thermalStatus>
+ * <brightness>0.1</brightness>
+ * </brightnessThrottlingPoint>
+ * <brightnessThrottlingPoint>
+ * <thermalStatus>critical</thermalStatus>
+ * <brightness>0.01</brightness>
+ * </brightnessThrottlingPoint>
+ * </brightnessThrottlingMap>
+ * </thermalThrottling>
+ *
+ * <highBrightnessMode enabled="true">
+ * <transitionPoint>0.62</transitionPoint>
+ * <minimumLux>10000</minimumLux>
+ * <timing>
+ * <timeWindowSecs>1800</timeWindowSecs> // Window in which we restrict HBM.
+ * <timeMaxSecs>300</timeMaxSecs> // Maximum time of HBM allowed in that window.
+ * <timeMinSecs>60</timeMinSecs> // Minimum time remaining required to switch
+ * </timing> // HBM on for.
+ * <refreshRate>
+ * <minimum>120</minimum>
+ * <maximum>120</maximum>
+ * </refreshRate>
+ * <thermalStatusLimit>light</thermalStatusLimit>
+ * <allowInLowPowerMode>false</allowInLowPowerMode>
+ * </highBrightnessMode>
+ *
+ * <quirks>
+ * <quirk>canSetBrightnessViaHwc</quirk>
+ * </quirks>
+ *
+ * <screenBrightnessRampFastDecrease>0.01</screenBrightnessRampFastDecrease>
+ * <screenBrightnessRampFastIncrease>0.02</screenBrightnessRampFastIncrease>
+ * <screenBrightnessRampSlowDecrease>0.03</screenBrightnessRampSlowDecrease>
+ * <screenBrightnessRampSlowIncrease>0.04</screenBrightnessRampSlowIncrease>
+ *
+ * <lightSensor>
+ * <type>android.sensor.light</type>
+ * <name>1234 Ambient Light Sensor</name>
+ * </lightSensor>
+ * <proxSensor>
+ * <type>android.sensor.proximity</type>
+ * <name>1234 Proximity Sensor</name>
+ * </proxSensor>
+ *
+ * <ambientLightHorizonLong>10001</ambientLightHorizonLong>
+ * <ambientLightHorizonShort>2001</ambientLightHorizonShort>
+ *
+ * <displayBrightnessChangeThresholds>
+ * <brighteningThresholds>
+ * <minimum>0.001</minimum> // Minimum change needed in screen brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.002</minimum> // Minimum change needed in screen brightness to darken.
+ * </darkeningThresholds>
+ * </displayBrightnessChangeThresholds>
+ *
+ * <ambientBrightnessChangeThresholds>
+ * <brighteningThresholds>
+ * <minimum>0.003</minimum> // Minimum change needed in ambient brightness to brighten.
+ * </brighteningThresholds>
+ * <darkeningThresholds>
+ * <minimum>0.004</minimum> // Minimum change needed in ambient brightness to darken.
+ * </darkeningThresholds>
+ * </ambientBrightnessChangeThresholds>
+ *
+ * </displayConfiguration>
+ * }
+ * </pre>
*/
public class DisplayDeviceConfig {
private static final String TAG = "DisplayDeviceConfig";
+ private static final boolean DEBUG = false;
public static final float HIGH_BRIGHTNESS_MODE_UNSUPPORTED = Float.NaN;
@@ -86,6 +207,9 @@
private static final String NO_SUFFIX_FORMAT = "%d";
private static final long STABLE_FLAG = 1L << 62;
+ private static final int INTERPOLATION_DEFAULT = 0;
+ private static final int INTERPOLATION_LINEAR = 1;
+
// Float.NaN (used as invalid for brightness) cannot be stored in config.xml
// so -2 is used instead
private static final float INVALID_BRIGHTNESS_IN_CONFIG = -2f;
@@ -99,6 +223,9 @@
// Length of the ambient light horizon used to calculate short-term estimate of ambient light.
private static final int AMBIENT_LIGHT_SHORT_HORIZON_MILLIS = 2000;
+ @VisibleForTesting
+ static final float HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT = 0.5f;
+
private final Context mContext;
// The details of the ambient light sensor associated with this display.
@@ -114,6 +241,7 @@
// config.xml. These are the raw values and just used for the dumpsys
private float[] mRawNits;
private float[] mRawBacklight;
+ private int mInterpolationType;
// These arrays are calculated from the raw arrays, but clamped to contain values equal to and
// between mBacklightMinimum and mBacklightMaximum. These three arrays should all be the same
@@ -142,6 +270,7 @@
private Spline mBrightnessToBacklightSpline;
private Spline mBacklightToBrightnessSpline;
private Spline mBacklightToNitsSpline;
+ private Spline mNitsToBacklightSpline;
private List<String> mQuirks;
private boolean mIsHighBrightnessModeEnabled = false;
private HighBrightnessModeData mHbmData;
@@ -149,6 +278,7 @@
private String mLoadedFrom = null;
private BrightnessThrottlingData mBrightnessThrottlingData;
+ private Spline mSdrToHdrRatioSpline;
private DisplayDeviceConfig(Context context) {
mContext = context;
@@ -333,6 +463,45 @@
}
/**
+ * Calculate the HDR brightness for the specified SDR brightenss.
+ *
+ * @return the HDR brightness or BRIGHTNESS_INVALID when no mapping exists.
+ */
+ public float getHdrBrightnessFromSdr(float brightness) {
+ if (mSdrToHdrRatioSpline == null) {
+ return PowerManager.BRIGHTNESS_INVALID;
+ }
+
+ float backlight = getBacklightFromBrightness(brightness);
+ float nits = getNitsFromBacklight(backlight);
+ if (nits == NITS_INVALID) {
+ return PowerManager.BRIGHTNESS_INVALID;
+ }
+
+ float ratio = mSdrToHdrRatioSpline.interpolate(nits);
+ float hdrNits = nits * ratio;
+ if (mNitsToBacklightSpline == null) {
+ return PowerManager.BRIGHTNESS_INVALID;
+ }
+
+ float hdrBacklight = mNitsToBacklightSpline.interpolate(hdrNits);
+ hdrBacklight = Math.max(mBacklightMinimum, Math.min(mBacklightMaximum, hdrBacklight));
+ float hdrBrightness = mBacklightToBrightnessSpline.interpolate(hdrBacklight);
+
+ if (DEBUG) {
+ Slog.d(TAG, "getHdrBrightnessFromSdr: sdr brightness " + brightness
+ + " backlight " + backlight
+ + " nits " + nits
+ + " ratio " + ratio
+ + " hdrNits " + hdrNits
+ + " hdrBacklight " + hdrBacklight
+ + " hdrBrightness " + hdrBrightness
+ );
+ }
+ return hdrBrightness;
+ }
+
+ /**
* Return an array of equal length to backlight and nits, that covers the entire system
* brightness range of 0.0-1.0.
*
@@ -444,15 +613,18 @@
+ ", mNits=" + Arrays.toString(mNits)
+ ", mRawBacklight=" + Arrays.toString(mRawBacklight)
+ ", mRawNits=" + Arrays.toString(mRawNits)
+ + ", mInterpolationType=" + mInterpolationType
+ ", mBrightness=" + Arrays.toString(mBrightness)
+ ", mBrightnessToBacklightSpline=" + mBrightnessToBacklightSpline
+ ", mBacklightToBrightnessSpline=" + mBacklightToBrightnessSpline
+ + ", mNitsToBacklightSpline=" + mNitsToBacklightSpline
+ ", mBacklightMinimum=" + mBacklightMinimum
+ ", mBacklightMaximum=" + mBacklightMaximum
+ ", mBrightnessDefault=" + mBrightnessDefault
+ ", mQuirks=" + mQuirks
+ ", isHbmEnabled=" + mIsHighBrightnessModeEnabled
+ ", mHbmData=" + mHbmData
+ + ", mSdrToHdrRatioSpline=" + mSdrToHdrRatioSpline
+ ", mBrightnessThrottlingData=" + mBrightnessThrottlingData
+ ", mBrightnessRampFastDecrease=" + mBrightnessRampFastDecrease
+ ", mBrightnessRampFastIncrease=" + mBrightnessRampFastIncrease
@@ -653,6 +825,7 @@
float[] nits = new float[size];
float[] backlight = new float[size];
+ mInterpolationType = convertInterpolationType(map.getInterpolation());
int i = 0;
for (Point point : points) {
nits[i] = point.getNits().floatValue();
@@ -678,6 +851,40 @@
constrainNitsAndBacklightArrays();
}
+ private Spline loadSdrHdrRatioMap(HighBrightnessMode hbmConfig) {
+ final SdrHdrRatioMap sdrHdrRatioMap = hbmConfig.getSdrHdrRatioMap_all();
+
+ if (sdrHdrRatioMap == null) {
+ return null;
+ }
+
+ final List<SdrHdrRatioPoint> points = sdrHdrRatioMap.getPoint();
+ final int size = points.size();
+ if (size <= 0) {
+ return null;
+ }
+
+ float[] nits = new float[size];
+ float[] ratios = new float[size];
+
+ int i = 0;
+ for (SdrHdrRatioPoint point : points) {
+ nits[i] = point.getSdrNits().floatValue();
+ if (i > 0) {
+ if (nits[i] < nits[i - 1]) {
+ Slog.e(TAG, "sdrHdrRatioMap must be non-decreasing, ignoring rest "
+ + " of configuration. nits: " + nits[i] + " < "
+ + nits[i - 1]);
+ return null;
+ }
+ }
+ ratios[i] = point.getHdrRatio().floatValue();
+ ++i;
+ }
+
+ return Spline.createSpline(nits, ratios);
+ }
+
private void loadBrightnessThrottlingMap(DisplayConfiguration config) {
final ThermalThrottling throttlingConfig = config.getThermalThrottling();
if (throttlingConfig == null) {
@@ -823,9 +1030,18 @@
mBacklight[mBacklight.length - 1],
PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, mBacklight[i]);
}
- mBrightnessToBacklightSpline = Spline.createSpline(mBrightness, mBacklight);
- mBacklightToBrightnessSpline = Spline.createSpline(mBacklight, mBrightness);
- mBacklightToNitsSpline = Spline.createSpline(mBacklight, mNits);
+ mBrightnessToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
+ ? Spline.createLinearSpline(mBrightness, mBacklight)
+ : Spline.createSpline(mBrightness, mBacklight);
+ mBacklightToBrightnessSpline = mInterpolationType == INTERPOLATION_LINEAR
+ ? Spline.createLinearSpline(mBacklight, mBrightness)
+ : Spline.createSpline(mBacklight, mBrightness);
+ mBacklightToNitsSpline = mInterpolationType == INTERPOLATION_LINEAR
+ ? Spline.createLinearSpline(mBacklight, mNits)
+ : Spline.createSpline(mBacklight, mNits);
+ mNitsToBacklightSpline = mInterpolationType == INTERPOLATION_LINEAR
+ ? Spline.createLinearSpline(mNits, mBacklight)
+ : Spline.createSpline(mNits, mBacklight);
}
private void loadQuirks(DisplayConfiguration config) {
@@ -862,6 +1078,20 @@
mRefreshRateLimitations.add(new RefreshRateLimitation(
DisplayManagerInternal.REFRESH_RATE_LIMIT_HIGH_BRIGHTNESS_MODE, min, max));
}
+ BigDecimal minHdrPctOfScreen = hbm.getMinimumHdrPercentOfScreen_all();
+ if (minHdrPctOfScreen != null) {
+ mHbmData.minimumHdrPercentOfScreen = minHdrPctOfScreen.floatValue();
+ if (mHbmData.minimumHdrPercentOfScreen > 1
+ || mHbmData.minimumHdrPercentOfScreen < 0) {
+ Slog.w(TAG, "Invalid minimum HDR percent of screen: "
+ + String.valueOf(mHbmData.minimumHdrPercentOfScreen));
+ mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
+ }
+ } else {
+ mHbmData.minimumHdrPercentOfScreen = HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
+ }
+
+ mSdrToHdrRatioSpline = loadSdrHdrRatioMap(hbm);
}
}
@@ -1024,6 +1254,21 @@
}
}
+ private int convertInterpolationType(Interpolation value) {
+ if (value == null) {
+ return INTERPOLATION_DEFAULT;
+ }
+ switch (value) {
+ case _default:
+ return INTERPOLATION_DEFAULT;
+ case linear:
+ return INTERPOLATION_LINEAR;
+ default:
+ Slog.wtf(TAG, "Unexpected Interpolation Type: " + value);
+ return INTERPOLATION_DEFAULT;
+ }
+ }
+
private void loadAmbientHorizonFromDdc(DisplayConfiguration config) {
final BigInteger configLongHorizon = config.getAmbientLightHorizonLong();
if (configLongHorizon != null) {
@@ -1088,11 +1333,15 @@
/** Minimum time that HBM can be on before being enabled. */
public long timeMinMillis;
+ /** Minimum HDR video size to enter high brightness mode */
+ public float minimumHdrPercentOfScreen;
+
HighBrightnessModeData() {}
HighBrightnessModeData(float minimumLux, float transitionPoint, long timeWindowMillis,
long timeMaxMillis, long timeMinMillis,
- @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode) {
+ @PowerManager.ThermalStatus int thermalStatusLimit, boolean allowInLowPowerMode,
+ float minimumHdrPercentOfScreen) {
this.minimumLux = minimumLux;
this.transitionPoint = transitionPoint;
this.timeWindowMillis = timeWindowMillis;
@@ -1100,6 +1349,7 @@
this.timeMinMillis = timeMinMillis;
this.thermalStatusLimit = thermalStatusLimit;
this.allowInLowPowerMode = allowInLowPowerMode;
+ this.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
/**
@@ -1114,6 +1364,7 @@
other.transitionPoint = transitionPoint;
other.thermalStatusLimit = thermalStatusLimit;
other.allowInLowPowerMode = allowInLowPowerMode;
+ other.minimumHdrPercentOfScreen = minimumHdrPercentOfScreen;
}
@Override
@@ -1126,6 +1377,7 @@
+ ", timeMin: " + timeMinMillis + "ms"
+ ", thermalStatusLimit: " + thermalStatusLimit
+ ", allowInLowPowerMode: " + allowInLowPowerMode
+ + ", minimumHdrPercentOfScreen: " + minimumHdrPercentOfScreen
+ "} ";
}
}
diff --git a/services/core/java/com/android/server/display/DisplayManagerService.java b/services/core/java/com/android/server/display/DisplayManagerService.java
index 4e88acd..7f1482e 100644
--- a/services/core/java/com/android/server/display/DisplayManagerService.java
+++ b/services/core/java/com/android/server/display/DisplayManagerService.java
@@ -1833,6 +1833,16 @@
}
}
+ Display.Mode getSystemPreferredDisplayModeInternal(int displayId) {
+ synchronized (mSyncRoot) {
+ final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ return device.getSystemPreferredDisplayModeLocked();
+ }
+ }
+
void setShouldAlwaysRespectAppRequestedModeInternal(boolean enabled) {
mDisplayModeDirector.setShouldAlwaysRespectAppRequestedMode(enabled);
}
@@ -2183,6 +2193,16 @@
}
}
+ Display.Mode getActiveDisplayModeAtStart(int displayId) {
+ synchronized (mSyncRoot) {
+ final DisplayDevice device = getDeviceForDisplayLocked(displayId);
+ if (device == null) {
+ return null;
+ }
+ return device.getActiveDisplayModeAtStartLocked();
+ }
+ }
+
void setAmbientColorTemperatureOverride(float cct) {
synchronized (mSyncRoot) {
final DisplayPowerController displayPowerController = mDisplayPowerControllers.get(
@@ -3471,6 +3491,16 @@
}
@Override // Binder call
+ public Display.Mode getSystemPreferredDisplayMode(int displayId) {
+ final long token = Binder.clearCallingIdentity();
+ try {
+ return getSystemPreferredDisplayModeInternal(displayId);
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
+ }
+
+ @Override // Binder call
public void setShouldAlwaysRespectAppRequestedMode(boolean enabled) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.OVERRIDE_DISPLAY_MODE_REQUESTS,
diff --git a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
index a9875c8..bfdac57 100644
--- a/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
+++ b/services/core/java/com/android/server/display/DisplayManagerShellCommand.java
@@ -68,6 +68,8 @@
return clearUserPreferredDisplayMode();
case "get-user-preferred-display-mode":
return getUserPreferredDisplayMode();
+ case "get-active-display-mode-at-start":
+ return getActiveDisplayModeAtStart();
case "set-match-content-frame-rate-pref":
return setMatchContentFrameRateUserPreference();
case "get-match-content-frame-rate-pref":
@@ -125,6 +127,9 @@
pw.println(" Returns the user preferred display mode or null if no mode is set by user."
+ "If DISPLAY_ID is passed, the mode for display with id = DISPLAY_ID is "
+ "returned, else global display mode is returned.");
+ pw.println(" get-active-display-mode-at-start DISPLAY_ID");
+ pw.println(" Returns the display mode which was found at boot time of display with "
+ + "id = DISPLAY_ID");
pw.println(" set-match-content-frame-rate-pref PREFERENCE");
pw.println(" Sets the match content frame rate preference as PREFERENCE ");
pw.println(" get-match-content-frame-rate-pref");
@@ -298,6 +303,30 @@
return 0;
}
+ private int getActiveDisplayModeAtStart() {
+ final String displayIdText = getNextArg();
+ if (displayIdText == null) {
+ getErrPrintWriter().println("Error: no displayId specified");
+ return 1;
+ }
+ final int displayId;
+ try {
+ displayId = Integer.parseInt(displayIdText);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: invalid displayId");
+ return 1;
+ }
+
+ Display.Mode mode = mService.getActiveDisplayModeAtStart(displayId);
+ if (mode == null) {
+ getOutPrintWriter().println("Boot display mode: null");
+ return 0;
+ }
+ getOutPrintWriter().println("Boot display mode: " + mode.getPhysicalWidth() + " "
+ + mode.getPhysicalHeight() + " " + mode.getRefreshRate());
+ return 0;
+ }
+
private int setMatchContentFrameRateUserPreference() {
final String matchContentFrameRatePrefText = getNextArg();
if (matchContentFrameRatePrefText == null) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index 6ae1a5a..d71e07a 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -546,28 +546,6 @@
// Seed the cached brightness
saveBrightnessInfo(getScreenBrightnessSetting());
- setUpAutoBrightness(resources, handler);
-
- mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
- mColorFadeFadesConfig = resources.getBoolean(
- com.android.internal.R.bool.config_animateScreenLights);
-
- mDisplayBlanksAfterDozeConfig = resources.getBoolean(
- com.android.internal.R.bool.config_displayBlanksAfterDoze);
-
- mBrightnessBucketsInDozeConfig = resources.getBoolean(
- com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
-
- loadProximitySensor();
-
- mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
- mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
- mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
- mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
- mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
-
DisplayWhiteBalanceSettings displayWhiteBalanceSettings = null;
DisplayWhiteBalanceController displayWhiteBalanceController = null;
if (mDisplayId == Display.DEFAULT_DISPLAY) {
@@ -610,6 +588,29 @@
} else {
mCdsi = null;
}
+
+ setUpAutoBrightness(resources, handler);
+
+ mColorFadeEnabled = !ActivityManager.isLowRamDeviceStatic();
+ mColorFadeFadesConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_animateScreenLights);
+
+ mDisplayBlanksAfterDozeConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_displayBlanksAfterDoze);
+
+ mBrightnessBucketsInDozeConfig = resources.getBoolean(
+ com.android.internal.R.bool.config_displayBrightnessBucketsInDoze);
+
+ loadProximitySensor();
+
+ mCurrentScreenBrightnessSetting = getScreenBrightnessSetting();
+ mScreenBrightnessForVr = getScreenBrightnessForVrSetting();
+ mAutoBrightnessAdjustment = getAutoBrightnessAdjustmentSetting();
+ mTemporaryScreenBrightness = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingScreenBrightnessSetting = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mTemporaryAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+ mPendingAutoBrightnessAdjustment = PowerManager.BRIGHTNESS_INVALID_FLOAT;
+
}
private void applyReduceBrightColorsSplineAdjustment(
@@ -831,7 +832,13 @@
setUpAutoBrightness(mContext.getResources(), mHandler);
reloadReduceBrightColours();
mHbmController.resetHbmData(info.width, info.height, token, info.uniqueId,
- mDisplayDeviceConfig.getHighBrightnessModeData());
+ mDisplayDeviceConfig.getHighBrightnessModeData(),
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ });
mBrightnessThrottler.resetThrottlingData(
mDisplayDeviceConfig.getBrightnessThrottlingData());
}
@@ -901,7 +908,7 @@
final boolean isIdleScreenBrightnessEnabled = resources.getBoolean(
R.bool.config_enableIdleScreenBrightnessMode);
mInteractiveModeBrightnessMapper = BrightnessMappingStrategy.create(resources,
- mDisplayDeviceConfig);
+ mDisplayDeviceConfig, mDisplayWhiteBalanceController);
if (isIdleScreenBrightnessEnabled) {
mIdleModeBrightnessMapper = BrightnessMappingStrategy.createForIdleMode(resources,
mDisplayDeviceConfig, mDisplayWhiteBalanceController);
@@ -1713,6 +1720,12 @@
final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
return new HighBrightnessModeController(mHandler, info.width, info.height, displayToken,
displayUniqueId, PowerManager.BRIGHTNESS_MIN, PowerManager.BRIGHTNESS_MAX, hbmData,
+ new HighBrightnessModeController.HdrBrightnessDeviceConfig() {
+ @Override
+ public float getHdrBrightnessFromSdr(float sdrBrightness) {
+ return mDisplayDeviceConfig.getHdrBrightnessFromSdr(sdrBrightness);
+ }
+ },
() -> {
sendUpdatePowerStateLocked();
postBrightnessChangeRunnable();
diff --git a/services/core/java/com/android/server/display/HighBrightnessModeController.java b/services/core/java/com/android/server/display/HighBrightnessModeController.java
index 23c17f5..0b9d4de 100644
--- a/services/core/java/com/android/server/display/HighBrightnessModeController.java
+++ b/services/core/java/com/android/server/display/HighBrightnessModeController.java
@@ -58,11 +58,13 @@
private static final boolean DEBUG = false;
- private static final float HDR_PERCENT_OF_SCREEN_REQUIRED = 0.50f;
-
@VisibleForTesting
static final float HBM_TRANSITION_POINT_INVALID = Float.POSITIVE_INFINITY;
+ public interface HdrBrightnessDeviceConfig {
+ float getHdrBrightnessFromSdr(float sdrBrightness);
+ }
+
private final float mBrightnessMin;
private final float mBrightnessMax;
private final Handler mHandler;
@@ -76,6 +78,7 @@
private HdrListener mHdrListener;
private HighBrightnessModeData mHbmData;
+ private HdrBrightnessDeviceConfig mHdrBrightnessCfg;
private IBinder mRegisteredDisplayToken;
private boolean mIsInAllowedAmbientRange = false;
@@ -115,16 +118,17 @@
HighBrightnessModeController(Handler handler, int width, int height, IBinder displayToken,
String displayUniqueId, float brightnessMin, float brightnessMax,
- HighBrightnessModeData hbmData, Runnable hbmChangeCallback, Context context) {
+ HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
+ Runnable hbmChangeCallback, Context context) {
this(new Injector(), handler, width, height, displayToken, displayUniqueId, brightnessMin,
- brightnessMax, hbmData, hbmChangeCallback, context);
+ brightnessMax, hbmData, hdrBrightnessCfg, hbmChangeCallback, context);
}
@VisibleForTesting
HighBrightnessModeController(Injector injector, Handler handler, int width, int height,
IBinder displayToken, String displayUniqueId, float brightnessMin, float brightnessMax,
- HighBrightnessModeData hbmData, Runnable hbmChangeCallback,
- Context context) {
+ HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg,
+ Runnable hbmChangeCallback, Context context) {
mInjector = injector;
mContext = context;
mClock = injector.getClock();
@@ -138,7 +142,7 @@
mRecalcRunnable = this::recalculateTimeAllowance;
mHdrListener = new HdrListener();
- resetHbmData(width, height, displayToken, displayUniqueId, hbmData);
+ resetHbmData(width, height, displayToken, displayUniqueId, hbmData, hdrBrightnessCfg);
}
void setAutoBrightnessEnabled(int state) {
@@ -178,6 +182,13 @@
}
float getHdrBrightnessValue() {
+ if (mHdrBrightnessCfg != null) {
+ float hdrBrightness = mHdrBrightnessCfg.getHdrBrightnessFromSdr(mBrightness);
+ if (hdrBrightness != PowerManager.BRIGHTNESS_INVALID) {
+ return hdrBrightness;
+ }
+ }
+
// For HDR brightness, we take the current brightness and scale it to the max. The reason
// we do this is because we want brightness to go to HBM max when it would normally go
// to normal max, meaning it should not wait to go to 10000 lux (or whatever the transition
@@ -250,10 +261,11 @@
}
void resetHbmData(int width, int height, IBinder displayToken, String displayUniqueId,
- HighBrightnessModeData hbmData) {
+ HighBrightnessModeData hbmData, HdrBrightnessDeviceConfig hdrBrightnessCfg) {
mWidth = width;
mHeight = height;
mHbmData = hbmData;
+ mHdrBrightnessCfg = hdrBrightnessCfg;
mDisplayStatsId = displayUniqueId.hashCode();
unregisterHdrListener();
@@ -602,8 +614,8 @@
int maxW, int maxH, int flags) {
mHandler.post(() -> {
mIsHdrLayerPresent = numberOfHdrLayers > 0
- && (float) (maxW * maxH)
- >= ((float) (mWidth * mHeight) * HDR_PERCENT_OF_SCREEN_REQUIRED);
+ && (float) (maxW * maxH) >= ((float) (mWidth * mHeight)
+ * mHbmData.minimumHdrPercentOfScreen);
// Calling the brightness update so that we can recalculate
// brightness with HDR in mind.
onBrightnessChanged(mBrightness, mUnthrottledBrightness, mThrottlingReason);
diff --git a/services/core/java/com/android/server/display/LocalDisplayAdapter.java b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
index 3a9ef0a..a31c231 100644
--- a/services/core/java/com/android/server/display/LocalDisplayAdapter.java
+++ b/services/core/java/com/android/server/display/LocalDisplayAdapter.java
@@ -192,8 +192,12 @@
private float mBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private float mSdrBrightnessState = PowerManager.BRIGHTNESS_INVALID_FLOAT;
private int mDefaultModeId = INVALID_MODE_ID;
+ private int mSystemPreferredModeId = INVALID_MODE_ID;
private int mDefaultModeGroup;
private int mUserPreferredModeId = INVALID_MODE_ID;
+ // This is used only for the purpose of testing, to verify if the mode was correct when the
+ // device started or booted.
+ private int mActiveDisplayModeAtStartId = INVALID_MODE_ID;
private Display.Mode mUserPreferredMode;
private int mActiveModeId = INVALID_MODE_ID;
private DisplayModeDirector.DesiredDisplayModeSpecs mDisplayModeSpecs =
@@ -208,7 +212,7 @@
private boolean mSidekickActive;
private SidekickInternal mSidekickInternal;
private SurfaceControl.StaticDisplayInfo mStaticDisplayInfo;
- // The supported display modes according in SurfaceFlinger
+ // The supported display modes according to SurfaceFlinger
private SurfaceControl.DisplayMode[] mSfDisplayModes;
// The active display mode in SurfaceFlinger
private SurfaceControl.DisplayMode mActiveSfDisplayMode;
@@ -230,6 +234,7 @@
mBacklightAdapter = new BacklightAdapter(displayToken, isDefaultDisplay,
mSurfaceControlProxy);
mDisplayDeviceConfig = null;
+ mActiveDisplayModeAtStartId = dynamicInfo.activeDisplayModeId;
}
@Override
@@ -238,12 +243,23 @@
}
/**
+ * Returns the boot display mode of this display.
+ * @hide
+ */
+ @Override
+ public Display.Mode getActiveDisplayModeAtStartLocked() {
+ return findMode(mActiveDisplayModeAtStartId);
+ }
+
+ /**
* Returns true if there is a change.
**/
public boolean updateDisplayPropertiesLocked(SurfaceControl.StaticDisplayInfo staticInfo,
SurfaceControl.DynamicDisplayInfo dynamicInfo,
SurfaceControl.DesiredDisplayModeSpecs modeSpecs) {
- boolean changed = updateDisplayModesLocked(
+ boolean changed =
+ updateSystemPreferredDisplayMode(dynamicInfo.preferredBootDisplayMode);
+ changed |= updateDisplayModesLocked(
dynamicInfo.supportedDisplayModes, dynamicInfo.activeDisplayModeId, modeSpecs);
changed |= updateStaticInfo(staticInfo);
changed |= updateColorModesLocked(dynamicInfo.supportedColorModes,
@@ -369,8 +385,11 @@
// For a new display, we need to initialize the default mode ID.
if (mDefaultModeId == INVALID_MODE_ID) {
- mDefaultModeId = activeRecord.mMode.getModeId();
- mDefaultModeGroup = mActiveSfDisplayMode.group;
+ mDefaultModeId = mSystemPreferredModeId != INVALID_MODE_ID
+ ? mSystemPreferredModeId : activeRecord.mMode.getModeId();
+ mDefaultModeGroup = mSystemPreferredModeId != INVALID_MODE_ID
+ ? getModeById(mSfDisplayModes, mSystemPreferredModeId).group
+ : mActiveSfDisplayMode.group;
} else if (modesAdded && activeModeChanged) {
Slog.d(TAG, "New display modes are added and the active mode has changed, "
+ "use active mode as default mode.");
@@ -531,6 +550,15 @@
return true;
}
+ private boolean updateSystemPreferredDisplayMode(int modeId) {
+ if (!mSurfaceControlProxy.getBootDisplayModeSupport()
+ || mSystemPreferredModeId == modeId) {
+ return false;
+ }
+ mSystemPreferredModeId = modeId;
+ return true;
+ }
+
private SurfaceControl.DisplayMode getModeById(SurfaceControl.DisplayMode[] supportedModes,
int modeId) {
for (SurfaceControl.DisplayMode mode : supportedModes) {
@@ -857,6 +885,16 @@
if (oldModeId != getPreferredModeId()) {
updateDeviceInfoLocked();
}
+
+ if (!mSurfaceControlProxy.getBootDisplayModeSupport()) {
+ return;
+ }
+ if (mUserPreferredMode == null) {
+ mSurfaceControlProxy.clearBootDisplayMode(getDisplayTokenLocked());
+ } else {
+ mSurfaceControlProxy.setBootDisplayMode(getDisplayTokenLocked(),
+ mUserPreferredMode.getModeId());
+ }
}
@Override
@@ -865,6 +903,11 @@
}
@Override
+ public Display.Mode getSystemPreferredDisplayModeLocked() {
+ return findMode(mSystemPreferredModeId);
+ }
+
+ @Override
public void setRequestedColorModeLocked(int colorMode) {
requestColorModeLocked(colorMode);
}
@@ -1072,6 +1115,17 @@
return matchingModeId;
}
+ // Returns a mode with id = modeId.
+ private Display.Mode findMode(int modeId) {
+ for (int i = 0; i < mSupportedModes.size(); i++) {
+ Display.Mode supportedMode = mSupportedModes.valueAt(i).mMode;
+ if (supportedMode.getModeId() == modeId) {
+ return supportedMode;
+ }
+ }
+ return null;
+ }
+
// Returns a mode with resolution (width, height) and/or refreshRate. If any one of the
// resolution or refresh-rate is valid, a mode having the valid parameters is returned.
private Display.Mode findMode(int width, int height, float refreshRate) {
@@ -1318,6 +1372,18 @@
return SurfaceControl.setActiveColorMode(displayToken, colorMode);
}
+ public boolean getBootDisplayModeSupport() {
+ return SurfaceControl.getBootDisplayModeSupport();
+ }
+
+ public void setBootDisplayMode(IBinder displayToken, int modeId) {
+ SurfaceControl.setBootDisplayMode(displayToken, modeId);
+ }
+
+ public void clearBootDisplayMode(IBinder displayToken) {
+ SurfaceControl.clearBootDisplayMode(displayToken);
+ }
+
public void setAutoLowLatencyMode(IBinder displayToken, boolean on) {
SurfaceControl.setAutoLowLatencyMode(displayToken, on);
@@ -1340,7 +1406,6 @@
return SurfaceControl.setDisplayBrightness(displayToken, sdrBacklight, sdrNits,
displayBacklight, displayNits);
}
-
}
static class BacklightAdapter {
diff --git a/services/core/java/com/android/server/display/RampAnimator.java b/services/core/java/com/android/server/display/RampAnimator.java
index 1ebd1f5a..d8672fc 100644
--- a/services/core/java/com/android/server/display/RampAnimator.java
+++ b/services/core/java/com/android/server/display/RampAnimator.java
@@ -70,7 +70,7 @@
mRate = 0;
mTargetValue = target;
mCurrentValue = target;
- mProperty.setValue(mObject, target);
+ setPropertyValue(target);
if (mAnimating) {
mAnimating = false;
cancelAnimationCallback();
@@ -125,6 +125,15 @@
mListener = listener;
}
+ /**
+ * Sets the brightness property by converting the given value from HLG space
+ * into linear space.
+ */
+ private void setPropertyValue(float val) {
+ final float linearVal = BrightnessUtils.convertGammaToLinear(val);
+ mProperty.setValue(mObject, linearVal);
+ }
+
private void postAnimationCallback() {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, mAnimationCallback, null);
}
@@ -160,9 +169,7 @@
final float oldCurrentValue = mCurrentValue;
mCurrentValue = mAnimatedValue;
if (oldCurrentValue != mCurrentValue) {
- // Convert value from HLG into linear space for the property.
- final float linearCurrentVal = BrightnessUtils.convertGammaToLinear(mCurrentValue);
- mProperty.setValue(mObject, linearCurrentVal);
+ setPropertyValue(mCurrentValue);
}
if (mTargetValue != mCurrentValue) {
postAnimationCallback();
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
index 4792821f6..79820a2 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecConfig.java
@@ -29,6 +29,7 @@
import android.os.SystemProperties;
import android.provider.Settings.Global;
import android.util.ArrayMap;
+import android.util.Slog;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -246,9 +247,11 @@
mAllowedValues.add(value);
if (mContext.getResources().getBoolean(defaultResId)) {
if (mDefaultValue != null) {
- throw new VerificationException("Invalid CEC setup for '"
- + this.getName() + "' setting. "
- + "Setting already has a default value.");
+ Slog.e(TAG,
+ "Failed to set '" + value + "' as a default for '" + this.getName()
+ + "': Setting already has a default ('" + mDefaultValue
+ + "').");
+ return;
}
mDefaultValue = value;
}
@@ -277,6 +280,11 @@
mContext = context;
mStorageAdapter = storageAdapter;
+ // IMPORTANT: when adding a config value for a particular setting, register that value AFTER
+ // the existing values for that setting. That way, defaults set in the RRO are forward
+ // compatible even if the RRO doesn't include that new value yet
+ // (e.g. because it's ported from a previous release).
+
Setting hdmiCecEnabled = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_HDMI_CEC_ENABLED,
R.bool.config_cecHdmiCecEnabled_userConfigurable);
@@ -313,15 +321,15 @@
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV,
R.bool.config_cecPowerControlModeTv_allowed,
R.bool.config_cecPowerControlModeTv_default);
- powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
- R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
- R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST,
R.bool.config_cecPowerControlModeBroadcast_allowed,
R.bool.config_cecPowerControlModeBroadcast_default);
powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_NONE,
R.bool.config_cecPowerControlModeNone_allowed,
R.bool.config_cecPowerControlModeNone_default);
+ powerControlMode.registerValue(HdmiControlManager.POWER_CONTROL_MODE_TV_AND_AUDIO_SYSTEM,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_allowed,
+ R.bool.config_cecPowerControlModeTvAndAudioSystem_default);
Setting powerStateChangeOnActiveSourceLost = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_POWER_STATE_CHANGE_ON_ACTIVE_SOURCE_LOST,
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 64b4da7..bfaa7b3 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -150,7 +150,7 @@
static final String TAG = "InputManager";
static final boolean DEBUG = false;
- private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = false;
+ private static final boolean USE_SPY_WINDOW_GESTURE_MONITORS = true;
private static final String EXCLUDED_DEVICES_PATH = "etc/excluded-input-devices.xml";
private static final String PORT_ASSOCIATIONS_PATH = "etc/input-port-associations.xml";
diff --git a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
index 4c26166..9846a2b 100644
--- a/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
+++ b/services/core/java/com/android/server/inputmethod/HandwritingEventReceiverSurface.java
@@ -19,7 +19,6 @@
import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
import android.annotation.NonNull;
-import android.graphics.Rect;
import android.os.Process;
import android.view.InputApplicationHandle;
import android.view.InputChannel;
@@ -33,8 +32,11 @@
public static final String TAG = HandwritingEventReceiverSurface.class.getSimpleName();
static final boolean DEBUG = HandwritingModeController.DEBUG;
- private final int mClientPid;
- private final int mClientUid;
+ // Place the layer below the highest layer to place it under gesture monitors. If the surface
+ // is above gesture monitors, then edge-back and swipe-up gestures won't work when this surface
+ // is intercepting.
+ // TODO(b/217538817): Specify the ordering in WM by usage.
+ private static final int HANDWRITING_SURFACE_LAYER = Integer.MAX_VALUE - 1;
private final InputApplicationHandle mApplicationHandle;
private final InputWindowHandle mWindowHandle;
@@ -44,9 +46,6 @@
HandwritingEventReceiverSurface(String name, int displayId, @NonNull SurfaceControl sc,
@NonNull InputChannel inputChannel) {
- // Initialized the window as being owned by the system.
- mClientPid = Process.myPid();
- mClientUid = Process.myUid();
mApplicationHandle = new InputApplicationHandle(null, name,
DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
@@ -57,29 +56,26 @@
mWindowHandle.name = name;
mWindowHandle.token = mClientChannel.getToken();
mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
- mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
+ | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
mWindowHandle.visible = true;
mWindowHandle.focusable = false;
mWindowHandle.hasWallpaper = false;
mWindowHandle.paused = false;
- mWindowHandle.ownerPid = mClientPid;
- mWindowHandle.ownerUid = mClientUid;
+ mWindowHandle.ownerPid = Process.myPid();
+ mWindowHandle.ownerUid = Process.myUid();
mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY
| WindowManager.LayoutParams.INPUT_FEATURE_INTERCEPTS_STYLUS;
mWindowHandle.scaleFactor = 1.0f;
mWindowHandle.trustedOverlay = true;
- mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface as crop */);
+ mWindowHandle.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
t.setInputWindowInfo(mInputSurface, mWindowHandle);
- t.setLayer(mInputSurface, Integer.MAX_VALUE);
+ t.setLayer(mInputSurface, HANDWRITING_SURFACE_LAYER);
t.setPosition(mInputSurface, 0, 0);
- // Use an arbitrarily large crop that is positioned at the origin. The crop determines the
- // bounds and the coordinate space of the input events, so it must start at the origin to
- // receive input in display space.
- // TODO(b/210039666): fix this in SurfaceFlinger and avoid the hack.
- t.setCrop(mInputSurface, new Rect(0, 0, 10000, 10000));
+ t.setCrop(mInputSurface, null /* crop to parent surface */);
t.show(mInputSurface);
t.apply();
diff --git a/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
new file mode 100644
index 0000000..6b442a6
--- /dev/null
+++ b/services/core/java/com/android/server/logcat/LogAccessConfirmationActivity.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.logcat;
+
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.os.Bundle;
+import android.os.ServiceManager;
+import android.os.logcat.ILogcatManagerService;
+import android.util.Slog;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.internal.R;
+import com.android.internal.app.AlertActivity;
+import com.android.internal.app.AlertController;
+
+
+/**
+ * This dialog is shown to the user before an activity in a harmful app is launched.
+ *
+ * See {@code PackageManager.setLogcatAppInfo} for more info.
+ */
+public class LogAccessConfirmationActivity extends AlertActivity implements
+ DialogInterface.OnClickListener {
+ private static final String TAG = LogAccessConfirmationActivity.class.getSimpleName();
+
+ private String mPackageName;
+ private IntentSender mTarget;
+ private final ILogcatManagerService mLogcatManagerService =
+ ILogcatManagerService.Stub.asInterface(ServiceManager.getService("logcat"));
+
+ private int mUid;
+ private int mGid;
+ private int mPid;
+ private int mFd;
+
+ private static final String EXTRA_UID = "uid";
+ private static final String EXTRA_GID = "gid";
+ private static final String EXTRA_PID = "pid";
+ private static final String EXTRA_FD = "fd";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ final Intent intent = getIntent();
+ mPackageName = intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ mUid = intent.getIntExtra("uid", 0);
+ mGid = intent.getIntExtra("gid", 0);
+ mPid = intent.getIntExtra("pid", 0);
+ mFd = intent.getIntExtra("fd", 0);
+
+ final AlertController.AlertParams p = mAlertParams;
+ p.mTitle = getString(R.string.log_access_confirmation_title);
+ p.mView = createView();
+
+ p.mPositiveButtonText = getString(R.string.log_access_confirmation_allow);
+ p.mPositiveButtonListener = this;
+ p.mNegativeButtonText = getString(R.string.log_access_confirmation_deny);
+ p.mNegativeButtonListener = this;
+
+ mAlert.installContent(mAlertParams);
+ }
+
+ private View createView() {
+ final View view = getLayoutInflater().inflate(R.layout.harmful_app_warning_dialog,
+ null /*root*/);
+ ((TextView) view.findViewById(R.id.app_name_text))
+ .setText(mPackageName);
+ ((TextView) view.findViewById(R.id.message))
+ .setText(getIntent().getExtras().getString("body"));
+ return view;
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ try {
+ mLogcatManagerService.approve(mUid, mGid, mPid, mFd);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+ }
+ finish();
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ try {
+ mLogcatManagerService.decline(mUid, mGid, mPid, mFd);
+ } catch (Throwable t) {
+ Slog.e(TAG, "Could not start the LogcatManagerService.", t);
+ }
+ finish();
+ break;
+ }
+ }
+
+ /**
+ * Create the Intent for a LogAccessConfirmationActivity.
+ */
+ public static Intent createIntent(Context context, String targetPackageName,
+ IntentSender target, int uid, int gid, int pid, int fd) {
+ final Intent intent = new Intent();
+ intent.setClass(context, LogAccessConfirmationActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, targetPackageName);
+ intent.putExtra(EXTRA_UID, uid);
+ intent.putExtra(EXTRA_GID, gid);
+ intent.putExtra(EXTRA_PID, pid);
+ intent.putExtra(EXTRA_FD, fd);
+
+ return intent;
+ }
+
+}
diff --git a/services/core/java/com/android/server/logcat/LogcatManagerService.java b/services/core/java/com/android/server/logcat/LogcatManagerService.java
index ff6372ae..140c6d4 100644
--- a/services/core/java/com/android/server/logcat/LogcatManagerService.java
+++ b/services/core/java/com/android/server/logcat/LogcatManagerService.java
@@ -16,20 +16,36 @@
package com.android.server.logcat;
+import android.annotation.NonNull;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.ActivityManagerInternal;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.os.ILogd;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.os.UserHandle;
import android.os.logcat.ILogcatManagerService;
import android.util.Slog;
+import com.android.internal.R;
+import com.android.internal.notification.SystemNotificationChannels;
+import com.android.internal.util.ArrayUtils;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
+import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
- * Service responsible for manage the access to Logcat.
+ * Service responsible for managing the access to Logcat.
*/
public final class LogcatManagerService extends SystemService {
@@ -38,6 +54,43 @@
private final BinderService mBinderService;
private final ExecutorService mThreadExecutor;
private ILogd mLogdService;
+ private NotificationManager mNotificationManager;
+ private @NonNull ActivityManager mActivityManager;
+ private ActivityManagerInternal mActivityManagerInternal;
+ private static final int MAX_UID_IMPORTANCE_COUNT_LISTENER = 2;
+ private static int sUidImportanceListenerCount = 0;
+ private static final int AID_SHELL_UID = 2000;
+
+ // TODO This allowlist is just a temporary workaround for the tests:
+ // FrameworksServicesTests
+ // PlatformRuleTests
+ // After adapting the test suites, the allowlist will be removed in
+ // the upcoming bug fix patches.
+ private static final String[] ALLOWABLE_TESTING_PACKAGES = {
+ "android.platform.test.rule.tests",
+ "com.android.frameworks.servicestests"
+ };
+
+ // TODO Same as the above ALLOWABLE_TESTING_PACKAGES.
+ private boolean isAllowableTestingPackage(int uid) {
+ PackageManager pm = mContext.getPackageManager();
+
+ String[] packageNames = pm.getPackagesForUid(uid);
+
+ if (ArrayUtils.isEmpty(packageNames)) {
+ return false;
+ }
+
+ for (String name : packageNames) {
+ Slog.e(TAG, "isAllowableTestingPackage: " + name);
+
+ if (Arrays.asList(ALLOWABLE_TESTING_PACKAGES).contains(name)) {
+ return true;
+ }
+ }
+
+ return false;
+ };
private final class BinderService extends ILogcatManagerService.Stub {
@Override
@@ -51,6 +104,197 @@
// the logd data access is finished.
mThreadExecutor.execute(new LogdMonitor(uid, gid, pid, fd, false));
}
+
+ @Override
+ public void approve(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().approve(uid, gid, pid, fd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+ public void decline(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().decline(uid, gid, pid, fd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private ILogd getLogdService() {
+ synchronized (LogcatManagerService.this) {
+ if (mLogdService == null) {
+ LogcatManagerService.this.addLogdService();
+ }
+ return mLogdService;
+ }
+ }
+
+ private String getBodyString(Context context, String callingPackage, int uid) {
+ PackageManager pm = context.getPackageManager();
+ try {
+ return context.getString(
+ com.android.internal.R.string.log_access_confirmation_body,
+ pm.getApplicationInfoAsUser(callingPackage, PackageManager.MATCH_DIRECT_BOOT_AUTO,
+ UserHandle.getUserId(uid)).loadLabel(pm));
+ } catch (NameNotFoundException e) {
+ // App name is unknown.
+ return null;
+ }
+ }
+
+ private void sendNotification(int notificationId, String clientInfo, int uid, int gid, int pid,
+ int fd) {
+
+ final ActivityManagerInternal activityManagerInternal =
+ LocalServices.getService(ActivityManagerInternal.class);
+
+ PackageManager pm = mContext.getPackageManager();
+ String packageName = activityManagerInternal.getPackageNameByPid(pid);
+ if (packageName != null) {
+ String notificationBody = getBodyString(mContext, packageName, uid);
+
+ final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+ packageName, null, uid, gid, pid, fd);
+
+ if (notificationBody == null) {
+ // Decline the logd access if the nofitication body is unknown
+ Slog.e(TAG, "Unknown notification body, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ // TODO Next version will replace notification with dialogue
+ // per UX guidance.
+ generateNotificationWithBodyContent(notificationId, clientInfo, notificationBody,
+ mIntent);
+ return;
+
+ }
+
+ String[] packageNames = pm.getPackagesForUid(uid);
+
+ if (ArrayUtils.isEmpty(packageNames)) {
+ // Decline the logd access if the app name is unknown
+ Slog.e(TAG, "Unknown calling package name, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ String firstPackageName = packageNames[0];
+
+ if (firstPackageName == null || firstPackageName.length() == 0) {
+ // Decline the logd access if the package name from uid is unknown
+ Slog.e(TAG, "Unknown calling package name, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ String notificationBody = getBodyString(mContext, firstPackageName, uid);
+
+ final Intent mIntent = LogAccessConfirmationActivity.createIntent(mContext,
+ firstPackageName, null, uid, gid, pid, fd);
+
+ if (notificationBody == null) {
+ Slog.e(TAG, "Unknown notification body, declining the logd access");
+ declineLogdAccess(uid, gid, pid, fd);
+ return;
+ }
+
+ // TODO Next version will replace notification with dialogue
+ // per UX guidance.
+ generateNotificationWithBodyContent(notificationId, clientInfo,
+ notificationBody, mIntent);
+ }
+
+ private void declineLogdAccess(int uid, int gid, int pid, int fd) {
+ try {
+ getLogdService().decline(uid, gid, pid, fd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
+ }
+ }
+
+ private void generateNotificationWithBodyContent(int notificationId, String clientInfo,
+ String notificationBody, Intent intent) {
+ final Notification.Builder notificationBuilder = new Notification.Builder(
+ mContext,
+ SystemNotificationChannels.ACCESSIBILITY_SECURITY_POLICY);
+ intent.setFlags(
+ Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
+ intent.setIdentifier(String.valueOf(notificationId) + clientInfo);
+ intent.putExtra("body", notificationBody);
+
+ notificationBuilder
+ .setSmallIcon(R.drawable.ic_info)
+ .setContentTitle(
+ mContext.getString(R.string.log_access_confirmation_title))
+ .setContentText(notificationBody)
+ .setContentIntent(
+ PendingIntent.getActivity(mContext, 0, intent,
+ PendingIntent.FLAG_IMMUTABLE))
+ .setTicker(mContext.getString(R.string.log_access_confirmation_title))
+ .setOnlyAlertOnce(true)
+ .setAutoCancel(true);
+ mNotificationManager.notify(notificationId, notificationBuilder.build());
+ }
+
+ /**
+ * A class which watches an uid for background access and notifies the logdMonitor when
+ * the package status becomes foreground (importance change)
+ */
+ private class UidImportanceListener implements ActivityManager.OnUidImportanceListener {
+ private final int mExpectedUid;
+ private final int mExpectedGid;
+ private final int mExpectedPid;
+ private final int mExpectedFd;
+ private int mExpectedImportance;
+ private int mCurrentImportance = RunningAppProcessInfo.IMPORTANCE_GONE;
+
+ UidImportanceListener(int uid, int gid, int pid, int fd, int importance) {
+ mExpectedUid = uid;
+ mExpectedGid = gid;
+ mExpectedPid = pid;
+ mExpectedFd = fd;
+ mExpectedImportance = importance;
+ }
+
+ @Override
+ public void onUidImportance(int uid, int importance) {
+ if (uid == mExpectedUid) {
+ mCurrentImportance = importance;
+
+ /**
+ * 1) If the process status changes to foreground, send a notification
+ * for user consent.
+ * 2) If the process status remains background, we decline logd access request.
+ **/
+ if (importance <= RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE) {
+ String clientInfo = getClientInfo(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+ sendNotification(0, clientInfo, uid, mExpectedGid, mExpectedPid,
+ mExpectedFd);
+ mActivityManager.removeOnUidImportanceListener(this);
+
+ synchronized (LogcatManagerService.this) {
+ sUidImportanceListenerCount--;
+ }
+ } else {
+ try {
+ getLogdService().decline(uid, mExpectedGid, mExpectedPid, mExpectedFd);
+ } catch (RemoteException ex) {
+ Slog.e(TAG, "Fails to call remote functions ", ex);
+ }
+ }
+ }
+ }
+ }
+
+ private static String getClientInfo(int uid, int gid, int pid, int fd) {
+ return "UID=" + Integer.toString(uid) + " GID=" + Integer.toString(gid) + " PID="
+ + Integer.toString(pid) + " FD=" + Integer.toString(fd);
}
private class LogdMonitor implements Runnable {
@@ -74,9 +318,7 @@
}
/**
- * The current version grant the permission by default.
- * And track the logd access.
- * The next version will generate a prompt for users.
+ * LogdMonitor generates a prompt for users.
* The users decide whether the logd access is allowed.
*/
@Override
@@ -86,10 +328,61 @@
}
if (mStart) {
- try {
- mLogdService.approve(mUid, mGid, mPid, mFd);
- } catch (RemoteException ex) {
- Slog.e(TAG, "Fails to call remote functions ", ex);
+
+ // TODO See the comments of ALLOWABLE_TESTING_PACKAGES.
+ if (isAllowableTestingPackage(mUid)) {
+ try {
+ getLogdService().approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ // If the access request is coming from adb shell, approve the logd access
+ if (mUid == AID_SHELL_UID) {
+ try {
+ getLogdService().approve(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ return;
+ }
+
+ final int procState = LocalServices.getService(ActivityManagerInternal.class)
+ .getUidProcessState(mUid);
+ // If the process is foreground, send a notification for user consent
+ if (procState <= ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE) {
+ String clientInfo = getClientInfo(mUid, mGid, mPid, mFd);
+ sendNotification(0, clientInfo, mUid, mGid, mPid, mFd);
+ } else {
+ /**
+ * If the process is background, add a background process change listener and
+ * monitor if the process status changes.
+ * To avoid clients registering multiple listeners, we limit the number of
+ * maximum listeners to MAX_UID_IMPORTANCE_COUNT_LISTENER.
+ **/
+ if (mActivityManager == null) {
+ return;
+ }
+
+ synchronized (LogcatManagerService.this) {
+ if (sUidImportanceListenerCount < MAX_UID_IMPORTANCE_COUNT_LISTENER) {
+ // Trigger addOnUidImportanceListener when there is an update from
+ // the importance of the process
+ mActivityManager.addOnUidImportanceListener(new UidImportanceListener(
+ mUid, mGid, mPid, mFd,
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE),
+ RunningAppProcessInfo.IMPORTANCE_FOREGROUND_SERVICE);
+ sUidImportanceListenerCount++;
+ } else {
+ try {
+ getLogdService().decline(mUid, mGid, mPid, mFd);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ }
}
}
}
@@ -100,6 +393,8 @@
mContext = context;
mBinderService = new BinderService();
mThreadExecutor = Executors.newCachedThreadPool();
+ mActivityManager = context.getSystemService(ActivityManager.class);
+ mNotificationManager = mContext.getSystemService(NotificationManager.class);
}
@Override
@@ -114,5 +409,4 @@
private void addLogdService() {
mLogdService = ILogd.Stub.asInterface(ServiceManager.getService("logd"));
}
-
}
diff --git a/services/core/java/com/android/server/media/BluetoothRouteProvider.java b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
index 91de9e5..728782c 100644
--- a/services/core/java/com/android/server/media/BluetoothRouteProvider.java
+++ b/services/core/java/com/android/server/media/BluetoothRouteProvider.java
@@ -45,7 +45,6 @@
import com.android.internal.R;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -448,15 +447,16 @@
case BluetoothProfile.A2DP:
mA2dpProfile = (BluetoothA2dp) proxy;
// It may contain null.
- activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice());
+ activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.A2DP);
break;
case BluetoothProfile.HEARING_AID:
mHearingAidProfile = (BluetoothHearingAid) proxy;
- activeDevices = mHearingAidProfile.getActiveDevices();
+ activeDevices = mBluetoothAdapter.getActiveDevices(
+ BluetoothProfile.HEARING_AID);
break;
case BluetoothProfile.LE_AUDIO:
mLeAudioProfile = (BluetoothLeAudio) proxy;
- activeDevices = mLeAudioProfile.getActiveDevices();
+ activeDevices = mBluetoothAdapter.getActiveDevices(BluetoothProfile.LE_AUDIO);
break;
default:
return;
diff --git a/services/core/java/com/android/server/notification/NotificationRecord.java b/services/core/java/com/android/server/notification/NotificationRecord.java
index 96d7521..70e968f 100644
--- a/services/core/java/com/android/server/notification/NotificationRecord.java
+++ b/services/core/java/com/android/server/notification/NotificationRecord.java
@@ -201,6 +201,10 @@
private boolean mIsAppImportanceLocked;
private ArraySet<Uri> mGrantableUris;
+ // Storage for phone numbers that were found to be associated with
+ // contacts in this notification.
+ private ArraySet<String> mPhoneNumbers;
+
// Whether this notification record should have an update logged the next time notifications
// are sorted.
private boolean mPendingLogUpdate = false;
@@ -1547,6 +1551,26 @@
return mPendingLogUpdate;
}
+ /**
+ * Merge the given set of phone numbers into the list of phone numbers that
+ * are cached on this notification record.
+ */
+ public void mergePhoneNumbers(ArraySet<String> phoneNumbers) {
+ // if the given phone numbers are null or empty then don't do anything
+ if (phoneNumbers == null || phoneNumbers.size() == 0) {
+ return;
+ }
+ // initialize if not already
+ if (mPhoneNumbers == null) {
+ mPhoneNumbers = new ArraySet<>();
+ }
+ mPhoneNumbers.addAll(phoneNumbers);
+ }
+
+ public ArraySet<String> getPhoneNumbers() {
+ return mPhoneNumbers;
+ }
+
@VisibleForTesting
static final class Light {
public final int color;
diff --git a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
index d7bc3bb..dc4d04f 100644
--- a/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
+++ b/services/core/java/com/android/server/notification/ValidateNotificationPeople.java
@@ -68,7 +68,10 @@
private static final boolean ENABLE_PEOPLE_VALIDATOR = true;
private static final String SETTING_ENABLE_PEOPLE_VALIDATOR =
"validate_notification_people_enabled";
- private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.STARRED };
+ private static final String[] LOOKUP_PROJECTION = { Contacts._ID, Contacts.LOOKUP_KEY,
+ Contacts.STARRED, Contacts.HAS_PHONE_NUMBER };
+ private static final String[] PHONE_LOOKUP_PROJECTION =
+ { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER };
private static final int MAX_PEOPLE = 10;
private static final int PEOPLE_CACHE_SIZE = 200;
@@ -409,6 +412,35 @@
return lookupResult;
}
+ @VisibleForTesting
+ // Performs a contacts search using searchContacts, and then follows up by looking up
+ // any phone numbers associated with the resulting contact information and merge those
+ // into the lookup result as well. Will have no additional effect if the contact does
+ // not have any phone numbers.
+ LookupResult searchContactsAndLookupNumbers(Context context, Uri lookupUri) {
+ LookupResult lookupResult = searchContacts(context, lookupUri);
+ String phoneLookupKey = lookupResult.getPhoneLookupKey();
+ if (phoneLookupKey != null) {
+ String selection = Contacts.LOOKUP_KEY + " = ?";
+ String[] selectionArgs = new String[] { phoneLookupKey };
+ try (Cursor cursor = context.getContentResolver().query(
+ ContactsContract.CommonDataKinds.Phone.CONTENT_URI, PHONE_LOOKUP_PROJECTION,
+ selection, selectionArgs, /* sortOrder= */ null)) {
+ if (cursor == null) {
+ Slog.w(TAG, "Cursor is null when querying contact phone number.");
+ return lookupResult;
+ }
+
+ while (cursor.moveToNext()) {
+ lookupResult.mergePhoneNumber(cursor);
+ }
+ } catch (Throwable t) {
+ Slog.w(TAG, "Problem getting content resolver or querying phone numbers.", t);
+ }
+ }
+ return lookupResult;
+ }
+
private void addWorkContacts(LookupResult lookupResult, Context context, Uri corpLookupUri) {
final int workUserId = findWorkUserId(context);
if (workUserId == -1) {
@@ -454,6 +486,9 @@
private final long mExpireMillis;
private float mAffinity = NONE;
+ private boolean mHasPhone = false;
+ private String mPhoneLookupKey = null;
+ private ArraySet<String> mPhoneNumbers = new ArraySet<>();
public LookupResult() {
mExpireMillis = System.currentTimeMillis() + CONTACT_REFRESH_MILLIS;
@@ -473,6 +508,15 @@
Slog.i(TAG, "invalid cursor: no _ID");
}
+ // Lookup key for potentially looking up contact phone number later
+ final int lookupKeyIdx = cursor.getColumnIndex(Contacts.LOOKUP_KEY);
+ if (lookupKeyIdx >= 0) {
+ mPhoneLookupKey = cursor.getString(lookupKeyIdx);
+ if (DEBUG) Slog.d(TAG, "contact LOOKUP_KEY is: " + mPhoneLookupKey);
+ } else {
+ if (DEBUG) Slog.d(TAG, "invalid cursor: no LOOKUP_KEY");
+ }
+
// Starred
final int starIdx = cursor.getColumnIndex(Contacts.STARRED);
if (starIdx >= 0) {
@@ -484,6 +528,39 @@
} else {
if (DEBUG) Slog.d(TAG, "invalid cursor: no STARRED");
}
+
+ // whether a phone number is present
+ final int hasPhoneIdx = cursor.getColumnIndex(Contacts.HAS_PHONE_NUMBER);
+ if (hasPhoneIdx >= 0) {
+ mHasPhone = cursor.getInt(hasPhoneIdx) != 0;
+ if (DEBUG) Slog.d(TAG, "contact HAS_PHONE_NUMBER is: " + mHasPhone);
+ } else {
+ if (DEBUG) Slog.d(TAG, "invalid cursor: no HAS_PHONE_NUMBER");
+ }
+ }
+
+ // Returns the phone lookup key that is cached in this result, or null
+ // if the contact has no known phone info.
+ public String getPhoneLookupKey() {
+ if (!mHasPhone) {
+ return null;
+ }
+ return mPhoneLookupKey;
+ }
+
+ // Merge phone number found in this lookup and store it in mPhoneNumbers.
+ public void mergePhoneNumber(Cursor cursor) {
+ final int phoneNumIdx = cursor.getColumnIndex(
+ ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER);
+ if (phoneNumIdx >= 0) {
+ mPhoneNumbers.add(cursor.getString(phoneNumIdx));
+ } else {
+ if (DEBUG) Slog.d(TAG, "invalid cursor: no NORMALIZED_NUMBER");
+ }
+ }
+
+ public ArraySet<String> getPhoneNumbers() {
+ return mPhoneNumbers;
}
private boolean isExpired() {
@@ -509,6 +586,7 @@
// Amount of time to wait for a result from the contacts db before rechecking affinity.
private static final long LOOKUP_TIME = 1000;
private float mContactAffinity = NONE;
+ private ArraySet<String> mPhoneNumbers = null;
private NotificationRecord mRecord;
private PeopleRankingReconsideration(Context context, String key,
@@ -543,7 +621,9 @@
lookupResult = resolveEmailContact(mContext, uri.getSchemeSpecificPart());
} else if (handle.startsWith(Contacts.CONTENT_LOOKUP_URI.toString())) {
if (DEBUG) Slog.d(TAG, "checking lookup URI: " + handle);
- lookupResult = searchContacts(mContext, uri);
+ // only look up phone number if this is a contact lookup uri and thus isn't
+ // already directly a phone number.
+ lookupResult = searchContactsAndLookupNumbers(mContext, uri);
} else {
lookupResult = new LookupResult(); // invalid person for the cache
if (!"name".equals(uri.getScheme())) {
@@ -561,6 +641,13 @@
Slog.d(TAG, "lookup contactAffinity is " + lookupResult.getAffinity());
}
mContactAffinity = Math.max(mContactAffinity, lookupResult.getAffinity());
+ // merge any phone numbers found in this lookup result
+ if (lookupResult.getPhoneNumbers() != null) {
+ if (mPhoneNumbers == null) {
+ mPhoneNumbers = new ArraySet<>();
+ }
+ mPhoneNumbers.addAll(lookupResult.getPhoneNumbers());
+ }
} else {
if (DEBUG) Slog.d(TAG, "lookupResult is null");
}
@@ -581,6 +668,7 @@
float affinityBound = operand.getContactAffinity();
operand.setContactAffinity(Math.max(mContactAffinity, affinityBound));
if (VERBOSE) Slog.i(TAG, "final affinity: " + operand.getContactAffinity());
+ operand.mergePhoneNumbers(mPhoneNumbers);
}
public float getContactAffinity() {
diff --git a/services/core/java/com/android/server/notification/ZenModeFiltering.java b/services/core/java/com/android/server/notification/ZenModeFiltering.java
index 29aad63..d04b331 100644
--- a/services/core/java/com/android/server/notification/ZenModeFiltering.java
+++ b/services/core/java/com/android/server/notification/ZenModeFiltering.java
@@ -33,6 +33,7 @@
import android.telephony.PhoneNumberUtils;
import android.telephony.TelephonyManager;
import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Slog;
import com.android.internal.messages.nano.SystemMessageProto;
@@ -140,7 +141,7 @@
}
protected void recordCall(NotificationRecord record) {
- REPEAT_CALLERS.recordCall(mContext, extras(record));
+ REPEAT_CALLERS.recordCall(mContext, extras(record), record.getPhoneNumbers());
}
/**
@@ -351,7 +352,8 @@
private final ArrayMap<String, Long> mOtherCalls = new ArrayMap<>();
private int mThresholdMinutes;
- private synchronized void recordCall(Context context, Bundle extras) {
+ private synchronized void recordCall(Context context, Bundle extras,
+ ArraySet<String> phoneNumbers) {
setThresholdMinutes(context);
if (mThresholdMinutes <= 0 || extras == null) return;
final String[] extraPeople = ValidateNotificationPeople.getExtraPeople(extras);
@@ -359,7 +361,7 @@
final long now = System.currentTimeMillis();
cleanUp(mTelCalls, now);
cleanUp(mOtherCalls, now);
- recordCallers(extraPeople, now);
+ recordCallers(extraPeople, phoneNumbers, now);
}
private synchronized boolean isRepeat(Context context, Bundle extras) {
@@ -407,7 +409,8 @@
}
}
- private synchronized void recordCallers(String[] people, long now) {
+ private synchronized void recordCallers(String[] people, ArraySet<String> phoneNumbers,
+ long now) {
for (int i = 0; i < people.length; i++) {
String person = people[i];
if (person == null) continue;
@@ -428,6 +431,14 @@
mOtherCalls.put(person, now);
}
}
+
+ // record any additional numbers from the notification record if
+ // provided; these are in the format of just a phone number string
+ if (phoneNumbers != null) {
+ for (String num : phoneNumbers) {
+ mTelCalls.put(num, now);
+ }
+ }
}
private synchronized boolean checkCallers(Context context, String[] people) {
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index 2e9ad50..2d87099 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -32,9 +32,6 @@
import android.content.pm.PackageInstaller;
import android.content.pm.PackageManager;
import android.content.pm.SigningDetails;
-import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
-import com.android.server.pm.pkg.component.ParsedApexSystemService;
import android.content.pm.parsing.result.ParseResult;
import android.content.pm.parsing.result.ParseTypeImpl;
import android.os.Binder;
@@ -59,6 +56,9 @@
import com.android.server.pm.parsing.PackageParser2;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.ParsedPackage;
+import com.android.server.pm.pkg.component.ParsedApexSystemService;
+import com.android.server.pm.pkg.parsing.PackageInfoWithoutStateUtils;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import com.android.server.utils.TimingsTraceAndSlog;
import com.google.android.collect.Lists;
@@ -414,9 +414,11 @@
throws PackageManagerException;
/**
- * Get a map of system services defined in an apex mapped to the jar files they reside in.
+ * Get a list of apex system services implemented in an apex.
+ *
+ * <p>The list is sorted by initOrder for consistency.
*/
- public abstract Map<String, String> getApexSystemServices();
+ public abstract List<ApexSystemServiceInfo> getApexSystemServices();
/**
* Dumps various state information to the provided {@link PrintWriter} object.
@@ -449,7 +451,7 @@
* Map of all apex system services to the jar files they are contained in.
*/
@GuardedBy("mLock")
- private Map<String, String> mApexSystemServices = new ArrayMap<>();
+ private List<ApexSystemServiceInfo> mApexSystemServices = new ArrayList<>();
/**
* Contains the list of {@code packageName}s of apks-in-apex for given
@@ -605,14 +607,19 @@
}
String name = service.getName();
- if (mApexSystemServices.containsKey(name)) {
- throw new IllegalStateException(String.format(
- "Duplicate apex-system-service %s from %s, %s",
- name, mApexSystemServices.get(name), service.getJarPath()));
+ for (ApexSystemServiceInfo info : mApexSystemServices) {
+ if (info.getName().equals(name)) {
+ throw new IllegalStateException(String.format(
+ "Duplicate apex-system-service %s from %s, %s",
+ name, info.mJarPath, service.getJarPath()));
+ }
}
- mApexSystemServices.put(name, service.getJarPath());
+ ApexSystemServiceInfo info = new ApexSystemServiceInfo(
+ service.getName(), service.getJarPath(), service.getInitOrder());
+ mApexSystemServices.add(info);
}
+ Collections.sort(mApexSystemServices);
mPackageNameToApexModuleName.put(packageInfo.packageName, ai.moduleName);
if (ai.isActive) {
if (activePackagesSet.contains(packageInfo.packageName)) {
@@ -1133,7 +1140,7 @@
}
@Override
- public Map<String, String> getApexSystemServices() {
+ public List<ApexSystemServiceInfo> getApexSystemServices() {
synchronized (mLock) {
Preconditions.checkState(mApexSystemServices != null,
"APEX packages have not been scanned");
@@ -1423,10 +1430,10 @@
}
@Override
- public Map<String, String> getApexSystemServices() {
+ public List<ApexSystemServiceInfo> getApexSystemServices() {
// TODO(satayev): we can't really support flattened apex use case, and need to migrate
// the manifest entries into system's manifest asap.
- return Collections.emptyMap();
+ return Collections.emptyList();
}
@Override
diff --git a/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java
new file mode 100644
index 0000000..f75ba6d
--- /dev/null
+++ b/services/core/java/com/android/server/pm/ApexSystemServiceInfo.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.pm;
+
+import android.annotation.Nullable;
+
+/**
+ * A helper class that contains information about apex-system-service to be used within system
+ * server process.
+ */
+public final class ApexSystemServiceInfo implements Comparable<ApexSystemServiceInfo> {
+
+ final String mName;
+ @Nullable
+ final String mJarPath;
+ final int mInitOrder;
+
+ public ApexSystemServiceInfo(String name, String jarPath, int initOrder) {
+ this.mName = name;
+ this.mJarPath = jarPath;
+ this.mInitOrder = initOrder;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getJarPath() {
+ return mJarPath;
+ }
+
+ public int getInitOrder() {
+ return mInitOrder;
+ }
+
+ @Override
+ public int compareTo(ApexSystemServiceInfo other) {
+ if (mInitOrder == other.mInitOrder) {
+ return mName.compareTo(other.mName);
+ }
+ // higher initOrder values take precedence
+ return -Integer.compare(mInitOrder, other.mInitOrder);
+ }
+}
diff --git a/services/core/java/com/android/server/pm/DexOptHelper.java b/services/core/java/com/android/server/pm/DexOptHelper.java
index ba89916..53eb9cf 100644
--- a/services/core/java/com/android/server/pm/DexOptHelper.java
+++ b/services/core/java/com/android/server/pm/DexOptHelper.java
@@ -293,7 +293,8 @@
public ArraySet<String> getOptimizablePackages() {
ArraySet<String> pkgs = new ArraySet<>();
mPm.forEachPackageState(packageState -> {
- if (mPm.mPackageDexOptimizer.canOptimizePackage(packageState.getPkg())) {
+ final AndroidPackage pkg = packageState.getPkg();
+ if (pkg != null && mPm.mPackageDexOptimizer.canOptimizePackage(pkg)) {
pkgs.add(packageState.getPackageName());
}
});
diff --git a/services/core/java/com/android/server/pm/PackageDexOptimizer.java b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
index a5b42f0..69d4987 100644
--- a/services/core/java/com/android/server/pm/PackageDexOptimizer.java
+++ b/services/core/java/com/android/server/pm/PackageDexOptimizer.java
@@ -182,7 +182,7 @@
mInjector = injector;
}
- boolean canOptimizePackage(AndroidPackage pkg) {
+ boolean canOptimizePackage(@NonNull AndroidPackage pkg) {
// We do not dexopt a package with no code.
// Note that the system package is marked as having no code, however we can
// still optimize it via dexoptSystemServerPath.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index 79c5ea2..edc0e3d 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -51,6 +51,7 @@
import android.content.pm.PermissionInfo;
import android.content.pm.permission.SplitPermissionInfoParcelable;
import android.os.Binder;
+import android.os.Build;
import android.os.IBinder;
import android.os.Process;
import android.os.RemoteException;
@@ -613,11 +614,12 @@
int granted = PermissionManagerService.this.checkUidPermission(uid,
POST_NOTIFICATIONS);
AndroidPackage pkg = mPackageManagerInt.getPackage(uid);
- if (granted != PermissionManager.PERMISSION_GRANTED) {
+ if (granted != PackageManager.PERMISSION_GRANTED
+ && pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) {
int flags = PermissionManagerService.this.getPermissionFlags(pkg.getPackageName(),
POST_NOTIFICATIONS, UserHandle.getUserId(uid));
if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
- return PermissionManager.PERMISSION_GRANTED;
+ return PackageManager.PERMISSION_GRANTED;
}
}
return granted;
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
index 586d2c4..cf478b1 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemService.java
@@ -34,4 +34,7 @@
@Nullable
String getMaxSdkVersion();
+
+ int getInitOrder();
+
}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
index 1e427d0..167aba3 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
@@ -48,18 +48,18 @@
@Nullable
private String maxSdkVersion;
+ private int initOrder;
+
public ParsedApexSystemServiceImpl() {
}
-
-
// Code below generated by codegen v1.0.23.
//
// DO NOT MODIFY!
// CHECKSTYLE:OFF Generated code
//
// To regenerate run:
- // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java
+ // $ codegen $ANDROID_BUILD_TOP/frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java
//
// To exclude the generated code from IntelliJ auto-formatting enable (one-time):
// Settings > Editor > Code Style > Formatter Control
@@ -71,13 +71,15 @@
@NonNull String name,
@Nullable String jarPath,
@Nullable String minSdkVersion,
- @Nullable String maxSdkVersion) {
+ @Nullable String maxSdkVersion,
+ int initOrder) {
this.name = name;
com.android.internal.util.AnnotationValidations.validate(
NonNull.class, null, name);
this.jarPath = jarPath;
this.minSdkVersion = minSdkVersion;
this.maxSdkVersion = maxSdkVersion;
+ this.initOrder = initOrder;
// onConstructed(); // You can define this method to get a callback
}
@@ -103,6 +105,11 @@
}
@DataClass.Generated.Member
+ public int getInitOrder() {
+ return initOrder;
+ }
+
+ @DataClass.Generated.Member
public @NonNull ParsedApexSystemServiceImpl setName(@NonNull String value) {
name = value;
com.android.internal.util.AnnotationValidations.validate(
@@ -129,6 +136,12 @@
}
@DataClass.Generated.Member
+ public @NonNull ParsedApexSystemServiceImpl setInitOrder( int value) {
+ initOrder = value;
+ return this;
+ }
+
+ @DataClass.Generated.Member
static Parcelling<String> sParcellingForName =
Parcelling.Cache.get(
Parcelling.BuiltIn.ForInternedString.class);
@@ -187,6 +200,7 @@
sParcellingForJarPath.parcel(jarPath, dest, flags);
sParcellingForMinSdkVersion.parcel(minSdkVersion, dest, flags);
sParcellingForMaxSdkVersion.parcel(maxSdkVersion, dest, flags);
+ dest.writeInt(initOrder);
}
@Override
@@ -205,6 +219,7 @@
String _jarPath = sParcellingForJarPath.unparcel(in);
String _minSdkVersion = sParcellingForMinSdkVersion.unparcel(in);
String _maxSdkVersion = sParcellingForMaxSdkVersion.unparcel(in);
+ int _initOrder = in.readInt();
this.name = _name;
com.android.internal.util.AnnotationValidations.validate(
@@ -212,6 +227,7 @@
this.jarPath = _jarPath;
this.minSdkVersion = _minSdkVersion;
this.maxSdkVersion = _maxSdkVersion;
+ this.initOrder = _initOrder;
// onConstructed(); // You can define this method to get a callback
}
@@ -231,10 +247,10 @@
};
@DataClass.Generated(
- time = 1641431950080L,
+ time = 1643723578605L,
codegenVersion = "1.0.23",
- sourceFile = "frameworks/base/core/java/android/content/pm/parsing/component/ParsedApexSystemServiceImpl.java",
- inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [android.content.pm.parsing.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
+ sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceImpl.java",
+ inputSignatures = "private @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.NonNull java.lang.String name\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String jarPath\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String minSdkVersion\nprivate @com.android.internal.util.DataClass.ParcelWith(com.android.internal.util.Parcelling.BuiltIn.ForInternedString.class) @android.annotation.Nullable java.lang.String maxSdkVersion\nprivate int initOrder\nclass ParsedApexSystemServiceImpl extends java.lang.Object implements [com.android.server.pm.pkg.component.ParsedApexSystemService, android.os.Parcelable]\n@com.android.internal.util.DataClass(genGetters=true, genAidl=false, genSetters=true, genParcelable=true)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
index 38a6f5a35..ed9aa2e 100644
--- a/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
+++ b/services/core/java/com/android/server/pm/pkg/component/ParsedApexSystemServiceUtils.java
@@ -53,10 +53,13 @@
R.styleable.AndroidManifestApexSystemService_minSdkVersion);
String maxSdkVersion = sa.getString(
R.styleable.AndroidManifestApexSystemService_maxSdkVersion);
+ int initOrder = sa.getInt(R.styleable.AndroidManifestApexSystemService_initOrder, 0);
systemService.setName(className)
.setMinSdkVersion(minSdkVersion)
- .setMaxSdkVersion(maxSdkVersion);
+ .setMaxSdkVersion(maxSdkVersion)
+ .setInitOrder(initOrder);
+
if (!TextUtils.isEmpty(jarPath)) {
systemService.setJarPath(jarPath);
}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
index f519ced..243efb5 100644
--- a/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
+++ b/services/core/java/com/android/server/security/AttestationVerificationManagerService.java
@@ -16,6 +16,8 @@
package com.android.server.security;
+import static android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
import static android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN;
import android.content.Context;
@@ -76,10 +78,24 @@
private void verifyAttestationForAllVerifiers(
AttestationProfile profile, int localBindingType, Bundle requirements,
byte[] attestation, AndroidFuture<IVerificationResult> resultCallback) {
- // TODO(b/201696614): Implement
IVerificationResult result = new IVerificationResult();
- result.resultCode = RESULT_UNKNOWN;
+ // TODO(b/201696614): Implement
result.token = null;
+ switch (profile.getAttestationProfileId()) {
+ case PROFILE_SELF_TRUSTED:
+ Slog.d(TAG, "Verifying Self trusted profile.");
+ try {
+ result.resultCode =
+ AttestationVerificationSelfTrustedVerifierForTesting.getInstance()
+ .verifyAttestation(localBindingType, requirements, attestation);
+ } catch (Throwable t) {
+ result.resultCode = RESULT_FAILURE;
+ }
+ break;
+ default:
+ Slog.d(TAG, "No profile found, defaulting.");
+ result.resultCode = RESULT_UNKNOWN;
+ }
resultCallback.complete(result);
}
diff --git a/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
new file mode 100644
index 0000000..58df2bd
--- /dev/null
+++ b/services/core/java/com/android/server/security/AttestationVerificationSelfTrustedVerifierForTesting.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.security;
+
+import static android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE;
+import static android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS;
+import static android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE;
+
+import android.annotation.NonNull;
+import android.os.Build;
+import android.os.Bundle;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.util.Log;
+import android.util.Slog;
+
+import com.android.internal.org.bouncycastle.asn1.ASN1InputStream;
+import com.android.internal.org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import com.android.internal.org.bouncycastle.asn1.ASN1OctetString;
+import com.android.internal.org.bouncycastle.asn1.ASN1Sequence;
+import com.android.internal.org.bouncycastle.asn1.x509.Certificate;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.KeyPairGenerator;
+import java.security.KeyStore;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathValidator;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.PKIXParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Verifies {@code PROFILE_SELF_TRUSTED} attestations.
+ *
+ * Verifies that the attesting environment can create an attestation with the same root certificate
+ * as the verifying device with a matching attestation challenge. Skips CRL revocations checking
+ * so this verifier can work in a hermetic test environment.
+ *
+ * This verifier profile is intended to be used only for testing.
+ */
+class AttestationVerificationSelfTrustedVerifierForTesting {
+ private static final String TAG = "AVF";
+ private static final boolean DEBUG = Build.IS_DEBUGGABLE && Log.isLoggable(TAG, Log.VERBOSE);
+
+ // The OID for the extension Android Keymint puts into device-generated certificates.
+ private static final String ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID =
+ "1.3.6.1.4.1.11129.2.1.17";
+
+ // ASN.1 sequence index values for the Android Keymint extension.
+ private static final int ATTESTATION_CHALLENGE_INDEX = 4;
+
+ private static final String ANDROID_KEYSTORE = "AndroidKeyStore";
+ private static final String GOLDEN_ALIAS =
+ AttestationVerificationSelfTrustedVerifierForTesting.class.getCanonicalName()
+ + ".Golden";
+
+ private static volatile AttestationVerificationSelfTrustedVerifierForTesting
+ sAttestationVerificationSelfTrustedVerifier = null;
+
+ private final CertificateFactory mCertificateFactory;
+ private final CertPathValidator mCertPathValidator;
+ private final KeyStore mAndroidKeyStore;
+ private X509Certificate mGoldenRootCert;
+
+ static AttestationVerificationSelfTrustedVerifierForTesting getInstance()
+ throws Exception {
+ if (sAttestationVerificationSelfTrustedVerifier == null) {
+ synchronized (AttestationVerificationSelfTrustedVerifierForTesting.class) {
+ if (sAttestationVerificationSelfTrustedVerifier == null) {
+ sAttestationVerificationSelfTrustedVerifier =
+ new AttestationVerificationSelfTrustedVerifierForTesting();
+ }
+ }
+ }
+ return sAttestationVerificationSelfTrustedVerifier;
+ }
+
+ private static void debugVerboseLog(String str, Throwable t) {
+ if (DEBUG) {
+ Slog.v(TAG, str, t);
+ }
+ }
+
+ private static void debugVerboseLog(String str) {
+ if (DEBUG) {
+ Slog.v(TAG, str);
+ }
+ }
+
+ private AttestationVerificationSelfTrustedVerifierForTesting() throws Exception {
+ mCertificateFactory = CertificateFactory.getInstance("X.509");
+ mCertPathValidator = CertPathValidator.getInstance("PKIX");
+ mAndroidKeyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
+ mAndroidKeyStore.load(null);
+ if (!mAndroidKeyStore.containsAlias(GOLDEN_ALIAS)) {
+ KeyPairGenerator kpg =
+ KeyPairGenerator.getInstance(KeyProperties.KEY_ALGORITHM_EC, ANDROID_KEYSTORE);
+ KeyGenParameterSpec parameterSpec = new KeyGenParameterSpec.Builder(
+ GOLDEN_ALIAS, KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY)
+ .setAttestationChallenge(GOLDEN_ALIAS.getBytes())
+ .setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512).build();
+ kpg.initialize(parameterSpec);
+ kpg.generateKeyPair();
+ }
+
+ X509Certificate[] goldenCerts = (X509Certificate[])
+ ((KeyStore.PrivateKeyEntry) mAndroidKeyStore.getEntry(GOLDEN_ALIAS, null))
+ .getCertificateChain();
+ mGoldenRootCert = goldenCerts[goldenCerts.length - 1];
+ }
+
+ int verifyAttestation(
+ int localBindingType, @NonNull Bundle requirements, @NonNull byte[] attestation) {
+ List<X509Certificate> certificates = new ArrayList<>();
+ ByteArrayInputStream bis = new ByteArrayInputStream(attestation);
+ try {
+ while (bis.available() > 0) {
+ certificates.add((X509Certificate) mCertificateFactory.generateCertificate(bis));
+ }
+ } catch (CertificateException e) {
+ debugVerboseLog("Unable to parse certificates from attestation", e);
+ return RESULT_FAILURE;
+ }
+
+ if (localBindingType == TYPE_CHALLENGE
+ && validateRequirements(requirements)
+ && checkLeafChallenge(requirements, certificates)
+ && verifyCertificateChain(certificates)) {
+ return RESULT_SUCCESS;
+ }
+
+ return RESULT_FAILURE;
+ }
+
+ private boolean verifyCertificateChain(List<X509Certificate> certificates) {
+ if (certificates.size() < 2) {
+ debugVerboseLog("Certificate chain less than 2 in size.");
+ return false;
+ }
+
+ try {
+ CertPath certificatePath = mCertificateFactory.generateCertPath(certificates);
+ PKIXParameters validationParams = new PKIXParameters(getTrustAnchors());
+ // Skipping revocation checking because we want this to work in a hermetic test
+ // environment.
+ validationParams.setRevocationEnabled(false);
+ mCertPathValidator.validate(certificatePath, validationParams);
+ } catch (Throwable t) {
+ debugVerboseLog("Invalid certificate chain", t);
+ return false;
+ }
+
+ return true;
+ }
+
+ private Set<TrustAnchor> getTrustAnchors() {
+ return Collections.singleton(new TrustAnchor(mGoldenRootCert, null));
+ }
+
+ private boolean validateRequirements(Bundle requirements) {
+ if (requirements.size() != 1) {
+ debugVerboseLog("Requirements does not contain exactly 1 key.");
+ return false;
+ }
+ if (!requirements.containsKey(PARAM_CHALLENGE)) {
+ debugVerboseLog("Requirements does not contain key: " + PARAM_CHALLENGE);
+ return false;
+ }
+ return true;
+ }
+
+ private boolean checkLeafChallenge(Bundle requirements, List<X509Certificate> certificates) {
+ // Verify challenge
+ byte[] challenge;
+ try {
+ challenge = getChallengeFromCert(certificates.get(0));
+ } catch (Throwable t) {
+ debugVerboseLog("Unable to parse challenge from certificate.", t);
+ return false;
+ }
+
+ if (Arrays.equals(requirements.getByteArray(PARAM_CHALLENGE), challenge)) {
+ return true;
+ } else {
+ debugVerboseLog("Self-Trusted validation failed; challenge mismatch.");
+ return false;
+ }
+ }
+
+ private byte[] getChallengeFromCert(@NonNull X509Certificate x509Certificate)
+ throws CertificateEncodingException, IOException {
+ Certificate certificate = Certificate.getInstance(
+ new ASN1InputStream(x509Certificate.getEncoded()).readObject());
+ ASN1Sequence keyAttributes = (ASN1Sequence) certificate.getTBSCertificate().getExtensions()
+ .getExtensionParsedValue(
+ new ASN1ObjectIdentifier(ANDROID_KEYMINT_KEY_DESCRIPTION_EXTENSION_OID));
+ return ((ASN1OctetString) keyAttributes.getObjectAt(ATTESTATION_CHALLENGE_INDEX))
+ .getOctets();
+ }
+}
diff --git a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
index 8a87c96..94f483c 100644
--- a/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
+++ b/services/core/java/com/android/server/statusbar/StatusBarManagerService.java
@@ -53,6 +53,7 @@
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayManager.DisplayListener;
import android.hardware.fingerprint.IUdfpsHbmListener;
+import android.media.MediaRoute2Info;
import android.net.Uri;
import android.os.Binder;
import android.os.Build;
@@ -92,6 +93,7 @@
import com.android.internal.statusbar.ISessionListener;
import com.android.internal.statusbar.IStatusBar;
import com.android.internal.statusbar.IStatusBarService;
+import com.android.internal.statusbar.IUndoMediaTransferCallback;
import com.android.internal.statusbar.NotificationVisibility;
import com.android.internal.statusbar.RegisterStatusBarResult;
import com.android.internal.statusbar.StatusBarIcon;
@@ -1265,6 +1267,12 @@
"StatusBarManagerService");
}
+ private void enforceMediaContentControl() {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.MEDIA_CONTENT_CONTROL,
+ "StatusBarManagerService");
+ }
+
/**
* For targetSdk S+ we require STATUS_BAR. For targetSdk < S, we only require EXPAND_STATUS_BAR
* but also require that it falls into one of the allowed use-cases to lock down abuse vector.
@@ -1987,6 +1995,53 @@
return false;
}
+ /**
+ * Notifies the system of a new media tap-to-transfer state for the *sender* device. See
+ * {@link StatusBarManager.updateMediaTapToTransferSenderDisplay} for more information.
+ *
+ * @param undoCallback a callback that will be triggered if the user elects to undo a media
+ * transfer.
+ *
+ * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
+ * permission.
+ */
+ @Override
+ public void updateMediaTapToTransferSenderDisplay(
+ @StatusBarManager.MediaTransferSenderState int displayState,
+ @NonNull MediaRoute2Info routeInfo,
+ @Nullable IUndoMediaTransferCallback undoCallback
+ ) {
+ enforceMediaContentControl();
+ if (mBar != null) {
+ try {
+ mBar.updateMediaTapToTransferSenderDisplay(displayState, routeInfo, undoCallback);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "updateMediaTapToTransferSenderDisplay", e);
+ }
+ }
+ }
+
+ /**
+ * Notifies the system of a new media tap-to-transfer state for the *receiver* device. See
+ * {@link StatusBarManager.updateMediaTapToTransferReceiverDisplay} for more information.
+ *
+ * Requires the caller to have the {@link android.Manifest.permission.MEDIA_CONTENT_CONTROL}
+ * permission.
+ */
+ @Override
+ public void updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState int displayState,
+ MediaRoute2Info routeInfo) {
+ enforceMediaContentControl();
+ if (mBar != null) {
+ try {
+ mBar.updateMediaTapToTransferReceiverDisplay(displayState, routeInfo);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "updateMediaTapToTransferReceiverDisplay", e);
+ }
+ }
+ }
+
/** @hide */
public void passThroughShellCommand(String[] args, FileDescriptor fd) {
enforceStatusBarOrShell();
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 5f04b7e..6836e31 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -7655,13 +7655,15 @@
* <li>{@link LetterboxConfiguration#getIsEducationEnabled} is true.
* <li>The activity is eligible for fixed orientation letterbox.
* <li>The activity is in fullscreen.
+ * <li>The activity is portrait-only.
* </ul>
*/
// TODO(b/215316431): Add tests
boolean isEligibleForLetterboxEducation() {
return mWmService.mLetterboxConfiguration.getIsEducationEnabled()
&& mIsEligibleForFixedOrientationLetterbox
- && getWindowingMode() == WINDOWING_MODE_FULLSCREEN;
+ && getWindowingMode() == WINDOWING_MODE_FULLSCREEN
+ && getRequestedConfigurationOrientation() == ORIENTATION_PORTRAIT;
}
/**
diff --git a/services/core/java/com/android/server/wm/BackNavigationController.java b/services/core/java/com/android/server/wm/BackNavigationController.java
index a8779fa..45a6cb9 100644
--- a/services/core/java/com/android/server/wm/BackNavigationController.java
+++ b/services/core/java/com/android/server/wm/BackNavigationController.java
@@ -29,6 +29,7 @@
import android.util.Slog;
import android.view.SurfaceControl;
import android.window.BackNavigationInfo;
+import android.window.IOnBackInvokedCallback;
import android.window.TaskSnapshot;
import com.android.internal.annotations.VisibleForTesting;
@@ -91,29 +92,41 @@
HardwareBuffer screenshotBuffer = null;
int prevTaskId;
int prevUserId;
+ IOnBackInvokedCallback callback;
synchronized (task.mWmService.mGlobalLock) {
activityRecord = task.topRunningActivity();
removedWindowContainer = activityRecord;
taskWindowConfiguration = task.getTaskInfo().configuration.windowConfiguration;
- ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, topRunningActivity=%s",
- task, activityRecord);
+ WindowState topChild = activityRecord.getTopChild();
+ callback = topChild.getOnBackInvokedCallback();
- // IME is visible, back gesture will dismiss it, nothing to preview.
- if (task.getDisplayContent().getImeContainer().isVisible()) {
- return null;
- }
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "startBackNavigation task=%s, "
+ + "topRunningActivity=%s, topWindow=%s backCallback=%s",
+ task, activityRecord, topChild,
+ callback != null ? callback.getClass().getSimpleName() : null);
- // Current Activity is home, there is no previous activity to display
- if (activityRecord.isActivityTypeHome()) {
- return null;
+ // For IME and Home, either a callback is registered, or we do nothing. In both cases,
+ // we don't need to pass the leashes below.
+ if (task.getDisplayContent().getImeContainer().isVisible()
+ || activityRecord.isActivityTypeHome()) {
+ if (callback != null) {
+ return new BackNavigationInfo(BackNavigationInfo.TYPE_CALLBACK,
+ null /* topWindowLeash */, null /* screenshotSurface */,
+ null /* screenshotBuffer */, null /* taskWindowConfiguration */,
+ null /* onBackNavigationDone */, callback /* onBackInvokedCallback */);
+ } else {
+ return null;
+ }
}
prev = task.getActivity(
(r) -> !r.finishing && r.getTask() == task && !r.isTopRunningActivity());
- if (prev != null) {
+ if (callback != null) {
+ backType = BackNavigationInfo.TYPE_CALLBACK;
+ } else if (prev != null) {
backType = BackNavigationInfo.TYPE_CROSS_ACTIVITY;
} else if (task.returnsToHomeRootTask()) {
prevTask = null;
@@ -148,7 +161,7 @@
// Prepare a leash to animate the current top window
animLeash = removedWindowContainer.makeAnimationLeash()
- .setName("BackPreview Leash")
+ .setName("BackPreview Leash for " + removedWindowContainer)
.setHidden(false)
.setBLASTLayer()
.build();
@@ -158,7 +171,7 @@
}
SurfaceControl.Builder builder = new SurfaceControl.Builder()
- .setName("BackPreview Screenshot")
+ .setName("BackPreview Screenshot for " + prev)
.setParent(animationLeashParent)
.setHidden(false)
.setBLASTLayer();
@@ -171,6 +184,10 @@
screenshotBuffer = getTaskSnapshot(prevTaskId, prevUserId);
}
}
+
+ // The Animation leash needs to be above the screenshot surface, but the animation leash
+ // needs to be added before to be in the synchronized block.
+ tx.setLayer(animLeash, 1);
tx.apply();
WindowContainer<?> finalRemovedWindowContainer = removedWindowContainer;
@@ -183,13 +200,16 @@
return null;
}
+ RemoteCallback onBackNavigationDone = new RemoteCallback(
+ result -> resetSurfaces(finalRemovedWindowContainer
+ ));
return new BackNavigationInfo(backType,
animLeash,
screenshotSurface,
screenshotBuffer,
taskWindowConfiguration,
- new RemoteCallback(result -> resetSurfaces(finalRemovedWindowContainer
- )));
+ onBackNavigationDone,
+ callback);
}
diff --git a/services/core/java/com/android/server/wm/Session.java b/services/core/java/com/android/server/wm/Session.java
index 2ae2b43..f26c5393 100644
--- a/services/core/java/com/android/server/wm/Session.java
+++ b/services/core/java/com/android/server/wm/Session.java
@@ -885,8 +885,16 @@
}
@Override
- public void setOnBackInvokedCallback(IWindow iWindow,
- IOnBackInvokedCallback iOnBackInvokedCallback) throws RemoteException {
- // TODO: Set the callback to the WindowState of the window.
+ public void setOnBackInvokedCallback(IWindow window,
+ IOnBackInvokedCallback onBackInvokedCallback) throws RemoteException {
+ synchronized (mService.mGlobalLock) {
+ WindowState windowState = mService.windowForClientLocked(this, window, false);
+ if (windowState == null) {
+ Slog.e(TAG_WM,
+ "setOnBackInvokedCallback(): Can't find window state for window:" + window);
+ } else {
+ windowState.setOnBackInvokedCallback(onBackInvokedCallback);
+ }
+ }
}
}
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 97735a2..b84ef77 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -2197,10 +2197,6 @@
final Rect taskBounds = getBounds();
width = taskBounds.width();
height = taskBounds.height();
-
- final int outset = getTaskOutset();
- width += 2 * outset;
- height += 2 * outset;
}
if (width == mLastSurfaceSize.x && height == mLastSurfaceSize.y) {
return;
@@ -2209,28 +2205,6 @@
mLastSurfaceSize.set(width, height);
}
- /**
- * Calculate an amount by which to expand the task bounds in each direction.
- * Used to make room for shadows in the pinned windowing mode.
- */
- int getTaskOutset() {
- // If we are drawing shadows on the task then don't outset the root task.
- if (mWmService.mRenderShadowsInCompositor) {
- return 0;
- }
- DisplayContent displayContent = getDisplayContent();
- if (inPinnedWindowingMode() && displayContent != null) {
- final DisplayMetrics displayMetrics = displayContent.getDisplayMetrics();
-
- // We multiply by two to match the client logic for converting view elevation
- // to insets, as in {@link WindowManager.LayoutParams#setSurfaceInsets}
- return (int) Math.ceil(
- mWmService.dipToPixel(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP, displayMetrics)
- * 2);
- }
- return 0;
- }
-
@VisibleForTesting
Point getLastSurfaceSize() {
return mLastSurfaceSize;
@@ -4338,7 +4312,7 @@
*/
private void updateShadowsRadius(boolean taskIsFocused,
SurfaceControl.Transaction pendingTransaction) {
- if (!mWmService.mRenderShadowsInCompositor || !isRootTask()) return;
+ if (!isRootTask()) return;
final float newShadowRadius = getShadowRadius(taskIsFocused);
if (mShadowRadius != newShadowRadius) {
@@ -6015,14 +5989,6 @@
scheduleAnimation();
}
- @Override
- void getRelativePosition(Point outPos) {
- super.getRelativePosition(outPos);
- final int outset = getTaskOutset();
- outPos.x -= outset;
- outPos.y -= outset;
- }
-
private Point getRelativePosition() {
Point position = new Point();
getRelativePosition(position);
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 1167cb5..22c430f 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -462,11 +462,6 @@
int mVr2dDisplayId = INVALID_DISPLAY;
boolean mVrModeEnabled = false;
- /* If true, shadows drawn around the window will be rendered by the system compositor. If
- * false, shadows will be drawn by the client by setting an elevation on the root view and
- * the contents will be inset by the shadow radius. */
- boolean mRenderShadowsInCompositor = false;
-
/**
* Tracks a map of input tokens to info that is used to decide whether to intercept
* a key event.
@@ -811,8 +806,6 @@
resolver.registerContentObserver(mForceResizableUri, false, this, UserHandle.USER_ALL);
resolver.registerContentObserver(mDevEnableNonResizableMultiWindowUri, false, this,
UserHandle.USER_ALL);
- resolver.registerContentObserver(mRenderShadowsInCompositorUri, false, this,
- UserHandle.USER_ALL);
resolver.registerContentObserver(mDisplaySettingsPathUri, false, this,
UserHandle.USER_ALL);
}
@@ -853,11 +846,6 @@
return;
}
- if (mRenderShadowsInCompositorUri.equals(uri)) {
- setShadowRenderer();
- return;
- }
-
if (mDisplaySettingsPathUri.equals(uri)) {
updateDisplaySettingsLocation();
return;
@@ -972,11 +960,6 @@
}
}
- private void setShadowRenderer() {
- mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
- }
-
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
@@ -1395,7 +1378,6 @@
float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
lightRadius);
- setShadowRenderer();
}
/**
@@ -3855,14 +3837,20 @@
}
/**
- * Generates and returns an up-to-date {@link Bitmap} for the specified taskId. The returned
- * bitmap will be full size and will not include any secure content.
+ * Generates and returns an up-to-date {@link Bitmap} for the specified taskId.
*
- * @param taskId The task ID of the task for which a snapshot is requested.
+ * @param taskId The task ID of the task for which a Bitmap is requested.
+ * @param layerCaptureArgsBuilder A {@link SurfaceControl.LayerCaptureArgs.Builder} with
+ * arguments for how to capture the Bitmap. The caller can
+ * specify any arguments, but this method will ensure that the
+ * specified task's SurfaceControl is used and the crop is set to
+ * the bounds of that task.
* @return The Bitmap, or null if no task with the specified ID can be found or the bitmap could
* not be generated.
*/
- @Nullable public Bitmap captureTaskBitmap(int taskId) {
+ @Nullable
+ public Bitmap captureTaskBitmap(int taskId,
+ @NonNull SurfaceControl.LayerCaptureArgs.Builder layerCaptureArgsBuilder) {
if (mTaskSnapshotController.shouldDisableSnapshots()) {
return null;
}
@@ -3876,9 +3864,7 @@
task.getBounds(mTmpRect);
final SurfaceControl sc = task.getSurfaceControl();
final SurfaceControl.ScreenshotHardwareBuffer buffer = SurfaceControl.captureLayers(
- new SurfaceControl.LayerCaptureArgs.Builder(sc)
- .setSourceCrop(mTmpRect)
- .build());
+ layerCaptureArgsBuilder.setLayer(sc).setSourceCrop(mTmpRect).build());
if (buffer == null) {
Slog.w(TAG, "Could not get screenshot buffer for taskId: " + taskId);
return null;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 79c64b1..0d72e9a 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -106,6 +106,7 @@
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ADD_REMOVE;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_APP_TRANSITIONS;
+import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_BACK_PREVIEW;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_FOCUS_LIGHT;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ORIENTATION;
@@ -245,6 +246,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.window.ClientWindowFrames;
+import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.policy.KeyInterceptionInfo;
@@ -845,6 +847,11 @@
}
};
+ /**
+ * @see #setOnBackInvokedCallback(IOnBackInvokedCallback)
+ */
+ private IOnBackInvokedCallback mOnBackInvokedCallback;
+
@Override
WindowState asWindowState() {
return this;
@@ -1061,6 +1068,22 @@
return true;
}
+ /**
+ * Used by {@link android.window.WindowOnBackInvokedDispatcher} to set the callback to be
+ * called when a back navigation action is initiated.
+ * @see BackNavigationController
+ */
+ void setOnBackInvokedCallback(@Nullable IOnBackInvokedCallback onBackInvokedCallback) {
+ ProtoLog.d(WM_DEBUG_BACK_PREVIEW, "%s: Setting back callback %s",
+ this, onBackInvokedCallback);
+ mOnBackInvokedCallback = onBackInvokedCallback;
+ }
+
+ @Nullable
+ IOnBackInvokedCallback getOnBackInvokedCallback() {
+ return mOnBackInvokedCallback;
+ }
+
interface PowerManagerWrapper {
void wakeUp(long time, @WakeReason int reason, String details);
@@ -5398,17 +5421,6 @@
outPoint.offset(-parentBounds.left, -parentBounds.top);
}
- Task rootTask = getRootTask();
-
- // If we have root task outsets, that means the top-left
- // will be outset, and we need to inset ourselves
- // to account for it. If we actually have shadows we will
- // then un-inset ourselves by the surfaceInsets.
- if (rootTask != null) {
- final int outset = rootTask.getTaskOutset();
- outPoint.offset(outset, outset);
- }
-
// The surface size is larger than the window if the window has positive surface insets.
transformSurfaceInsetsPosition(mTmpPoint, mAttrs.surfaceInsets);
outPoint.offset(-mTmpPoint.x, -mTmpPoint.y);
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 166a0f5..0584604 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -509,7 +509,7 @@
template <class T_list, class T_sv_info>
Return<void> GnssCallback::gnssSvStatusCbImpl(const T_list& svStatus) {
// In HIDL or AIDL v1, if no listener is registered, do not report svInfoList to the framework.
- if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() == 1) {
+ if (gnssHalAidl == nullptr || gnssHalAidl->getInterfaceVersion() <= 1) {
if (!isSvStatusRegistered) {
return Void();
}
@@ -695,7 +695,7 @@
Status GnssCallbackAidl::gnssNmeaCb(const int64_t timestamp, const std::string& nmea) {
// In AIDL v1, if no listener is registered, do not report nmea to the framework.
- if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() == 1) {
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() <= 1) {
if (!isNmeaRegistered) {
return Status::ok();
}
diff --git a/services/core/xsd/display-device-config/display-device-config.xsd b/services/core/xsd/display-device-config/display-device-config.xsd
index 79d8036..be0ddc1 100644
--- a/services/core/xsd/display-device-config/display-device-config.xsd
+++ b/services/core/xsd/display-device-config/display-device-config.xsd
@@ -123,6 +123,18 @@
<xs:annotation name="nonnull"/>
<xs:annotation name="final"/>
</xs:element>
+ <!-- The minimum HDR video size at which high-brightness-mode is allowed to operate.
+ Default is 0.5 if not specified-->
+ <xs:element name="minimumHdrPercentOfScreen" type="nonNegativeDecimal"
+ minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <!-- This LUT specifies how to boost HDR brightness at given SDR brightness (nits). -->
+ <xs:element type="sdrHdrRatioMap" name="sdrHdrRatioMap" minOccurs="0" maxOccurs="1">
+ <xs:annotation name="nullable"/>
+ <xs:annotation name="final"/>
+ </xs:element>
</xs:all>
<xs:attribute name="enabled" type="xs:boolean" use="optional"/>
</xs:complexType>
@@ -158,6 +170,14 @@
</xs:restriction>
</xs:simpleType>
+ <!-- Maps to DisplayDeviceConfig.INTERPOLATION_* values. -->
+ <xs:simpleType name="interpolation">
+ <xs:restriction base="xs:string">
+ <xs:enumeration value="default"/>
+ <xs:enumeration value="linear"/>
+ </xs:restriction>
+ </xs:simpleType>
+
<xs:complexType name="thermalThrottling">
<xs:complexType>
<xs:element type="brightnessThrottlingMap" name="brightnessThrottlingMap">
@@ -196,6 +216,7 @@
<xs:annotation name="final"/>
</xs:element>
</xs:sequence>
+ <xs:attribute name="interpolation" type="interpolation" use="optional"/>
</xs:complexType>
<xs:complexType name="point">
@@ -211,6 +232,28 @@
</xs:sequence>
</xs:complexType>
+ <xs:complexType name="sdrHdrRatioMap">
+ <xs:sequence>
+ <xs:element name="point" type="sdrHdrRatioPoint" maxOccurs="unbounded" minOccurs="2">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
+ <xs:complexType name="sdrHdrRatioPoint">
+ <xs:sequence>
+ <xs:element type="nonNegativeDecimal" name="sdrNits">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ <xs:element type="nonNegativeDecimal" name="hdrRatio">
+ <xs:annotation name="nonnull"/>
+ <xs:annotation name="final"/>
+ </xs:element>
+ </xs:sequence>
+ </xs:complexType>
+
<xs:simpleType name="nonNegativeDecimal">
<xs:restriction base="xs:decimal">
<xs:minInclusive value="0.0"/>
diff --git a/services/core/xsd/display-device-config/schema/current.txt b/services/core/xsd/display-device-config/schema/current.txt
index 0b7df4d..2890d68 100644
--- a/services/core/xsd/display-device-config/schema/current.txt
+++ b/services/core/xsd/display-device-config/schema/current.txt
@@ -90,23 +90,35 @@
ctor public HighBrightnessMode();
method @NonNull public final boolean getAllowInLowPowerMode_all();
method public boolean getEnabled();
+ method @Nullable public final java.math.BigDecimal getMinimumHdrPercentOfScreen_all();
method @NonNull public final java.math.BigDecimal getMinimumLux_all();
method @Nullable public final com.android.server.display.config.RefreshRateRange getRefreshRate_all();
+ method @Nullable public final com.android.server.display.config.SdrHdrRatioMap getSdrHdrRatioMap_all();
method @NonNull public final com.android.server.display.config.ThermalStatus getThermalStatusLimit_all();
method public com.android.server.display.config.HbmTiming getTiming_all();
method @NonNull public final java.math.BigDecimal getTransitionPoint_all();
method public final void setAllowInLowPowerMode_all(@NonNull boolean);
method public void setEnabled(boolean);
+ method public final void setMinimumHdrPercentOfScreen_all(@Nullable java.math.BigDecimal);
method public final void setMinimumLux_all(@NonNull java.math.BigDecimal);
method public final void setRefreshRate_all(@Nullable com.android.server.display.config.RefreshRateRange);
+ method public final void setSdrHdrRatioMap_all(@Nullable com.android.server.display.config.SdrHdrRatioMap);
method public final void setThermalStatusLimit_all(@NonNull com.android.server.display.config.ThermalStatus);
method public void setTiming_all(com.android.server.display.config.HbmTiming);
method public final void setTransitionPoint_all(@NonNull java.math.BigDecimal);
}
+ public enum Interpolation {
+ method public String getRawName();
+ enum_constant public static final com.android.server.display.config.Interpolation _default;
+ enum_constant public static final com.android.server.display.config.Interpolation linear;
+ }
+
public class NitsMap {
ctor public NitsMap();
+ method public com.android.server.display.config.Interpolation getInterpolation();
method @NonNull public final java.util.List<com.android.server.display.config.Point> getPoint();
+ method public void setInterpolation(com.android.server.display.config.Interpolation);
}
public class Point {
@@ -125,6 +137,19 @@
method public final void setMinimum(java.math.BigInteger);
}
+ public class SdrHdrRatioMap {
+ ctor public SdrHdrRatioMap();
+ method @NonNull public final java.util.List<com.android.server.display.config.SdrHdrRatioPoint> getPoint();
+ }
+
+ public class SdrHdrRatioPoint {
+ ctor public SdrHdrRatioPoint();
+ method @NonNull public final java.math.BigDecimal getHdrRatio();
+ method @NonNull public final java.math.BigDecimal getSdrNits();
+ method public final void setHdrRatio(@NonNull java.math.BigDecimal);
+ method public final void setSdrNits(@NonNull java.math.BigDecimal);
+ }
+
public class SensorDetails {
ctor public SensorDetails();
method @Nullable public final String getName();
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 6f41d42..e99b1f9 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -10951,7 +10951,7 @@
if (mPendingUserCreatedCallbackTokens.contains(token)) {
// Ignore because it was triggered by createAndManageUser()
Slogf.d(LOG_TAG, "handleNewUserCreated(): ignoring for user " + userId
- + " due to token" + token);
+ + " due to token " + token);
mPendingUserCreatedCallbackTokens.remove(token);
return;
}
@@ -11083,6 +11083,8 @@
switched = mInjector.getIActivityManager().switchUser(userId);
if (!switched) {
Slogf.w(LOG_TAG, "Failed to switch to user %d", userId);
+ } else {
+ Slogf.d(LOG_TAG, "Switched");
}
return switched;
} catch (RemoteException e) {
@@ -11106,18 +11108,12 @@
}
private @UserIdInt int getLogoutUserIdUnchecked() {
- if (!mInjector.userManagerIsHeadlessSystemUserMode()) {
- // mLogoutUserId is USER_SYSTEM as well, but there's no need to acquire the lock
- return UserHandle.USER_SYSTEM;
- }
synchronized (getLockObject()) {
return mLogoutUserId;
}
}
private void clearLogoutUser() {
- if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
-
synchronized (getLockObject()) {
setLogoutUserIdLocked(UserHandle.USER_NULL);
}
@@ -11125,8 +11121,6 @@
@GuardedBy("getLockObject()")
private void setLogoutUserIdLocked(@UserIdInt int userId) {
- if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
-
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentForegroundUserId();
}
@@ -11209,8 +11203,7 @@
return UserManager.USER_OPERATION_ERROR_MANAGED_PROFILE;
}
- // TODO(b/204585343): remove the headless system user check?
- if (mInjector.userManagerIsHeadlessSystemUserMode() && callingUserId != mInjector
+ if (callingUserId != mInjector
.binderWithCleanCallingIdentity(() -> getCurrentForegroundUserId())) {
Slogf.d(LOG_TAG, "logoutUser(): user %d is in background, just stopping, not switching",
callingUserId);
@@ -11226,8 +11219,15 @@
Preconditions.checkCallAuthorization(canManageUsers(caller)
|| hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
- int result = logoutUserUnchecked(getCurrentForegroundUserId());
- Slogf.d(LOG_TAG, "logout called by uid %d. Result: %d", caller.getUid(), result);
+ int currentUserId = getCurrentForegroundUserId();
+ if (VERBOSE_LOG) {
+ Slogf.v(LOG_TAG, "logout() called by uid %d; current user is %d", caller.getUid(),
+ currentUserId);
+ }
+ int result = logoutUserUnchecked(currentUserId);
+ if (VERBOSE_LOG) {
+ Slogf.v(LOG_TAG, "Result of logout(): %d", result);
+ }
return result;
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index d0c861f..c2dec06 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -154,6 +154,7 @@
import com.android.server.os.SchedulingPolicyService;
import com.android.server.people.PeopleService;
import com.android.server.pm.ApexManager;
+import com.android.server.pm.ApexSystemServiceInfo;
import com.android.server.pm.CrossProfileAppsService;
import com.android.server.pm.DataLoaderManagerService;
import com.android.server.pm.DynamicCodeLoggingService;
@@ -224,8 +225,8 @@
import java.util.Arrays;
import java.util.Date;
import java.util.LinkedList;
+import java.util.List;
import java.util.Locale;
-import java.util.Map;
import java.util.Timer;
import java.util.TreeSet;
import java.util.concurrent.CountDownLatch;
@@ -1392,7 +1393,6 @@
DynamicSystemService dynamicSystem = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
- IpSecService ipSecService = null;
VpnManagerService vpnManager = null;
VcnManagementService vcnManagement = null;
NetworkStatsService networkStats = null;
@@ -1472,7 +1472,7 @@
// TelecomLoader hooks into classes with defined HFP logic,
// so check for either telephony or microphone.
if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE) ||
- mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
+ mPackageManager.hasSystemFeature(PackageManager.FEATURE_TELEPHONY)) {
t.traceBegin("StartTelecomLoaderService");
mSystemServiceManager.startService(TelecomLoaderService.class);
t.traceEnd();
@@ -1480,7 +1480,7 @@
t.traceBegin("StartTelephonyRegistry");
telephonyRegistry = new TelephonyRegistry(
- context, new TelephonyRegistry.ConfigurationProvider());
+ context, new TelephonyRegistry.ConfigurationProvider());
ServiceManager.addService("telephony.registry", telephonyRegistry);
t.traceEnd();
@@ -1897,15 +1897,6 @@
}
t.traceEnd();
- t.traceBegin("StartIpSecService");
- try {
- ipSecService = IpSecService.create(context);
- ServiceManager.addService(Context.IPSEC_SERVICE, ipSecService);
- } catch (Throwable e) {
- reportWtf("starting IpSec Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartFontManagerService");
mSystemServiceManager.startService(new FontManagerService.Lifecycle(context, safeMode));
t.traceEnd();
@@ -2792,7 +2783,6 @@
final TelephonyRegistry telephonyRegistryF = telephonyRegistry;
final MediaRouterService mediaRouterF = mediaRouter;
final MmsServiceBroker mmsServiceF = mmsService;
- final IpSecService ipSecServiceF = ipSecService;
final VpnManagerService vpnManagerF = vpnManager;
final VcnManagementService vcnManagementF = vcnManagement;
final WindowManagerService windowManagerF = wm;
@@ -2884,15 +2874,6 @@
.networkScoreAndNetworkManagementServiceReady();
}
t.traceEnd();
- t.traceBegin("MakeIpSecServiceReady");
- try {
- if (ipSecServiceF != null) {
- ipSecServiceF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making IpSec Service ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
@@ -3017,7 +2998,9 @@
t.traceEnd();
t.traceBegin("MakeTelephonyRegistryReady");
try {
- if (telephonyRegistryF != null) telephonyRegistryF.systemRunning();
+ if (telephonyRegistryF != null) {
+ telephonyRegistryF.systemRunning();
+ }
} catch (Throwable e) {
reportWtf("Notifying TelephonyRegistry running", e);
}
@@ -3082,10 +3065,12 @@
*/
private void startApexServices(@NonNull TimingsTraceAndSlog t) {
t.traceBegin("startApexServices");
- Map<String, String> services = ApexManager.getInstance().getApexSystemServices();
- // TODO(satayev): introduce android:order for services coming the same apexes
- for (String name : new TreeSet<>(services.keySet())) {
- String jarPath = services.get(name);
+ // TODO(b/192880996): get the list from "android" package, once the manifest entries
+ // are migrated to system manifest.
+ List<ApexSystemServiceInfo> services = ApexManager.getInstance().getApexSystemServices();
+ for (ApexSystemServiceInfo info : services) {
+ String name = info.getName();
+ String jarPath = info.getJarPath();
t.traceBegin("starting " + name);
if (TextUtils.isEmpty(jarPath)) {
mSystemServiceManager.startService(name);
diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
index 16d6241..0a9b7b1 100644
--- a/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
+++ b/services/tests/apexsystemservices/apexes/test_com.android.server/Android.bp
@@ -32,7 +32,7 @@
name: "test_com.android.server",
manifest: "manifest.json",
androidManifest: "AndroidManifest.xml",
- java_libs: ["FakeApexSystemService"],
+ java_libs: ["FakeApexSystemServices"],
file_contexts: ":apex.test-file_contexts",
key: "test_com.android.server.key",
updatable: false,
diff --git a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
index eb741ca..6bec284 100644
--- a/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
+++ b/services/tests/apexsystemservices/apexes/test_com.android.server/AndroidManifest.xml
@@ -21,21 +21,29 @@
<application android:hasCode="false" android:testOnly="true">
<apex-system-service
android:name="com.android.server.testing.FakeApexSystemService"
- android:path="/apex/test_com.android.server/javalib/FakeApexSystemService.jar"
- android:minSdkVersion="30"/>
+ android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar"
+ android:minSdkVersion="30"
+ />
+
+ <apex-system-service
+ android:name="com.android.server.testing.FakeApexSystemService2"
+ android:path="/apex/test_com.android.server/javalib/FakeApexSystemServices.jar"
+ android:minSdkVersion="30"
+ android:initOrder="1"
+ />
<!-- Always inactive system service, since maxSdkVersion is low -->
<apex-system-service
- android:name="com.android.apex.test.OldApexSystemService"
- android:path="/apex/com.android.apex.test/javalib/fake.jar"
+ android:name="com.android.server.testing.OldApexSystemService"
+ android:path="/apex/test_com.android.server/javalib/fake.jar"
android:minSdkVersion="1"
android:maxSdkVersion="1"
/>
<!-- Always inactive system service, since minSdkVersion is high -->
<apex-system-service
- android:name="com.android.apex.test.NewApexSystemService"
- android:path="/apex/com.android.apex.test/javalib/fake.jar"
+ android:name="com.android.server.testing.NewApexSystemService"
+ android:path="/apex/test_com.android.server/javalib/fake.jar"
android:minSdkVersion="999999"
/>
</application>
diff --git a/services/tests/apexsystemservices/service/Android.bp b/services/tests/apexsystemservices/services/Android.bp
similarity index 94%
rename from services/tests/apexsystemservices/service/Android.bp
rename to services/tests/apexsystemservices/services/Android.bp
index 9d04f39..477ea4c 100644
--- a/services/tests/apexsystemservices/service/Android.bp
+++ b/services/tests/apexsystemservices/services/Android.bp
@@ -8,7 +8,7 @@
}
java_library {
- name: "FakeApexSystemService",
+ name: "FakeApexSystemServices",
srcs: ["**/*.java"],
sdk_version: "system_server_current",
libs: [
diff --git a/services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java
similarity index 100%
rename from services/tests/apexsystemservices/service/src/com/android/server/testing/FakeApexSystemService.java
rename to services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService.java
diff --git a/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.java
new file mode 100644
index 0000000..e83343b
--- /dev/null
+++ b/services/tests/apexsystemservices/services/src/com/android/server/testing/FakeApexSystemService2.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.server.testing;
+
+import android.content.Context;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import com.android.server.SystemService;
+
+/**
+ * A fake system service that just logs when it is started.
+ */
+public class FakeApexSystemService2 extends SystemService {
+
+ private static final String TAG = "FakeApexSystemService";
+
+ public FakeApexSystemService2(@NonNull Context context) {
+ super(context);
+ }
+
+ @Override
+ public void onStart() {
+ Log.d(TAG, "FakeApexSystemService2 onStart");
+ }
+}
diff --git a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
index 2b453a9..10635a1 100644
--- a/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
+++ b/services/tests/apexsystemservices/src/com/android/server/ApexSystemServicesTestCases.java
@@ -37,9 +37,15 @@
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
+import java.util.Objects;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
@RunWith(DeviceJUnit4ClassRunner.class)
public class ApexSystemServicesTestCases extends BaseHostJUnit4Test {
+ private static final int REBOOT_TIMEOUT = 1 * 60 * 1000;
+
private final InstallUtilsHost mHostUtils = new InstallUtilsHost(this);
private final TemporaryFolder mTemporaryFolder = new TemporaryFolder();
private final SystemPreparer mPreparer = new SystemPreparer(mTemporaryFolder, this::getDevice);
@@ -67,7 +73,7 @@
}
@Test
- public void noApexSystemServerStartsWithoutApex() throws Exception {
+ public void testNoApexSystemServiceStartsWithoutApex() throws Exception {
mPreparer.reboot();
assertThat(getFakeApexSystemServiceLogcat())
@@ -75,20 +81,55 @@
}
@Test
- public void apexSystemServerStarts() throws Exception {
+ public void testApexSystemServiceStarts() throws Exception {
// Pre-install the apex
String apex = "test_com.android.server.apex";
mPreparer.pushResourceFile(apex, "/system/apex/" + apex);
// Reboot activates the apex
mPreparer.reboot();
+ mDevice.waitForBootComplete(REBOOT_TIMEOUT);
+
assertThat(getFakeApexSystemServiceLogcat())
.contains("FakeApexSystemService onStart");
}
+ @Test
+ public void testInitOrder() throws Exception {
+ // Pre-install the apex
+ String apex = "test_com.android.server.apex";
+ mPreparer.pushResourceFile(apex, "/system/apex/" + apex);
+ // Reboot activates the apex
+ mPreparer.reboot();
+
+ mDevice.waitForBootComplete(REBOOT_TIMEOUT);
+
+ assertThat(getFakeApexSystemServiceLogcat().lines()
+ .map(ApexSystemServicesTestCases::getDebugMessage)
+ .filter(Objects::nonNull)
+ .collect(Collectors.toList()))
+ .containsExactly(
+ // Second service has a higher initOrder and must be started first
+ "FakeApexSystemService2 onStart",
+ "FakeApexSystemService onStart"
+ )
+ .inOrder();
+ }
+
private String getFakeApexSystemServiceLogcat() throws DeviceNotAvailableException {
return mDevice.executeAdbCommand("logcat", "-v", "brief", "-d", "FakeApexSystemService:D",
"*:S");
}
+ private static final Pattern DEBUG_MESSAGE =
+ Pattern.compile("(FakeApexSystemService[0-9]* onStart)");
+
+ private static String getDebugMessage(String logcatLine) {
+ return DEBUG_MESSAGE.matcher(logcatLine)
+ .results()
+ .map(m -> m.group(1))
+ .findFirst()
+ .orElse(null);
+ }
+
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
index 28c91aa..7670953 100644
--- a/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/am/BackgroundRestrictionTest.java
@@ -101,6 +101,7 @@
import android.os.UidBatteryConsumer;
import android.os.UserHandle;
import android.provider.DeviceConfig;
+import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.Pair;
@@ -211,6 +212,7 @@
@Mock private PermissionManagerServiceInternal mPermissionManagerServiceInternal;
@Mock private MediaSessionManager mMediaSessionManager;
@Mock private RoleManager mRoleManager;
+ @Mock private TelephonyManager mTelephonyManager;
private long mCurrentTimeMillis;
@@ -2309,6 +2311,11 @@
}
@Override
+ TelephonyManager getTelephonyManager() {
+ return mTelephonyManager;
+ }
+
+ @Override
AppFGSTracker getAppFGSTracker() {
return mAppFGSTracker;
}
diff --git a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
index 08de62b..7f57119 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -60,6 +60,7 @@
import android.service.games.IGameSession;
import android.service.games.IGameSessionController;
import android.service.games.IGameSessionService;
+import android.view.SurfaceControl;
import android.view.SurfaceControlViewHost.SurfacePackage;
import androidx.test.filters.SmallTest;
@@ -746,6 +747,11 @@
mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage));
+
IGameSessionController gameSessionController = getOnlyElement(
mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
@@ -754,18 +760,28 @@
GameScreenshotResult result = resultFuture.get();
assertEquals(GameScreenshotResult.GAME_SCREENSHOT_ERROR_INTERNAL_ERROR,
result.getStatus());
- verify(mMockWindowManagerService).captureTaskBitmap(10);
+
+ verify(mMockWindowManagerService).captureTaskBitmap(eq(10), any());
}
@Test
public void takeScreenshot_success() throws Exception {
- when(mMockWindowManagerService.captureTaskBitmap(10)).thenReturn(TEST_BITMAP);
+ SurfaceControl mockOverlaySurfaceControl = Mockito.mock(SurfaceControl.class);
+ SurfaceControl[] excludeLayers = new SurfaceControl[1];
+ excludeLayers[0] = mockOverlaySurfaceControl;
+ when(mMockWindowManagerService.captureTaskBitmap(eq(10), any())).thenReturn(TEST_BITMAP);
mGameServiceProviderInstance.start();
startTask(10, GAME_A_MAIN_ACTIVITY);
mockPermissionGranted(Manifest.permission.MANAGE_GAME_ACTIVITY);
mFakeGameService.requestCreateGameSession(10);
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockOverlaySurfacePackage = Mockito.mock(SurfacePackage.class);
+ when(mockOverlaySurfacePackage.getSurfaceControl()).thenReturn(mockOverlaySurfaceControl);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockOverlaySurfacePackage));
+
IGameSessionController gameSessionController = getOnlyElement(
mFakeGameSessionService.getCapturedCreateInvocations()).mGameSessionController;
AndroidFuture<GameScreenshotResult> resultFuture = new AndroidFuture<>();
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
index bdeb2b4..f9bdad6 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/JobSchedulerServiceTest.java
@@ -204,6 +204,49 @@
jobInfoBuilder.build(), callingUid, "com.android.test", 0, testTag);
}
+ @Test
+ public void testGetMinJobExecutionGuaranteeMs() {
+ JobStatus ejMax = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(1).setExpedited(true));
+ JobStatus ejHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(2).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH));
+ JobStatus ejMaxDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(3).setExpedited(true));
+ JobStatus ejHighDowngraded = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(4).setExpedited(true).setPriority(JobInfo.PRIORITY_HIGH));
+ JobStatus jobHigh = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(5).setPriority(JobInfo.PRIORITY_HIGH));
+ JobStatus jobDef = createJobStatus("testGetMinJobExecutionGuaranteeMs",
+ createJobInfo(6));
+
+ spyOn(ejMax);
+ spyOn(ejHigh);
+ spyOn(ejMaxDowngraded);
+ spyOn(ejHighDowngraded);
+ spyOn(jobHigh);
+ spyOn(jobDef);
+
+ when(ejMax.shouldTreatAsExpeditedJob()).thenReturn(true);
+ when(ejHigh.shouldTreatAsExpeditedJob()).thenReturn(true);
+ when(ejMaxDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
+ when(ejHighDowngraded.shouldTreatAsExpeditedJob()).thenReturn(false);
+ when(jobHigh.shouldTreatAsExpeditedJob()).thenReturn(false);
+ when(jobDef.shouldTreatAsExpeditedJob()).thenReturn(false);
+
+ assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(ejMax));
+ assertEquals(mService.mConstants.RUNTIME_MIN_EJ_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(ejHigh));
+ assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(ejMaxDowngraded));
+ assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(ejHighDowngraded));
+ assertEquals(mService.mConstants.RUNTIME_MIN_HIGH_PRIORITY_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(jobHigh));
+ assertEquals(mService.mConstants.RUNTIME_MIN_GUARANTEE_MS,
+ mService.getMinJobExecutionGuaranteeMs(jobDef));
+ }
+
/**
* Confirm that {@link JobSchedulerService#getRescheduleJobForPeriodic(JobStatus)} returns a job
* with the correct delay and deadline constraints if the periodic job is scheduled with the
diff --git a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
index 153ce17..9d6793c 100644
--- a/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/job/controllers/QuotaControllerTest.java
@@ -30,6 +30,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.android.server.job.JobSchedulerService.ACTIVE_INDEX;
+import static com.android.server.job.JobSchedulerService.EXEMPTED_INDEX;
import static com.android.server.job.JobSchedulerService.FREQUENT_INDEX;
import static com.android.server.job.JobSchedulerService.NEVER_INDEX;
import static com.android.server.job.JobSchedulerService.RARE_INDEX;
@@ -536,6 +537,7 @@
ExecutionStats expectedStats = new ExecutionStats();
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_RARE;
@@ -595,6 +597,7 @@
assertNotNull(mQuotaController.getEJTimingSessions(10, "com.android.test"));
ExecutionStats expectedStats = new ExecutionStats();
+ expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
@@ -638,11 +641,13 @@
ExecutionStats expectedStats = new ExecutionStats();
ExecutionStats inputStats = new ExecutionStats();
+ inputStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
inputStats.windowSizeMs = expectedStats.windowSizeMs = 12 * HOUR_IN_MILLIS;
inputStats.jobCountLimit = expectedStats.jobCountLimit = 100;
inputStats.sessionCountLimit = expectedStats.sessionCountLimit = 100;
// Invalid time is now +24 hours since there are no sessions at all for the app.
expectedStats.expirationTimeElapsed = now + 24 * HOUR_IN_MILLIS;
+ expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
synchronized (mQuotaController.mLock) {
mQuotaController.updateExecutionStatsLocked(0, "com.android.test.not.run", inputStats);
}
@@ -827,6 +832,8 @@
ExecutionStats expectedStats = new ExecutionStats();
ExecutionStats inputStats = new ExecutionStats();
+ inputStats.allowedTimePerPeriodMs = expectedStats.allowedTimePerPeriodMs =
+ 10 * MINUTE_IN_MILLIS;
inputStats.windowSizeMs = expectedStats.windowSizeMs = 24 * HOUR_IN_MILLIS;
inputStats.jobCountLimit = expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_RARE;
inputStats.sessionCountLimit = expectedStats.sessionCountLimit =
@@ -924,6 +931,7 @@
ExecutionStats expectedStats = new ExecutionStats();
// Active
+ expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
@@ -1006,6 +1014,7 @@
ExecutionStats expectedStats = new ExecutionStats();
// Active
+ expectedStats.allowedTimePerPeriodMs = 10 * MINUTE_IN_MILLIS;
expectedStats.windowSizeMs = 10 * MINUTE_IN_MILLIS;
expectedStats.jobCountLimit = mQcConstants.MAX_JOB_COUNT_ACTIVE;
expectedStats.sessionCountLimit = mQcConstants.MAX_SESSION_COUNT_ACTIVE;
@@ -1242,6 +1251,7 @@
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 6 * HOUR_IN_MILLIS);
ExecutionStats expectedStats = new ExecutionStats();
+ expectedStats.allowedTimePerPeriodMs = originalStatsActive.allowedTimePerPeriodMs;
expectedStats.windowSizeMs = originalStatsActive.windowSizeMs;
expectedStats.jobCountLimit = originalStatsActive.jobCountLimit;
expectedStats.sessionCountLimit = originalStatsActive.sessionCountLimit;
@@ -1261,6 +1271,7 @@
assertTrue(originalStatsActive == newStatsActive);
assertEquals(expectedStats, newStatsActive);
+ expectedStats.allowedTimePerPeriodMs = originalStatsWorking.allowedTimePerPeriodMs;
expectedStats.windowSizeMs = originalStatsWorking.windowSizeMs;
expectedStats.jobCountLimit = originalStatsWorking.jobCountLimit;
expectedStats.sessionCountLimit = originalStatsWorking.sessionCountLimit;
@@ -1277,6 +1288,7 @@
assertTrue(originalStatsWorking == newStatsWorking);
assertNotEquals(expectedStats, newStatsWorking);
+ expectedStats.allowedTimePerPeriodMs = originalStatsFrequent.allowedTimePerPeriodMs;
expectedStats.windowSizeMs = originalStatsFrequent.windowSizeMs;
expectedStats.jobCountLimit = originalStatsFrequent.jobCountLimit;
expectedStats.sessionCountLimit = originalStatsFrequent.sessionCountLimit;
@@ -1293,6 +1305,7 @@
assertTrue(originalStatsFrequent == newStatsFrequent);
assertNotEquals(expectedStats, newStatsFrequent);
+ expectedStats.allowedTimePerPeriodMs = originalStatsRare.allowedTimePerPeriodMs;
expectedStats.windowSizeMs = originalStatsRare.windowSizeMs;
expectedStats.jobCountLimit = originalStatsRare.jobCountLimit;
expectedStats.sessionCountLimit = originalStatsRare.sessionCountLimit;
@@ -1354,7 +1367,8 @@
@Test
public void testGetMaxJobExecutionTimeLocked_Regular_Active() {
JobStatus job = createJobStatus("testGetMaxJobExecutionTimeLocked_Regular_Active", 0);
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 2 * HOUR_IN_MILLIS);
setDischarging();
@@ -2886,11 +2900,12 @@
public void testMaybeScheduleStartAlarmLocked_JobCount_RateLimitingWindow() {
// Set rate limiting period different from allowed time to confirm code sets based on
// the former.
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 10 * MINUTE_IN_MILLIS);
+ final int standbyBucket = WORKING_INDEX;
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 5 * MINUTE_IN_MILLIS);
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- final int standbyBucket = WORKING_INDEX;
JobStatus jobStatus = createJobStatus("testMaybeScheduleStartAlarmLocked", 1);
setStandbyBucket(standbyBucket, jobStatus);
@@ -2953,8 +2968,8 @@
@Test
public void testMaybeScheduleStartAlarmLocked_SmallRollingQuota_UpdatedAllowedTime() {
// Make sure any new value is used correctly.
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2);
runTestMaybeScheduleStartAlarmLocked_SmallRollingQuota_AllowedTimeCheck();
mQuotaController.getTimingSessions(SOURCE_USER_ID, SOURCE_PACKAGE).clear();
@@ -2977,8 +2992,8 @@
// Make sure any new value is used correctly.
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS,
mQcConstants.IN_QUOTA_BUFFER_MS * 2);
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS,
- mQcConstants.ALLOWED_TIME_PER_PERIOD_MS / 2);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS / 2);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS,
mQcConstants.MAX_EXECUTION_TIME_MS / 2);
@@ -3002,7 +3017,8 @@
// Working set window size is 2 hours.
final int standbyBucket = WORKING_INDEX;
final long contributionMs = mQcConstants.IN_QUOTA_BUFFER_MS / 2;
- final long remainingTimeMs = mQcConstants.ALLOWED_TIME_PER_PERIOD_MS - contributionMs;
+ final long remainingTimeMs =
+ mQcConstants.ALLOWED_TIME_PER_PERIOD_WORKING_MS - contributionMs;
// Session straddles edge of bucket window. Only the contribution should be counted towards
// the quota.
@@ -3062,16 +3078,28 @@
@Test
public void testConstantsUpdating_ValidValues() {
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 5 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 8 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ 5 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ 7 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 4 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ 11 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 2 * MINUTE_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, .7f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .2f);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 99 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 30 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 45 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, 60 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, 120 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, 3 * HOUR_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, 6000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, 5000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 4000);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 3000);
@@ -3079,6 +3107,7 @@
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, 2000);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * MINUTE_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 500);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, 600);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, 500);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 400);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, 300);
@@ -3088,6 +3117,7 @@
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
10 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 7 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 3 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 2 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 90 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 1 * HOUR_IN_MILLIS);
@@ -3104,10 +3134,23 @@
84 * SECOND_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 83 * SECOND_IN_MILLIS);
- assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(8 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+ assertEquals(5 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+ assertEquals(7 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+ assertEquals(2 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+ assertEquals(4 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+ assertEquals(11 * MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(2 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
assertEquals(.7f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(.2f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+ assertEquals(99 * MINUTE_IN_MILLIS,
+ mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(30 * MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(45 * MINUTE_IN_MILLIS,
@@ -3118,12 +3161,14 @@
assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(500, mQuotaController.getMaxJobCountPerRateLimitingWindow());
+ assertEquals(6000, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]);
assertEquals(5000, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
assertEquals(4000, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(3000, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
assertEquals(2000, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(50, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
+ assertEquals(600, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]);
assertEquals(500, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(400, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(300, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
@@ -3132,6 +3177,7 @@
assertEquals(10 * SECOND_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(7 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(3 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
assertEquals(2 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
assertEquals(90 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
assertEquals(HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
@@ -3151,16 +3197,24 @@
@Test
public void testConstantsUpdating_InvalidValues() {
// Test negatives/too low.
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, -MINUTE_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, -.1f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, -.01f);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RARE_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_RESTRICTED_MS, -MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MAX_EXECUTION_TIME_MS, -MINUTE_IN_MILLIS);
+ setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_EXEMPTED, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_ACTIVE, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_WORKING, 1);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_FREQUENT, 1);
@@ -3168,6 +3222,7 @@
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_RESTRICTED, -1);
setDeviceConfigLong(QcConstants.KEY_RATE_LIMITING_WINDOW_MS, 15 * SECOND_IN_MILLIS);
setDeviceConfigInt(QcConstants.KEY_MAX_JOB_COUNT_PER_RATE_LIMITING_WINDOW, 0);
+ setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_EXEMPTED, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_ACTIVE, -1);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_WORKING, 0);
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_FREQUENT, -3);
@@ -3176,6 +3231,7 @@
setDeviceConfigInt(QcConstants.KEY_MAX_SESSION_COUNT_PER_RATE_LIMITING_WINDOW, 0);
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS, -1);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, -1);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, -1);
@@ -3191,10 +3247,19 @@
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, -1);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, -1);
- assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+ assertEquals(MINUTE_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getInQuotaBufferMs());
assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(0f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+ assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(MINUTE_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -3203,12 +3268,14 @@
assertEquals(HOUR_IN_MILLIS, mQuotaController.getMaxExecutionTimeMs());
assertEquals(30 * SECOND_IN_MILLIS, mQuotaController.getRateLimitingWindowMs());
assertEquals(10, mQuotaController.getMaxJobCountPerRateLimitingWindow());
+ assertEquals(10, mQuotaController.getBucketMaxJobCounts()[EXEMPTED_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[ACTIVE_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[WORKING_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[FREQUENT_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RARE_INDEX]);
assertEquals(10, mQuotaController.getBucketMaxJobCounts()[RESTRICTED_INDEX]);
assertEquals(10, mQuotaController.getMaxSessionCountPerRateLimitingWindow());
+ assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[EXEMPTED_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[ACTIVE_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[WORKING_INDEX]);
assertEquals(1, mQuotaController.getBucketMaxSessionCounts()[FREQUENT_INDEX]);
@@ -3216,6 +3283,7 @@
assertEquals(0, mQuotaController.getBucketMaxSessionCounts()[RESTRICTED_INDEX]);
assertEquals(0, mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(0, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
assertEquals(10 * MINUTE_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
@@ -3233,17 +3301,37 @@
// Invalid configurations.
// In_QUOTA_BUFFER should never be greater than ALLOWED_TIME_PER_PERIOD
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS,
+ 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ 2 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 10 * MINUTE_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ 10 * MINUTE_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 5 * MINUTE_IN_MILLIS);
assertTrue(mQuotaController.getInQuotaBufferMs()
- <= mQuotaController.getAllowedTimePerPeriodMs());
+ <= mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
// Test larger than a day. Controller should cap at one day.
- setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_EXEMPTED_MS,
+ 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_WORKING_MS,
+ 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_FREQUENT_MS,
+ 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RARE_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_ALLOWED_TIME_PER_PERIOD_RESTRICTED_MS,
+ 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_IN_QUOTA_BUFFER_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_LOW, 1f);
setDeviceConfigFloat(QcConstants.KEY_ALLOWED_TIME_SURPLUS_PRIORITY_MIN, .95f);
+ setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_WORKING_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_WINDOW_SIZE_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
@@ -3254,6 +3342,7 @@
setDeviceConfigLong(QcConstants.KEY_TIMING_SESSION_COALESCING_DURATION_MS,
25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_MIN_QUOTA_CHECK_DELAY_MS, 25 * HOUR_IN_MILLIS);
+ setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_EXEMPTED_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_ACTIVE_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_WORKING_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_LIMIT_FREQUENT_MS, 25 * HOUR_IN_MILLIS);
@@ -3269,10 +3358,21 @@
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TEMP_ALLOWLIST_MS, 25 * HOUR_IN_MILLIS);
setDeviceConfigLong(QcConstants.KEY_EJ_GRACE_PERIOD_TOP_APP_MS, 25 * HOUR_IN_MILLIS);
- assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs());
+ assertEquals(24 * HOUR_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[EXEMPTED_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[ACTIVE_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[WORKING_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[FREQUENT_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getAllowedTimePerPeriodMs()[RARE_INDEX]);
+ assertEquals(24 * HOUR_IN_MILLIS,
+ mQuotaController.getAllowedTimePerPeriodMs()[RESTRICTED_INDEX]);
assertEquals(5 * MINUTE_IN_MILLIS, mQuotaController.getInQuotaBufferMs());
assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityLow(), 1e-6);
assertEquals(.9f, mQuotaController.getAllowedTimeSurplusPriorityMin(), 1e-6);
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[EXEMPTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[WORKING_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getBucketWindowSizes()[FREQUENT_INDEX]);
@@ -3284,6 +3384,7 @@
assertEquals(15 * MINUTE_IN_MILLIS,
mQuotaController.getTimingSessionCoalescingDurationMs());
assertEquals(15 * MINUTE_IN_MILLIS, mQuotaController.getMinQuotaCheckDelayMs());
+ assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[EXEMPTED_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[ACTIVE_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[WORKING_INDEX]);
assertEquals(24 * HOUR_IN_MILLIS, mQuotaController.getEJLimitsMs()[FREQUENT_INDEX]);
diff --git a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
index 9e1445c..bdea679 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -30,7 +30,7 @@
import android.content.Intent;
import android.media.AudioManager;
import android.media.AudioSystem;
-import android.media.BtProfileConnectionInfo;
+import android.media.BluetoothProfileConnectionInfo;
import android.util.Log;
import androidx.test.InstrumentationRegistry;
@@ -100,7 +100,7 @@
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(2 * MAX_MESSAGE_HANDLING_DELAY_MS);
verify(mSpyDevInventory, times(1)).setBluetoothActiveDevice(
any(AudioDeviceBroker.BtDeviceInfo.class)
@@ -208,13 +208,13 @@
// first connection: ensure the device is connected as a starting condition for the test
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 1), "testSource"));
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
// disconnection
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(null, mFakeBtDevice,
- BtProfileConnectionInfo.a2dpInfo(false, -1), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(false, -1), "testSource"));
if (delayAfterDisconnection > 0) {
Thread.sleep(delayAfterDisconnection);
}
@@ -222,7 +222,7 @@
// reconnection
mAudioDeviceBroker.queueOnBluetoothActiveDeviceChanged(
new AudioDeviceBroker.BtDeviceChangedData(mFakeBtDevice, null,
- BtProfileConnectionInfo.a2dpInfo(true, 2), "testSource"));
+ BluetoothProfileConnectionInfo.createA2dpInfo(true, 2), "testSource"));
Thread.sleep(AudioService.BECOMING_NOISY_DELAY_MS + MAX_MESSAGE_HANDLING_DELAY_MS);
// Verify disconnection has been cancelled and we're seeing two connections attempts,
diff --git a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
index 24a4751..f352de4 100644
--- a/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/BrightnessMappingStrategyTest.java
@@ -36,8 +36,11 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.display.whitebalance.DisplayWhiteBalanceController;
+
import org.junit.Test;
import org.junit.runner.RunWith;
+import org.mockito.Mock;
import java.util.Arrays;
@@ -147,11 +150,14 @@
private static final float TOLERANCE = 0.0001f;
+ @Mock
+ DisplayWhiteBalanceController mMockDwbc;
+
@Test
public void testSimpleStrategyMappingAtControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(PowerManager.BRIGHTNESS_OFF + 1,
@@ -166,7 +172,7 @@
public void testSimpleStrategyMappingBetweenControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy simple = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", simple);
for (int i = 1; i < LUX_LEVELS.length; i++) {
final float lux = (LUX_LEVELS[i - 1] + LUX_LEVELS[i]) / 2;
@@ -181,7 +187,7 @@
public void testSimpleStrategyIgnoresNewConfiguration() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
final float[] lux = { 0f, 1f };
final float[] nits = { 0, PowerManager.BRIGHTNESS_ON };
@@ -196,7 +202,7 @@
public void testSimpleStrategyIgnoresNullConfiguration() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
strategy.setBrightnessConfiguration(null);
final int N = DISPLAY_LEVELS_BACKLIGHT.length;
@@ -210,7 +216,7 @@
public void testPhysicalStrategyMappingAtControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
for (int i = 0; i < LUX_LEVELS.length; i++) {
final float expectedLevel = MathUtils.map(DISPLAY_RANGE_NITS[0], DISPLAY_RANGE_NITS[1],
@@ -227,7 +233,7 @@
public void testPhysicalStrategyMappingBetweenControlPoints() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", physical);
Spline brightnessToNits =
Spline.createSpline(BACKLIGHT_RANGE_ZERO_TO_ONE, DISPLAY_RANGE_NITS);
@@ -244,7 +250,7 @@
public void testPhysicalStrategyUsesNewConfigurations() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
final float[] lux = { 0f, 1f };
final float[] nits = {
@@ -269,7 +275,7 @@
public void testPhysicalStrategyRecalculateSplines() {
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS);
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
float[] adjustedNits50p = new float[DISPLAY_RANGE_NITS.length];
for (int i = 0; i < DISPLAY_RANGE_NITS.length; i++) {
adjustedNits50p[i] = DISPLAY_RANGE_NITS[i] * 0.5f;
@@ -301,7 +307,7 @@
Resources res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT,
DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertTrue(strategy instanceof BrightnessMappingStrategy.PhysicalMappingStrategy);
}
@@ -314,13 +320,13 @@
lux[idx+1] = tmp;
Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// And make sure we get the same result even if it's monotone but not increasing.
lux[idx] = lux[idx+1];
res = createResources(lux, DISPLAY_LEVELS_NITS);
- strategy = BrightnessMappingStrategy.create(res, ddc);
+ strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
}
@@ -333,11 +339,11 @@
lux[lux.length - 1] = lux[lux.length - 2] + 1;
Resources res = createResources(lux, DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
res = createResources(lux, DISPLAY_LEVELS_BACKLIGHT);
- strategy = BrightnessMappingStrategy.create(res, ddc);
+ strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra backlight level
@@ -345,14 +351,14 @@
DISPLAY_LEVELS_BACKLIGHT, DISPLAY_LEVELS_BACKLIGHT.length+1);
backlight[backlight.length - 1] = backlight[backlight.length - 2] + 1;
res = createResources(LUX_LEVELS, backlight);
- strategy = BrightnessMappingStrategy.create(res, ddc);
+ strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
// Extra nits level
final float[] nits = Arrays.copyOf(DISPLAY_RANGE_NITS, DISPLAY_LEVELS_NITS.length+1);
nits[nits.length - 1] = nits[nits.length - 2] + 1;
res = createResources(LUX_LEVELS, nits);
- strategy = BrightnessMappingStrategy.create(res, ddc);
+ strategy = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(strategy);
}
@@ -361,17 +367,17 @@
Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc(EMPTY_FLOAT_ARRAY /*nitsRange*/);
- BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc);
+ BrightnessMappingStrategy physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
DISPLAY_LEVELS_NITS);
- physical = BrightnessMappingStrategy.create(res, ddc);
+ physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
DISPLAY_LEVELS_NITS);
- physical = BrightnessMappingStrategy.create(res, ddc);
+ physical = BrightnessMappingStrategy.create(res, ddc, mMockDwbc);
assertNull(physical);
}
@@ -380,10 +386,10 @@
Resources res = createResources(LUX_LEVELS, EMPTY_INT_ARRAY /*brightnessLevelsBacklight*/,
DISPLAY_LEVELS_NITS);
DisplayDeviceConfig ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc));
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
ddc = createDdc(DISPLAY_RANGE_NITS, BACKLIGHT_RANGE_ZERO_TO_ONE);
res = createResources(LUX_LEVELS, DISPLAY_LEVELS_BACKLIGHT);
- assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc));
+ assertStrategyAdaptsToUserDataPoints(BrightnessMappingStrategy.create(res, ddc, mMockDwbc));
}
@Test
@@ -394,7 +400,7 @@
// Create an idle mode bms
// This will fail if it tries to fetch the wrong configuration.
BrightnessMappingStrategy bms = BrightnessMappingStrategy.createForIdleMode(res, ddc,
- null);
+ mMockDwbc);
assertNotNull("BrightnessMappingStrategy should not be null", bms);
// Ensure that the config is the one we set
@@ -586,7 +592,8 @@
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
+ mMockDwbc);
// Let's start with a validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -614,7 +621,8 @@
final float y3 = GAMMA_CORRECTION_SPLINE.interpolate(x3);
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
+ mMockDwbc);
// Validity check:
assertEquals(y1, strategy.getBrightness(x1), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
@@ -639,7 +647,8 @@
// just make sure the adjustment reflects the change.
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
+ mMockDwbc);
assertEquals(0.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
strategy.addUserDataPoint(2500, 1.0f);
assertEquals(+1.0f, strategy.getAutoBrightnessAdjustment(), 0.0001f /* tolerance */);
@@ -660,7 +669,8 @@
final float y4 = GAMMA_CORRECTION_SPLINE.interpolate(x4);
Resources resources = createResources(GAMMA_CORRECTION_LUX, GAMMA_CORRECTION_NITS);
DisplayDeviceConfig ddc = createDdc();
- BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc);
+ BrightnessMappingStrategy strategy = BrightnessMappingStrategy.create(resources, ddc,
+ mMockDwbc);
// Validity, as per tradition:
assertEquals(y0, strategy.getBrightness(x0), 0.0001f /* tolerance */);
assertEquals(y2, strategy.getBrightness(x2), 0.0001f /* tolerance */);
diff --git a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
index 6203c2f..53fa3e2 100644
--- a/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/HighBrightnessModeControllerTest.java
@@ -22,11 +22,14 @@
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_OFF;
import static android.hardware.display.BrightnessInfo.HIGH_BRIGHTNESS_MODE_SUNLIGHT;
+
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_DISABLED;
import static com.android.server.display.AutomaticBrightnessController.AUTO_BRIGHTNESS_ENABLED;
import static com.android.server.display.AutomaticBrightnessController
.AUTO_BRIGHTNESS_OFF_DUE_TO_DISPLAY_STATE;
+import static com.android.server.display.DisplayDeviceConfig.HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT;
+
import static com.android.server.display.HighBrightnessModeController.HBM_TRANSITION_POINT_INVALID;
import static org.junit.Assert.assertEquals;
@@ -111,7 +114,8 @@
private static final HighBrightnessModeData DEFAULT_HBM_DATA =
new HighBrightnessModeData(MINIMUM_LUX, TRANSITION_POINT, TIME_WINDOW_MILLIS,
TIME_ALLOWED_IN_WINDOW_MILLIS, TIME_MINIMUM_AVAILABLE_TO_ENABLE_MILLIS,
- THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE);
+ THERMAL_STATUS_LIMIT, ALLOW_IN_LOW_POWER_MODE,
+ HDR_PERCENT_OF_SCREEN_REQUIRED_DEFAULT);
@Before
public void setUp() {
@@ -136,7 +140,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
assertEquals(hbmc.getTransitionPoint(), HBM_TRANSITION_POINT_INVALID, 0.0f);
}
@@ -146,7 +150,7 @@
initHandler(null);
final HighBrightnessModeController hbmc = new HighBrightnessModeController(
mInjectorMock, mHandler, DISPLAY_WIDTH, DISPLAY_HEIGHT, mDisplayToken,
- mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, () -> {}, mContextSpy);
+ mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX, null, null, () -> {}, mContextSpy);
hbmc.setAutoBrightnessEnabled(AUTO_BRIGHTNESS_ENABLED);
hbmc.onAmbientLuxChange(MINIMUM_LUX - 1); // below allowed range
assertState(hbmc, DEFAULT_MIN, DEFAULT_MAX, HIGH_BRIGHTNESS_MODE_OFF);
@@ -703,7 +707,7 @@
initHandler(clock);
return new HighBrightnessModeController(mInjectorMock, mHandler, DISPLAY_WIDTH,
DISPLAY_HEIGHT, mDisplayToken, mDisplayUniqueId, DEFAULT_MIN, DEFAULT_MAX,
- DEFAULT_HBM_DATA, () -> {}, mContextSpy);
+ DEFAULT_HBM_DATA, null, () -> {}, mContextSpy);
}
private void initHandler(OffsettableClock clock) {
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
index ae7b494..8e756ae 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -320,8 +320,10 @@
@Test
public void getDefaultStringValue_MultipleDefaults() {
setBooleanResource(R.bool.config_cecPowerControlModeBroadcast_default, true);
- assertThrows(RuntimeException.class,
- () -> new HdmiCecConfig(mContext, mStorageAdapter));
+ HdmiCecConfig hdmiCecConfig = new HdmiCecConfig(mContext, mStorageAdapter);
+ assertThat(hdmiCecConfig.getDefaultStringValue(
+ HdmiControlManager.CEC_SETTING_NAME_POWER_CONTROL_MODE))
+ .isEqualTo(HdmiControlManager.POWER_CONTROL_MODE_BROADCAST);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
index 7f7c716..2f5993d1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/ApexManagerTest.java
@@ -61,7 +61,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
-import java.util.Map;
+import java.util.List;
@SmallTest
@Presubmit
@@ -136,9 +136,10 @@
mApexManager.scanApexPackagesTraced(mPackageParser2,
ParallelPackageParser.makeExecutorService());
- Map<String, String> services = mApexManager.getApexSystemServices();
+ List<ApexSystemServiceInfo> services = mApexManager.getApexSystemServices();
assertThat(services).hasSize(1);
- assertThat(services).containsKey("com.android.apex.test.ApexSystemService");
+ assertThat(services.stream().map(ApexSystemServiceInfo::getName).findFirst().orElse(null))
+ .matches("com.android.apex.test.ApexSystemService");
}
@Test
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
index 7542033..7e27e54 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationRecordTest.java
@@ -66,6 +66,7 @@
import android.provider.Settings;
import android.service.notification.Adjustment;
import android.service.notification.StatusBarNotification;
+import android.util.ArraySet;
import android.widget.RemoteViews;
import androidx.test.filters.SmallTest;
@@ -1304,4 +1305,45 @@
assertFalse(record.isConversation());
}
+
+ @Test
+ public void mergePhoneNumbers_nulls() {
+ // make sure nothing dies if we just don't have any phone numbers
+ StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, null /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ // by default, no phone numbers
+ assertNull(record.getPhoneNumbers());
+
+ // nothing happens if we attempt to merge phone numbers but there aren't any
+ record.mergePhoneNumbers(null);
+ assertNull(record.getPhoneNumbers());
+ }
+
+ @Test
+ public void mergePhoneNumbers_addNumbers() {
+ StatusBarNotification sbn = getNotification(PKG_N_MR1, true /* noisy */,
+ true /* defaultSound */, false /* buzzy */, false /* defaultBuzz */,
+ false /* lights */, false /* defaultLights */, null /* group */);
+ NotificationRecord record = new NotificationRecord(mMockContext, sbn, defaultChannel);
+
+ // by default, no phone numbers
+ assertNull(record.getPhoneNumbers());
+
+ // make sure it behaves properly when we merge in some real content
+ record.mergePhoneNumbers(new ArraySet<>(
+ new String[]{"16175551212", "16175552121"}));
+ assertTrue(record.getPhoneNumbers().contains("16175551212"));
+ assertTrue(record.getPhoneNumbers().contains("16175552121"));
+ assertFalse(record.getPhoneNumbers().contains("16175553434"));
+
+ // now merge in a new number, make sure old ones are still there and the new one
+ // is also there
+ record.mergePhoneNumbers(new ArraySet<>(new String[]{"16175553434"}));
+ assertTrue(record.getPhoneNumbers().contains("16175551212"));
+ assertTrue(record.getPhoneNumbers().contains("16175552121"));
+ assertTrue(record.getPhoneNumbers().contains("16175553434"));
+ }
}
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
index 0bf105d..0552a83 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ValidateNotificationPeopleTest.java
@@ -19,8 +19,13 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.contains;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -29,6 +34,7 @@
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.Context;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.UserManager;
@@ -43,6 +49,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
@@ -240,6 +248,118 @@
assertFalse(ContentProvider.uriHasUserId(queryUri.getValue()));
}
+ @Test
+ public void testMergePhoneNumbers_noPhoneNumber() {
+ // If merge phone number is called but the contacts lookup turned up no available
+ // phone number (HAS_PHONE_NUMBER is false), then no query should happen.
+
+ // setup of various bits required for querying
+ final Context mockContext = mock(Context.class);
+ final ContentResolver mockContentResolver = mock(ContentResolver.class);
+ when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+ final int contactId = 12345;
+ final Uri lookupUri = Uri.withAppendedPath(
+ ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId));
+
+ // when the contact is looked up, we return a cursor that has one entry whose info is:
+ // _ID: 1
+ // LOOKUP_KEY: "testlookupkey"
+ // STARRED: 0
+ // HAS_PHONE_NUMBER: 0
+ Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 0);
+ when(mockContentResolver.query(any(), any(), any(), any(), any())).thenReturn(cursor);
+
+ // call searchContacts and then mergePhoneNumbers, make sure we never actually
+ // query the content resolver for a phone number
+ new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+ verify(mockContentResolver, never()).query(
+ eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+ eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+ contains(ContactsContract.Contacts.LOOKUP_KEY),
+ any(), // selection args
+ isNull()); // sort order
+ }
+
+ @Test
+ public void testMergePhoneNumbers_hasNumber() {
+ // If merge phone number is called and the contact lookup has a phone number,
+ // make sure there's then a subsequent query for the phone number.
+
+ // setup of various bits required for querying
+ final Context mockContext = mock(Context.class);
+ final ContentResolver mockContentResolver = mock(ContentResolver.class);
+ when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
+ final int contactId = 12345;
+ final Uri lookupUri = Uri.withAppendedPath(
+ ContactsContract.Contacts.CONTENT_LOOKUP_URI, String.valueOf(contactId));
+
+ // when the contact is looked up, we return a cursor that has one entry whose info is:
+ // _ID: 1
+ // LOOKUP_KEY: "testlookupkey"
+ // STARRED: 0
+ // HAS_PHONE_NUMBER: 1
+ Cursor cursor = makeMockCursor(1, "testlookupkey", 0, 1);
+
+ // make sure to add some specifics so this cursor is only returned for the
+ // contacts database lookup.
+ when(mockContentResolver.query(eq(lookupUri), any(),
+ isNull(), isNull(), isNull())).thenReturn(cursor);
+
+ // in the case of a phone lookup, return null cursor; that's not an error case
+ // and we're not checking the actual storing of the phone data here.
+ when(mockContentResolver.query(eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+ eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+ contains(ContactsContract.Contacts.LOOKUP_KEY),
+ any(), isNull())).thenReturn(null);
+
+ // call searchContacts and then mergePhoneNumbers, and check that we query
+ // once for the
+ new ValidateNotificationPeople().searchContactsAndLookupNumbers(mockContext, lookupUri);
+ verify(mockContentResolver, times(1)).query(
+ eq(ContactsContract.CommonDataKinds.Phone.CONTENT_URI),
+ eq(new String[] { ContactsContract.CommonDataKinds.Phone.NORMALIZED_NUMBER }),
+ contains(ContactsContract.Contacts.LOOKUP_KEY),
+ eq(new String[] { "testlookupkey" }), // selection args
+ isNull()); // sort order
+ }
+
+ // Creates a cursor that points to one item of Contacts data with the specified
+ // columns.
+ private Cursor makeMockCursor(int id, String lookupKey, int starred, int hasPhone) {
+ Cursor mockCursor = mock(Cursor.class);
+ when(mockCursor.moveToFirst()).thenReturn(true);
+ doAnswer(new Answer<Boolean>() {
+ boolean mAccessed = false;
+ @Override
+ public Boolean answer(InvocationOnMock invocation) throws Throwable {
+ if (!mAccessed) {
+ mAccessed = true;
+ return true;
+ }
+ return false;
+ }
+
+ }).when(mockCursor).moveToNext();
+
+ // id
+ when(mockCursor.getColumnIndex(ContactsContract.Contacts._ID)).thenReturn(0);
+ when(mockCursor.getInt(0)).thenReturn(id);
+
+ // lookup key
+ when(mockCursor.getColumnIndex(ContactsContract.Contacts.LOOKUP_KEY)).thenReturn(1);
+ when(mockCursor.getString(1)).thenReturn(lookupKey);
+
+ // starred
+ when(mockCursor.getColumnIndex(ContactsContract.Contacts.STARRED)).thenReturn(2);
+ when(mockCursor.getInt(2)).thenReturn(starred);
+
+ // has phone number
+ when(mockCursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)).thenReturn(3);
+ when(mockCursor.getInt(3)).thenReturn(hasPhone);
+
+ return mockCursor;
+ }
+
private void assertStringArrayEquals(String message, String[] expected, String[] result) {
String expectedString = Arrays.toString(expected);
String resultString = Arrays.toString(result);
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
index 0f18cc6..abcc8c1 100644
--- a/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/ZenModeFilteringTest.java
@@ -50,11 +50,13 @@
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
+import android.util.ArraySet;
import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
import com.android.internal.util.NotificationMessagingUtil;
import com.android.server.UiServiceTestCase;
+import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -72,6 +74,8 @@
@Mock private TelephonyManager mTelephonyManager;
+ private long mTestStartTime;
+
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
@@ -79,6 +83,13 @@
// for repeat callers / matchesCallFilter
mContext.addMockSystemService(TelephonyManager.class, mTelephonyManager);
+ mTestStartTime = System.currentTimeMillis();
+ }
+
+ @After
+ public void tearDown() {
+ // make sure to get rid of any data stored in repeat callers
+ mZenModeFiltering.cleanUpCallersAfter(mTestStartTime);
}
private NotificationRecord getNotificationRecord() {
@@ -108,7 +119,10 @@
return extras;
}
- private NotificationRecord getNotificationRecordWithPeople(String[] people) {
+ // Create a notification record with the people String array as the
+ // bundled extras, and the numbers ArraySet as additional phone numbers.
+ private NotificationRecord getRecordWithPeopleInfo(String[] people,
+ ArraySet<String> numbers) {
// set up notification record
NotificationRecord r = mock(NotificationRecord.class);
StatusBarNotification sbn = mock(StatusBarNotification.class);
@@ -116,6 +130,7 @@
notification.extras = makeExtrasBundleWithPeople(people);
when(sbn.getNotification()).thenReturn(notification);
when(r.getSbn()).thenReturn(sbn);
+ when(r.getPhoneNumbers()).thenReturn(numbers);
return r;
}
@@ -339,7 +354,7 @@
// after calls given an email with an exact string match, make sure that
// matchesCallFilter returns the right thing
String[] mailSource = new String[]{"mailto:hello.world"};
- mZenModeFiltering.recordCall(getNotificationRecordWithPeople(mailSource));
+ mZenModeFiltering.recordCall(getRecordWithPeopleInfo(mailSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -362,7 +377,7 @@
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:+1-617-555-1212"};
- mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+ mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -406,7 +421,7 @@
when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
String[] telSource = new String[]{"tel:%2B16175551212"};
- mZenModeFiltering.recordCall(getNotificationRecordWithPeople(telSource));
+ mZenModeFiltering.recordCall(getRecordWithPeopleInfo(telSource, null));
// set up policy to only allow repeat callers
Policy policy = new Policy(
@@ -419,25 +434,64 @@
Bundle different1 = makeExtrasBundleWithPeople(new String[]{"tel:%2B16175553434"});
Bundle different2 = makeExtrasBundleWithPeople(new String[]{"tel:+16175553434"});
- assertTrue("same number should match",
+ assertTrue("same number 1 should match",
ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
policy, UserHandle.SYSTEM,
same1, null, 0, 0));
- assertTrue("same number should match",
+ assertTrue("same number 2 should match",
ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
policy, UserHandle.SYSTEM,
same2, null, 0, 0));
- assertTrue("same number should match",
+ assertTrue("same number 3 should match",
ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
policy, UserHandle.SYSTEM,
same3, null, 0, 0));
- assertFalse("different number should not match",
+ assertFalse("different number 1 should not match",
ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
policy, UserHandle.SYSTEM,
different1, null, 0, 0));
- assertFalse("different number should not match",
+ assertFalse("different number 2 should not match",
ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
policy, UserHandle.SYSTEM,
different2, null, 0, 0));
}
+
+ @Test
+ public void testMatchesCallFilter_repeatCallers_viaRecordPhoneNumbers() {
+ // make sure that phone numbers that are passed in via the NotificationRecord's
+ // cached phone numbers field (from a contact lookup if the record is provided a contact
+ // uri) also get recorded in the repeat callers list.
+
+ // set up telephony manager behavior
+ when(mTelephonyManager.getNetworkCountryIso()).thenReturn("us");
+
+ String[] contactSource = new String[]{"content://contacts/lookup/uri-here"};
+ ArraySet<String> contactNumbers = new ArraySet<>(
+ new String[]{"1-617-555-1212", "1-617-555-3434"});
+ NotificationRecord record = getRecordWithPeopleInfo(contactSource, contactNumbers);
+ record.mergePhoneNumbers(contactNumbers);
+ mZenModeFiltering.recordCall(record);
+
+ // set up policy to only allow repeat callers
+ Policy policy = new Policy(
+ PRIORITY_CATEGORY_REPEAT_CALLERS, 0, 0, 0, CONVERSATION_SENDERS_NONE);
+
+ // both phone numbers should register here
+ Bundle tel1 = makeExtrasBundleWithPeople(new String[]{"tel:+1-617-555-1212"});
+ Bundle tel2 = makeExtrasBundleWithPeople(new String[]{"tel:16175553434"});
+ Bundle different = makeExtrasBundleWithPeople(new String[]{"tel:16175555656"});
+
+ assertTrue("contact number 1 should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ tel1, null, 0, 0));
+ assertTrue("contact number 2 should match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ tel2, null, 0, 0));
+ assertFalse("different number should not match",
+ ZenModeFiltering.matchesCallFilter(mContext, ZEN_MODE_IMPORTANT_INTERRUPTIONS,
+ policy, UserHandle.SYSTEM,
+ different, null, 0, 0));
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
index 687779d..fb3a6264 100644
--- a/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/BackNavigationControllerTests.java
@@ -16,6 +16,7 @@
package com.android.server.wm;
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
import static android.window.BackNavigationInfo.typeToString;
import static com.google.common.truth.Truth.assertThat;
@@ -71,7 +72,8 @@
@Test
public void backTypeCrossActivityWhenBackToPreviousActivity() {
Task task = createTopTaskWithActivity();
- mAtm.setFocusedTask(task.mTaskId, createActivityRecord(task));
+ mAtm.setFocusedTask(task.mTaskId,
+ createAppWindow(task, FIRST_APPLICATION_WINDOW, "window").mActivityRecord);
BackNavigationInfo backNavigationInfo =
mBackNavigationController.startBackNavigation(task, new StubTransaction());
assertThat(backNavigationInfo).isNotNull();
@@ -85,7 +87,7 @@
@Test
public void backNavInfoFullyPopulated() {
Task task = createTopTaskWithActivity();
- createActivityRecord(task);
+ createAppWindow(task, FIRST_APPLICATION_WINDOW, "window");
// We need a mock screenshot so
TaskSnapshotController taskSnapshotController = createMockTaskSnapshotController();
@@ -115,6 +117,7 @@
private Task createTopTaskWithActivity() {
Task task = createTask(mDefaultDisplay);
ActivityRecord record = createActivityRecord(task);
+ createWindow(null, FIRST_APPLICATION_WINDOW, record, "window");
when(record.mSurfaceControl.isValid()).thenReturn(true);
mAtm.setFocusedTask(task.mTaskId, record);
return task;
diff --git a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
index 65b5cf5..dcaa511 100644
--- a/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/RootTaskTests.java
@@ -80,7 +80,6 @@
import android.app.WindowConfiguration;
import android.content.ComponentName;
import android.content.pm.ActivityInfo;
-import android.graphics.Rect;
import android.os.Binder;
import android.os.UserHandle;
import android.platform.test.annotations.Presubmit;
@@ -231,34 +230,6 @@
}
@Test
- public void testTaskOutset() {
- final Task task = createTask(mDisplayContent);
- final int taskOutset = 10;
- spyOn(task);
- doReturn(taskOutset).when(task).getTaskOutset();
- doReturn(true).when(task).inMultiWindowMode();
-
- // Mock the resolved override windowing mode to non-fullscreen
- final WindowConfiguration windowConfiguration =
- task.getResolvedOverrideConfiguration().windowConfiguration;
- spyOn(windowConfiguration);
- doReturn(WINDOWING_MODE_MULTI_WINDOW)
- .when(windowConfiguration).getWindowingMode();
-
- // Prevent adjust task dimensions
- doNothing().when(task).adjustForMinimalTaskDimensions(any(), any(), any());
-
- final Rect taskBounds = new Rect(200, 200, 800, 1000);
- // Update surface position and size by the given bounds.
- task.setBounds(taskBounds);
-
- assertEquals(taskBounds.width() + 2 * taskOutset, task.getLastSurfaceSize().x);
- assertEquals(taskBounds.height() + 2 * taskOutset, task.getLastSurfaceSize().y);
- assertEquals(taskBounds.left - taskOutset, task.getLastSurfacePosition().x);
- assertEquals(taskBounds.top - taskOutset, task.getLastSurfacePosition().y);
- }
-
- @Test
public void testActivityAndTaskGetsProperType() {
final Task task1 = new TaskBuilder(mSupervisor).build();
ActivityRecord activity1 = createNonAttachedActivityRecord(mDisplayContent);
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 98a41bc..4a761a7 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -1814,6 +1814,7 @@
synchronized (mLock) {
mResponseStatsTracker.dump(idpw);
}
+ return;
} else if (arg != null && !arg.startsWith("-")) {
// Anything else that doesn't start with '-' is a pkg to filter
pkgs.add(arg);
diff --git a/telephony/java/android/telephony/Annotation.java b/telephony/java/android/telephony/Annotation.java
index e88106c..86b98f1 100644
--- a/telephony/java/android/telephony/Annotation.java
+++ b/telephony/java/android/telephony/Annotation.java
@@ -127,7 +127,9 @@
ApnSetting.TYPE_EMERGENCY,
ApnSetting.TYPE_MCX,
ApnSetting.TYPE_XCAP,
- // ApnSetting.TYPE_ENTERPRISE
+ ApnSetting.TYPE_BIP,
+ ApnSetting.TYPE_VSIM,
+ ApnSetting.TYPE_ENTERPRISE
})
@Retention(RetentionPolicy.SOURCE)
public @interface ApnType {
@@ -707,6 +709,9 @@
NetworkCapabilities.NET_CAPABILITY_VSIM,
NetworkCapabilities.NET_CAPABILITY_BIP,
NetworkCapabilities.NET_CAPABILITY_HEAD_UNIT,
+ NetworkCapabilities.NET_CAPABILITY_MMTEL,
+ NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_LATENCY,
+ NetworkCapabilities.NET_CAPABILITY_PRIORITIZE_BANDWIDTH
})
public @interface NetCapability { }
@@ -721,4 +726,16 @@
NetworkAgent.VALIDATION_STATUS_NOT_VALID
})
public @interface ValidationStatus {}
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = { "NET_CAPABILITY_ENTERPRISE_SUB_LEVEL" }, value = {
+ NetworkCapabilities.NET_ENTERPRISE_ID_1,
+ NetworkCapabilities.NET_ENTERPRISE_ID_2,
+ NetworkCapabilities.NET_ENTERPRISE_ID_3,
+ NetworkCapabilities.NET_ENTERPRISE_ID_4,
+ NetworkCapabilities.NET_ENTERPRISE_ID_5
+ })
+
+ public @interface EnterpriseId {}
}
diff --git a/telephony/java/android/telephony/data/DataProfile.java b/telephony/java/android/telephony/data/DataProfile.java
index a166a5d..fa1bae4 100644
--- a/telephony/java/android/telephony/data/DataProfile.java
+++ b/telephony/java/android/telephony/data/DataProfile.java
@@ -26,10 +26,10 @@
import android.net.NetworkCapabilities;
import android.os.Parcel;
import android.os.Parcelable;
-import android.telephony.Annotation.ApnType;
import android.telephony.Annotation.NetCapability;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.NetworkTypeBitMask;
+import android.telephony.data.ApnSetting.ApnType;
import android.telephony.data.ApnSetting.AuthType;
import android.text.TextUtils;
@@ -245,8 +245,7 @@
* @return The supported APN types bitmask.
* @deprecated Use {@link #getApnSetting()} and {@link ApnSetting#getApnTypeBitmask()} instead.
*/
- @Deprecated
- public @ApnType int getSupportedApnTypesBitmask() {
+ @Deprecated public @ApnType int getSupportedApnTypesBitmask() {
if (mApnSetting != null) {
return mApnSetting.getApnTypeBitmask();
}
@@ -425,6 +424,12 @@
return ApnSetting.TYPE_MCX;
case NetworkCapabilities.NET_CAPABILITY_IA:
return ApnSetting.TYPE_IA;
+ case NetworkCapabilities.NET_CAPABILITY_BIP:
+ return ApnSetting.TYPE_BIP;
+ case NetworkCapabilities.NET_CAPABILITY_VSIM:
+ return ApnSetting.TYPE_VSIM;
+ case NetworkCapabilities.NET_CAPABILITY_ENTERPRISE:
+ return ApnSetting.TYPE_ENTERPRISE;
default:
return ApnSetting.TYPE_NONE;
}
diff --git a/telephony/java/android/telephony/data/TrafficDescriptor.java b/telephony/java/android/telephony/data/TrafficDescriptor.java
index 2178fc1..66dcf8f 100644
--- a/telephony/java/android/telephony/data/TrafficDescriptor.java
+++ b/telephony/java/android/telephony/data/TrafficDescriptor.java
@@ -21,8 +21,13 @@
import android.os.Parcel;
import android.os.Parcelable;
-import java.util.Arrays;
+import java.math.BigInteger;
+import java.nio.ByteBuffer;
import java.util.Objects;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
* A traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2. It is used for UE Route Selection
@@ -31,24 +36,215 @@
* not specify the end point to be used for the data call.
*/
public final class TrafficDescriptor implements Parcelable {
+ /**
+ * The OS/App id
+ *
+ * @hide
+ */
+ public static final class OsAppId {
+ /**
+ * OSId for "Android", using UUID version 5 with namespace ISO OSI.
+ * Prepended to the OsAppId in TrafficDescriptor to use for URSP matching.
+ */
+ public static final UUID ANDROID_OS_ID =
+ UUID.fromString("97a498e3-fc92-5c94-8986-0333d06e4e47");
+
+ /**
+ * Allowed app ids.
+ */
+ // The following app ids are the only apps id Android supports. OEMs or vendors are
+ // prohibited to modify/extend the allowed list, especially passing the real package name to
+ // the network.
+ private static final Set<String> ALLOWED_APP_IDS = Set.of(
+ "ENTERPRISE", "PRIORITIZE_LATENCY", "PRIORITIZE_BANDWIDTH", "CBS"
+ );
+
+ /** OS id in UUID format. */
+ private final @NonNull UUID mOsId;
+
+ /**
+ * App id in string format. Note that Android will not allow use specific app id. This must
+ * be a category/capability identifier.
+ */
+ private final @NonNull String mAppId;
+
+ /**
+ * The differentiator when multiple traffic descriptor has the same OS and app id. Must be
+ * greater than 1.
+ */
+ private final int mDifferentiator;
+
+ /**
+ * Constructor
+ *
+ * @param osId OS id in UUID format.
+ * @param appId App id in string format. Note that Android will not allow use specific app
+ * id. This must be a category/capability identifier.
+ */
+ public OsAppId(@NonNull UUID osId, @NonNull String appId) {
+ this(osId, appId, 1);
+ }
+
+ /**
+ * Constructor
+ *
+ * @param osId OS id in UUID format.
+ * @param appId App id in string format. Note that Android will not allow use specific app
+ * id. This must be a category/capability identifier.
+ * @param differentiator The differentiator when multiple traffic descriptor has the same
+ * OS and app id. Must be greater than 0.
+ */
+ public OsAppId(@NonNull UUID osId, @NonNull String appId, int differentiator) {
+ Objects.requireNonNull(osId);
+ Objects.requireNonNull(appId);
+ if (differentiator < 1) {
+ throw new IllegalArgumentException("Invalid differentiator " + differentiator);
+ }
+
+ mOsId = osId;
+ mAppId = appId;
+ mDifferentiator = differentiator;
+ }
+
+ /**
+ * Constructor from raw byte array.
+ *
+ * @param rawOsAppId The raw OS/App id.
+ */
+ public OsAppId(@NonNull byte[] rawOsAppId) {
+ try {
+ ByteBuffer bb = ByteBuffer.wrap(rawOsAppId);
+ // OS id is the first 16 bytes.
+ mOsId = new UUID(bb.getLong(), bb.getLong());
+ // App id length is 1 byte.
+ int appIdLen = bb.get();
+ // The remaining is the app id + differentiator.
+ byte[] appIdAndDifferentiator = new byte[appIdLen];
+ bb.get(appIdAndDifferentiator, 0, appIdLen);
+ // Extract trailing numbers, for example, "ENTERPRISE", "ENTERPRISE3".
+ String appIdAndDifferentiatorStr = new String(appIdAndDifferentiator);
+ Pattern pattern = Pattern.compile("[^0-9]+([0-9]+)$");
+ Matcher matcher = pattern.matcher(new String(appIdAndDifferentiator));
+ if (matcher.find()) {
+ mDifferentiator = Integer.parseInt(matcher.group(1));
+ mAppId = appIdAndDifferentiatorStr.replace(matcher.group(1), "");
+ } else {
+ mDifferentiator = 1;
+ mAppId = appIdAndDifferentiatorStr;
+ }
+ } catch (Exception e) {
+ throw new IllegalArgumentException("Failed to decode " + (rawOsAppId != null
+ ? new BigInteger(1, rawOsAppId).toString(16) : null));
+ }
+ }
+
+ /**
+ * @return The OS id in UUID format.
+ */
+ public @NonNull UUID getOsId() {
+ return mOsId;
+ }
+
+ /**
+ * @return App id in string format. Note that Android will not allow use specific app id.
+ * This must be a category/capability identifier.
+ */
+ public @NonNull String getAppId() {
+ return mAppId;
+ }
+
+ /**
+ * @return The differentiator when multiple traffic descriptor has the same OS and app id.
+ * Must be greater than 1.
+ */
+ public int getDifferentiator() {
+ return mDifferentiator;
+ }
+
+ /**
+ * @return OS/App id in raw byte format.
+ */
+ public @NonNull byte[] getBytes() {
+ byte[] osAppId = (mAppId + (mDifferentiator > 1 ? mDifferentiator : "")).getBytes();
+ // 16 bytes for UUID, 1 byte for length of osAppId, and up to 255 bytes for osAppId
+ ByteBuffer bb = ByteBuffer.allocate(16 + 1 + osAppId.length);
+ bb.putLong(mOsId.getMostSignificantBits());
+ bb.putLong(mOsId.getLeastSignificantBits());
+ bb.put((byte) osAppId.length);
+ bb.put(osAppId);
+ return bb.array();
+ }
+
+ @Override
+ public String toString() {
+ return "[OsAppId: OS=" + mOsId + ", App=" + mAppId + ", differentiator="
+ + mDifferentiator + ", raw="
+ + new BigInteger(1, getBytes()).toString(16) + "]";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ OsAppId osAppId = (OsAppId) o;
+ return mDifferentiator == osAppId.mDifferentiator && mOsId.equals(osAppId.mOsId)
+ && mAppId.equals(osAppId.mAppId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mOsId, mAppId, mDifferentiator);
+ }
+ }
+
private final String mDnn;
- private final byte[] mOsAppId;
+ private final OsAppId mOsAppId;
private TrafficDescriptor(@NonNull Parcel in) {
mDnn = in.readString();
- mOsAppId = in.createByteArray();
+ byte[] osAppIdBytes = in.createByteArray();
+ OsAppId osAppId = null;
+ if (osAppIdBytes != null) {
+ osAppId = new OsAppId(osAppIdBytes);
+ }
+ mOsAppId = osAppId;
+
+ enforceAllowedIds();
}
/**
* Create a traffic descriptor, as defined in 3GPP TS 24.526 Section 5.2
* @param dnn optional DNN, which must be used for traffic matching, if present
- * @param osAppId OsId + osAppId of the traffic descriptor
+ * @param osAppIdRawBytes Raw bytes of OsId + osAppId of the traffic descriptor
*
* @hide
*/
- public TrafficDescriptor(String dnn, byte[] osAppId) {
+ public TrafficDescriptor(String dnn, @Nullable byte[] osAppIdRawBytes) {
mDnn = dnn;
+ OsAppId osAppId = null;
+ if (osAppIdRawBytes != null) {
+ osAppId = new OsAppId(osAppIdRawBytes);
+ }
mOsAppId = osAppId;
+
+ enforceAllowedIds();
+ }
+
+ /**
+ * Enforce the OS id and app id are in the allowed list.
+ *
+ * @throws IllegalArgumentException if ids are not allowed.
+ */
+ private void enforceAllowedIds() {
+ if (mOsAppId != null && !mOsAppId.getOsId().equals(OsAppId.ANDROID_OS_ID)) {
+ throw new IllegalArgumentException("OS id " + mOsAppId.getOsId() + " does not match "
+ + OsAppId.ANDROID_OS_ID);
+ }
+
+ if (mOsAppId != null && !OsAppId.ALLOWED_APP_IDS.contains(mOsAppId.getAppId())) {
+ throw new IllegalArgumentException("Illegal app id " + mOsAppId.getAppId()
+ + ". Only allowing one of the following " + OsAppId.ALLOWED_APP_IDS);
+ }
}
/**
@@ -61,13 +257,13 @@
}
/**
- * OsAppId is the app id as defined in 3GPP TS 24.526 Section 5.2, and it identifies a traffic
- * category. It includes the OS Id component of the field as defined in the specs.
- * @return the OS App ID of this traffic descriptor if one is included by the network, null
- * otherwise.
+ * OsAppId identifies a broader traffic category. Although it names Os/App id, it only includes
+ * OS version with a general/broader category id used as app id.
+ *
+ * @return The id in byte format. {@code null} if not available.
*/
public @Nullable byte[] getOsAppId() {
- return mOsAppId;
+ return mOsAppId != null ? mOsAppId.getBytes() : null;
}
@Override
@@ -77,13 +273,13 @@
@NonNull @Override
public String toString() {
- return "TrafficDescriptor={mDnn=" + mDnn + ", mOsAppId=" + mOsAppId + "}";
+ return "TrafficDescriptor={mDnn=" + mDnn + ", " + mOsAppId + "}";
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mDnn);
- dest.writeByteArray(mOsAppId);
+ dest.writeByteArray(mOsAppId != null ? mOsAppId.getBytes() : null);
}
public static final @NonNull Parcelable.Creator<TrafficDescriptor> CREATOR =
@@ -104,7 +300,7 @@
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TrafficDescriptor that = (TrafficDescriptor) o;
- return Objects.equals(mDnn, that.mDnn) && Arrays.equals(mOsAppId, that.mOsAppId);
+ return Objects.equals(mDnn, that.mDnn) && Objects.equals(mOsAppId, that.mOsAppId);
}
@Override
@@ -148,7 +344,7 @@
}
/**
- * Set the OS App ID (including OS Id as defind in the specs).
+ * Set the OS App ID (including OS Id as defined in the specs).
*
* @return The same instance of the builder.
*/
diff --git a/telephony/java/android/telephony/ims/RcsClientConfiguration.java b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
index c25ace0..f367e40 100644
--- a/telephony/java/android/telephony/ims/RcsClientConfiguration.java
+++ b/telephony/java/android/telephony/ims/RcsClientConfiguration.java
@@ -34,7 +34,7 @@
/**@hide*/
@StringDef(prefix = "RCS_PROFILE_",
- value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3})
+ value = {RCS_PROFILE_1_0, RCS_PROFILE_2_3, RCS_PROFILE_2_4})
public @interface StringRcsProfile {}
/**
@@ -45,6 +45,10 @@
* RCS profile UP 2.3
*/
public static final String RCS_PROFILE_2_3 = "UP_2.3";
+ /**
+ * RCS profile UP 2.4
+ */
+ public static final String RCS_PROFILE_2_4 = "UP_2.4";
private String mRcsVersion;
private String mRcsProfile;
@@ -58,8 +62,8 @@
* @param rcsVersion The parameter identifies the RCS version supported
* by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
* @param rcsProfile Identifies a fixed set of RCS services that are
- * supported by the client. See {@link #RCS_PROFILE_1_0 } or
- * {@link #RCS_PROFILE_2_3 }
+ * supported by the client. See {@link #RCS_PROFILE_1_0 },
+ * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
* @param clientVendor Identifies the vendor providing the RCS client.
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
@@ -80,8 +84,8 @@
* @param rcsVersion The parameter identifies the RCS version supported
* by the client. Refer to GSMA RCC.07 "rcs_version" parameter.
* @param rcsProfile Identifies a fixed set of RCS services that are
- * supported by the client. See {@link #RCS_PROFILE_1_0 } or
- * {@link #RCS_PROFILE_2_3 }
+ * supported by the client. See {@link #RCS_PROFILE_1_0 },
+ * {@link #RCS_PROFILE_2_3 } or {@link #RCS_PROFILE_2_4 }
* @param clientVendor Identifies the vendor providing the RCS client.
* @param clientVersion Identifies the RCS client version. Refer to GSMA
* RCC.07 "client_version" parameter.
diff --git a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
index 48bfd6f..6290292 100644
--- a/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
+++ b/tests/AttestationVerificationTest/src/android/security/attestationverification/SystemAttestationVerificationTest.kt
@@ -11,10 +11,20 @@
import org.junit.Test
import org.junit.runner.RunWith
import com.google.common.truth.Truth.assertThat
+import android.security.attestationverification.AttestationVerificationManager.PARAM_CHALLENGE
+import android.security.attestationverification.AttestationVerificationManager.PROFILE_PEER_DEVICE
import android.security.attestationverification.AttestationVerificationManager.PROFILE_SELF_TRUSTED
-import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.RESULT_FAILURE
+import android.security.attestationverification.AttestationVerificationManager.RESULT_SUCCESS
import android.security.attestationverification.AttestationVerificationManager.RESULT_UNKNOWN
+import android.security.attestationverification.AttestationVerificationManager.TYPE_PUBLIC_KEY
+import android.security.attestationverification.AttestationVerificationManager.TYPE_CHALLENGE
+import android.security.keystore.KeyGenParameterSpec
+import android.security.keystore.KeyProperties
import java.lang.IllegalArgumentException
+import java.io.ByteArrayOutputStream
+import java.security.KeyPairGenerator
+import java.security.KeyStore
import java.time.Duration
import java.util.concurrent.CompletableFuture
import java.util.concurrent.TimeUnit
@@ -23,25 +33,26 @@
@SmallTest
@RunWith(AndroidJUnit4::class)
class SystemAttestationVerificationTest {
-
@get:Rule
val rule = ActivityScenarioRule(TestActivity::class.java)
private lateinit var activity: Activity
private lateinit var avm: AttestationVerificationManager
+ private lateinit var androidKeystore: KeyStore
@Before
fun setup() {
rule.getScenario().onActivity {
avm = it.getSystemService(AttestationVerificationManager::class.java)
activity = it
+ androidKeystore = KeyStore.getInstance(ANDROID_KEYSTORE).apply { load(null) }
}
}
@Test
fun verifyAttestation_returnsUnknown() {
val future = CompletableFuture<Int>()
- val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { result, _ ->
future.complete(result)
@@ -51,9 +62,82 @@
}
@Test
- fun verifyToken_returnsUnknown() {
+ fun verifyAttestation_returnsFailureWithEmptyAttestation() {
val future = CompletableFuture<Int>()
val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ avm.verifyAttestation(profile, TYPE_CHALLENGE, Bundle(), ByteArray(0),
+ activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithEmptyRequirements() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ Bundle(), selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongBindingType() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, TYPE_PUBLIC_KEY,
+ selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongRequirements() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ val wrongKeyRequirements = Bundle()
+ wrongKeyRequirements.putByteArray(
+ "wrongBindingKey", "challengeStr".encodeToByteArray())
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ wrongKeyRequirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ @Test
+ fun verifyAttestation_returnsFailureWithWrongChallenge() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ val wrongChallengeRequirements = Bundle()
+ wrongChallengeRequirements.putByteArray(PARAM_CHALLENGE, "wrong".encodeToByteArray())
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ wrongChallengeRequirements, selfTrusted.attestation, activity.mainExecutor) {
+ result, _ -> future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_FAILURE)
+ }
+
+ // TODO(b/216144791): Add more failure tests for PROFILE_SELF_TRUSTED.
+ @Test
+ fun verifyAttestation_returnsSuccess() {
+ val future = CompletableFuture<Int>()
+ val selfTrusted = TestSelfTrustedAttestation("test", "challengeStr")
+ avm.verifyAttestation(selfTrusted.profile, selfTrusted.localBindingType,
+ selfTrusted.requirements, selfTrusted.attestation, activity.mainExecutor) { result, _ ->
+ future.complete(result)
+ }
+ assertThat(future.getSoon()).isEqualTo(RESULT_SUCCESS)
+ }
+
+ @Test
+ fun verifyToken_returnsUnknown() {
+ val future = CompletableFuture<Int>()
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
val result = avm.verifyToken(profile, TYPE_PUBLIC_KEY, Bundle(), token, null)
@@ -66,7 +150,7 @@
@Test
fun verifyToken_tooBigMaxAgeThrows() {
val future = CompletableFuture<VerificationToken>()
- val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ val profile = AttestationProfile(PROFILE_PEER_DEVICE)
avm.verifyAttestation(profile, TYPE_PUBLIC_KEY, Bundle(), ByteArray(0),
activity.mainExecutor) { _, token ->
future.complete(token)
@@ -87,4 +171,52 @@
super.onCreate(savedInstanceState)
}
}
+
+ inner class TestSelfTrustedAttestation(val alias: String, val challenge: String) {
+ val profile = AttestationProfile(PROFILE_SELF_TRUSTED)
+ val localBindingType = TYPE_CHALLENGE
+ val requirements: Bundle
+ val attestation: ByteArray
+
+ init {
+ val challengeByteArray = challenge.encodeToByteArray()
+ generateAndStoreKey(alias, challengeByteArray)
+ attestation = generateCertificatesByteArray(alias)
+ requirements = Bundle()
+ requirements.putByteArray(PARAM_CHALLENGE, challengeByteArray)
+ }
+
+ private fun generateAndStoreKey(alias: String, challenge: ByteArray) {
+ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
+ KeyProperties.KEY_ALGORITHM_EC,
+ ANDROID_KEYSTORE
+ )
+ val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
+ alias,
+ KeyProperties.PURPOSE_SIGN or KeyProperties.PURPOSE_VERIFY
+ ).run {
+ // a challenge results in a generated attestation
+ setAttestationChallenge(challenge)
+ setDigests(KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA512)
+ build()
+ }
+ kpg.initialize(parameterSpec)
+ kpg.generateKeyPair()
+ }
+
+ private fun generateCertificatesByteArray(alias: String): ByteArray {
+ val pkEntry = androidKeystore.getEntry(alias, null) as KeyStore.PrivateKeyEntry
+ val certs = pkEntry.certificateChain
+ val bos = ByteArrayOutputStream()
+ certs.forEach {
+ bos.write(it.encoded)
+ }
+ return bos.toByteArray()
+ }
+ }
+
+ companion object {
+ private const val TAG = "AVFTEST"
+ private const val ANDROID_KEYSTORE = "AndroidKeyStore"
+ }
}
diff --git a/tests/FlickerTests/AndroidTest.xml b/tests/FlickerTests/AndroidTest.xml
index 566c725..98d13e8 100644
--- a/tests/FlickerTests/AndroidTest.xml
+++ b/tests/FlickerTests/AndroidTest.xml
@@ -26,8 +26,16 @@
<option name="shell-timeout" value="6600s" />
<option name="test-timeout" value="6600s" />
<option name="hidden-api-checks" value="false" />
+ <option name="device-listeners"
+ value="com.android.server.wm.flicker.TraceFileReadyListener" />
</test>
<metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
+ <option name="pull-pattern-keys" value="(\w)+\.winscope" />
+ <option name="pull-pattern-keys" value="(\w)+\.mp4" />
+ <option name="collect-on-run-ended-only" value="false" />
+ <option name="clean-up" value="true" />
+ </metrics_collector>
+ <metrics_collector class="com.android.tradefed.device.metric.FilePullerLogCollector">
<option name="directory-keys" value="/sdcard/flicker" />
<option name="collect-on-run-ended-only" value="true" />
<option name="clean-up" value="true" />
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
index 8fe0029..b66c45c7 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/helpers/TwoActivitiesAppHelper.kt
@@ -19,11 +19,13 @@
import android.app.Instrumentation
import android.support.test.launcherhelper.ILauncherStrategy
import android.support.test.launcherhelper.LauncherStrategyFactory
+import android.view.Display
import androidx.test.uiautomator.By
import androidx.test.uiautomator.UiDevice
import androidx.test.uiautomator.Until
import com.android.server.wm.flicker.testapp.ActivityOptions
import com.android.server.wm.traces.common.FlickerComponentName
+import com.android.server.wm.traces.common.WindowManagerConditionsFactory
import com.android.server.wm.traces.parser.toFlickerComponent
import com.android.server.wm.traces.parser.windowmanager.WindowManagerStateHelper
@@ -36,6 +38,10 @@
.getInstance(instr)
.launcherStrategy
) : StandardAppHelper(instr, launcherName, component, launcherStrategy) {
+
+ private val secondActivityComponent =
+ ActivityOptions.SIMPLE_ACTIVITY_AUTO_FOCUS_COMPONENT_NAME.toFlickerComponent()
+
fun openSecondActivity(device: UiDevice, wmHelper: WindowManagerStateHelper) {
val launchActivityButton = By.res(getPackage(), LAUNCH_SECOND_ACTIVITY)
val button = device.wait(Until.findObject(launchActivityButton), FIND_TIMEOUT)
@@ -47,8 +53,11 @@
button.click()
device.wait(Until.gone(launchActivityButton), FIND_TIMEOUT)
- wmHelper.waitForAppTransitionIdle()
- wmHelper.waitForFullScreenApp(component)
+ wmHelper.waitForFullScreenApp(secondActivityComponent)
+ wmHelper.waitFor(
+ WindowManagerConditionsFactory.isAppTransitionIdle(Display.DEFAULT_DISPLAY),
+ WindowManagerConditionsFactory.hasLayersAnimating().negate()
+ )
}
companion object {
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
index 648353e..195af58 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/launch/ActivitiesTransitionTest.kt
@@ -70,7 +70,7 @@
fun buildFlicker(): FlickerBuilder {
return FlickerBuilder(instrumentation).apply {
setup {
- eachRun {
+ test {
testApp.launchViaIntent(wmHelper)
wmHelper.waitForFullScreenApp(testApp.component)
}
diff --git a/tests/vcn/Android.bp b/tests/vcn/Android.bp
index 41f73cd..228520e 100644
--- a/tests/vcn/Android.bp
+++ b/tests/vcn/Android.bp
@@ -18,6 +18,7 @@
"java/**/*.kt",
],
platform_apis: true,
+ defaults: ["framework-connectivity-test-defaults"],
test_suites: ["device-tests"],
certificate: "platform",
static_libs: [
@@ -28,6 +29,7 @@
"net-tests-utils",
"platform-test-annotations",
"services.core",
+ "service-connectivity-tiramisu-pre-jarjar",
],
libs: [
"android.test.runner",
diff --git a/tests/vcn/AndroidManifest.xml b/tests/vcn/AndroidManifest.xml
index 2ad9aac..a8f657c 100644
--- a/tests/vcn/AndroidManifest.xml
+++ b/tests/vcn/AndroidManifest.xml
@@ -16,7 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.frameworks.tests.vcn">
-
+ <uses-sdk android:minSdkVersion="33"
+ android:targetSdkVersion="33"/>
<application>
<uses-library android:name="android.test.runner" />
</application>