Merge "Add some protologs for recents"
diff --git a/Android.bp b/Android.bp
index e03f844..7d02d7d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -155,32 +155,9 @@
name: "framework-all",
installable: false,
static_libs: [
- "android.net.ipsec.ike.impl",
+ "all-framework-module-impl",
"framework-minus-apex",
- "framework-appsearch.impl",
- "framework-connectivity.impl",
- "framework-connectivity-tiramisu.impl",
- "framework-graphics.impl",
- "framework-mediaprovider.impl",
- "framework-permission.impl",
- "framework-permission-s.impl",
- "framework-scheduling.impl",
- "framework-sdkextensions.impl",
- "framework-statsd.impl",
- "framework-supplementalprocess.impl",
- "framework-tethering.impl",
- "framework-uwb.impl",
- "framework-wifi.impl",
- "updatable-media",
],
- soong_config_variables: {
- include_nonpublic_framework_api: {
- static_libs: [
- "framework-auxiliary.impl",
- "framework-supplementalapi.impl",
- ],
- },
- },
apex_available: ["//apex_available:platform"],
sdk_version: "core_platform",
visibility: [
@@ -308,6 +285,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",
],
},
@@ -416,7 +395,6 @@
static_libs: [
"app-compat-annotations",
"framework-minus-apex",
- "framework-appsearch.impl", // TODO(b/146218515): should be removed
"framework-updatable-stubs-module_libs_api",
],
sdk_version: "core_platform",
@@ -445,7 +423,6 @@
// TODO: remove these annotations as soon as we can use andoid.support.annotations.*
":framework-annotations",
":modules-utils-preconditions-srcs",
- "core/java/android/net/DhcpResults.java",
"core/java/android/util/IndentingPrintWriter.java",
"core/java/android/util/LocalLog.java",
"core/java/com/android/internal/util/HexDump.java",
@@ -557,6 +534,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)
@@ -602,6 +582,7 @@
libs: [
"art.module.public.api",
"sdk_module-lib_current_framework-tethering",
+ "sdk_module-lib_current_framework-connectivity-tiramisu",
"sdk_public_current_framework-bluetooth",
// There are a few classes from modules used by the core that
// need to be resolved by metalava. We use a prebuilt stub of the
diff --git a/StubLibraries.bp b/StubLibraries.bp
index a0a426e..92e7dc9 100644
--- a/StubLibraries.bp
+++ b/StubLibraries.bp
@@ -258,6 +258,7 @@
srcs: [":module-lib-api-stubs-docs-non-updatable"],
libs: [
"sdk_module-lib_current_framework-tethering",
+ "sdk_module-lib_current_framework-connectivity-tiramisu",
"sdk_public_current_framework-bluetooth",
// NOTE: The below can be removed once the prebuilt stub contains bluetooth.
"sdk_system_current_android",
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index afad29c..c4795f5 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/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index b4edd39..2c2af28 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -66,8 +66,8 @@
},
visibility: [
"//frameworks/av/apex:__subpackages__",
- "//frameworks/base", // For framework-all
"//frameworks/base/apex/media/service",
+ "//frameworks/base/api", // For framework-all
],
}
diff --git a/api/api.go b/api/api.go
index 17649e8..5e5f60e 100644
--- a/api/api.go
+++ b/api/api.go
@@ -27,6 +27,7 @@
const art = "art.module.public.api"
const conscrypt = "conscrypt.module.public.api"
const i18n = "i18n.module.public.api"
+var core_libraries_modules = []string{art, conscrypt, i18n}
// The intention behind this soong plugin is to generate a number of "merged"
// API-related modules that would otherwise require a large amount of very
@@ -199,9 +200,28 @@
ctx.CreateModule(java.LibraryFactory, &props)
}
-func createMergedModuleLibStubs(ctx android.LoadHookContext, modules []string) {
+func createMergedFrameworkImpl(ctx android.LoadHookContext, modules []string) {
+ // This module is for the "framework-all" module, which should not include the core libraries.
+ modules = removeAll(modules, core_libraries_modules)
+ // TODO(b/214988855): remove the line below when framework-bluetooth has an impl jar.
+ modules = remove(modules, "framework-bluetooth")
+ props := libraryProps{}
+ props.Name = proptools.StringPtr("all-framework-module-impl")
+ props.Static_libs = transformArray(modules, "", ".impl")
+ // Media module's impl jar is called "updatable-media"
+ for i, v := range props.Static_libs {
+ if v == "framework-media.impl" {
+ props.Static_libs[i] = "updatable-media"
+ }
+ }
+ props.Sdk_version = proptools.StringPtr("module_current")
+ props.Visibility = []string{"//frameworks/base"}
+ ctx.CreateModule(java.LibraryFactory, &props)
+}
+
+func createMergedFrameworkModuleLibStubs(ctx android.LoadHookContext, modules []string) {
// The user of this module compiles against the "core" SDK, so remove core libraries to avoid dupes.
- modules = removeAll(modules, []string{art, conscrypt, i18n})
+ modules = removeAll(modules, core_libraries_modules)
props := libraryProps{}
props.Name = proptools.StringPtr("framework-updatable-stubs-module_libs_api")
props.Static_libs = transformArray(modules, "", ".stubs.module_lib")
@@ -269,7 +289,8 @@
createMergedPublicStubs(ctx, bootclasspath)
createMergedSystemStubs(ctx, bootclasspath)
- createMergedModuleLibStubs(ctx, bootclasspath)
+ createMergedFrameworkModuleLibStubs(ctx, bootclasspath)
+ createMergedFrameworkImpl(ctx, bootclasspath)
createMergedAnnotations(ctx, bootclasspath)
diff --git a/boot/hiddenapi/hiddenapi-max-target-o.txt b/boot/hiddenapi/hiddenapi-max-target-o.txt
index 9153426..e346ebf 100644
--- a/boot/hiddenapi/hiddenapi-max-target-o.txt
+++ b/boot/hiddenapi/hiddenapi-max-target-o.txt
@@ -9315,78 +9315,6 @@
Landroid/app/usage/IUsageStatsManager;->setAppStandbyBuckets(Landroid/content/pm/ParceledListSlice;I)V
Landroid/app/usage/IUsageStatsManager;->unregisterAppUsageObserver(ILjava/lang/String;)V
Landroid/app/usage/IUsageStatsManager;->whitelistAppTemporarily(Ljava/lang/String;JI)V
-Landroid/app/usage/NetworkStats$Bucket;->convertDefaultNetworkStatus(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertMetered(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertRoaming(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertSet(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertState(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertTag(I)I
-Landroid/app/usage/NetworkStats$Bucket;->convertUid(I)I
-Landroid/app/usage/NetworkStats$Bucket;->mBeginTimeStamp:J
-Landroid/app/usage/NetworkStats$Bucket;->mDefaultNetworkStatus:I
-Landroid/app/usage/NetworkStats$Bucket;->mEndTimeStamp:J
-Landroid/app/usage/NetworkStats$Bucket;->mMetered:I
-Landroid/app/usage/NetworkStats$Bucket;->mRoaming:I
-Landroid/app/usage/NetworkStats$Bucket;->mRxBytes:J
-Landroid/app/usage/NetworkStats$Bucket;->mRxPackets:J
-Landroid/app/usage/NetworkStats$Bucket;->mState:I
-Landroid/app/usage/NetworkStats$Bucket;->mTag:I
-Landroid/app/usage/NetworkStats$Bucket;->mTxBytes:J
-Landroid/app/usage/NetworkStats$Bucket;->mTxPackets:J
-Landroid/app/usage/NetworkStats$Bucket;->mUid:I
-Landroid/app/usage/NetworkStats;-><init>(Landroid/content/Context;Landroid/net/NetworkTemplate;IJJLandroid/net/INetworkStatsService;)V
-Landroid/app/usage/NetworkStats;->fillBucketFromSummaryEntry(Landroid/app/usage/NetworkStats$Bucket;)V
-Landroid/app/usage/NetworkStats;->getDeviceSummaryForNetwork()Landroid/app/usage/NetworkStats$Bucket;
-Landroid/app/usage/NetworkStats;->getNextHistoryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z
-Landroid/app/usage/NetworkStats;->getNextSummaryBucket(Landroid/app/usage/NetworkStats$Bucket;)Z
-Landroid/app/usage/NetworkStats;->getSummaryAggregate()Landroid/app/usage/NetworkStats$Bucket;
-Landroid/app/usage/NetworkStats;->getUid()I
-Landroid/app/usage/NetworkStats;->hasNextUid()Z
-Landroid/app/usage/NetworkStats;->isUidEnumeration()Z
-Landroid/app/usage/NetworkStats;->mCloseGuard:Ldalvik/system/CloseGuard;
-Landroid/app/usage/NetworkStats;->mEndTimeStamp:J
-Landroid/app/usage/NetworkStats;->mEnumerationIndex:I
-Landroid/app/usage/NetworkStats;->mHistory:Landroid/net/NetworkStatsHistory;
-Landroid/app/usage/NetworkStats;->mRecycledHistoryEntry:Landroid/net/NetworkStatsHistory$Entry;
-Landroid/app/usage/NetworkStats;->mRecycledSummaryEntry:Landroid/net/NetworkStats$Entry;
-Landroid/app/usage/NetworkStats;->mSession:Landroid/net/INetworkStatsSession;
-Landroid/app/usage/NetworkStats;->mStartTimeStamp:J
-Landroid/app/usage/NetworkStats;->mState:I
-Landroid/app/usage/NetworkStats;->mSummary:Landroid/net/NetworkStats;
-Landroid/app/usage/NetworkStats;->mTag:I
-Landroid/app/usage/NetworkStats;->mTemplate:Landroid/net/NetworkTemplate;
-Landroid/app/usage/NetworkStats;->mUidOrUidIndex:I
-Landroid/app/usage/NetworkStats;->mUids:[I
-Landroid/app/usage/NetworkStats;->setSingleUidTagState(III)V
-Landroid/app/usage/NetworkStats;->startHistoryEnumeration(III)V
-Landroid/app/usage/NetworkStats;->startSummaryEnumeration()V
-Landroid/app/usage/NetworkStats;->startUserUidEnumeration()V
-Landroid/app/usage/NetworkStats;->stepHistory()V
-Landroid/app/usage/NetworkStats;->stepUid()V
-Landroid/app/usage/NetworkStats;->TAG:Ljava/lang/String;
-Landroid/app/usage/NetworkStatsManager$CallbackHandler;-><init>(Landroid/os/Looper;ILjava/lang/String;Landroid/app/usage/NetworkStatsManager$UsageCallback;)V
-Landroid/app/usage/NetworkStatsManager$CallbackHandler;->getObject(Landroid/os/Message;Ljava/lang/String;)Ljava/lang/Object;
-Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mCallback:Landroid/app/usage/NetworkStatsManager$UsageCallback;
-Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mNetworkType:I
-Landroid/app/usage/NetworkStatsManager$CallbackHandler;->mSubscriberId:Ljava/lang/String;
-Landroid/app/usage/NetworkStatsManager$UsageCallback;->request:Landroid/net/DataUsageRequest;
-Landroid/app/usage/NetworkStatsManager;-><init>(Landroid/content/Context;Landroid/net/INetworkStatsService;)V
-Landroid/app/usage/NetworkStatsManager;->CALLBACK_LIMIT_REACHED:I
-Landroid/app/usage/NetworkStatsManager;->CALLBACK_RELEASED:I
-Landroid/app/usage/NetworkStatsManager;->createTemplate(ILjava/lang/String;)Landroid/net/NetworkTemplate;
-Landroid/app/usage/NetworkStatsManager;->DBG:Z
-Landroid/app/usage/NetworkStatsManager;->FLAG_AUGMENT_WITH_SUBSCRIPTION_PLAN:I
-Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_FORCE:I
-Landroid/app/usage/NetworkStatsManager;->FLAG_POLL_ON_OPEN:I
-Landroid/app/usage/NetworkStatsManager;->mContext:Landroid/content/Context;
-Landroid/app/usage/NetworkStatsManager;->mFlags:I
-Landroid/app/usage/NetworkStatsManager;->MIN_THRESHOLD_BYTES:J
-Landroid/app/usage/NetworkStatsManager;->mService:Landroid/net/INetworkStatsService;
-Landroid/app/usage/NetworkStatsManager;->querySummaryForDevice(Landroid/net/NetworkTemplate;JJ)Landroid/app/usage/NetworkStats$Bucket;
-Landroid/app/usage/NetworkStatsManager;->registerUsageCallback(Landroid/net/NetworkTemplate;IJLandroid/app/usage/NetworkStatsManager$UsageCallback;Landroid/os/Handler;)V
-Landroid/app/usage/NetworkStatsManager;->setAugmentWithSubscriptionPlan(Z)V
-Landroid/app/usage/NetworkStatsManager;->setPollOnOpen(Z)V
-Landroid/app/usage/NetworkStatsManager;->TAG:Ljava/lang/String;
Landroid/app/usage/StorageStats;-><init>()V
Landroid/app/usage/StorageStats;-><init>(Landroid/os/Parcel;)V
Landroid/app/usage/StorageStats;->cacheBytes:J
@@ -35338,13 +35266,6 @@
Landroid/net/Credentials;->gid:I
Landroid/net/Credentials;->pid:I
Landroid/net/Credentials;->uid:I
-Landroid/net/DataUsageRequest;-><init>(ILandroid/net/NetworkTemplate;J)V
-Landroid/net/DataUsageRequest;->CREATOR:Landroid/os/Parcelable$Creator;
-Landroid/net/DataUsageRequest;->PARCELABLE_KEY:Ljava/lang/String;
-Landroid/net/DataUsageRequest;->requestId:I
-Landroid/net/DataUsageRequest;->REQUEST_ID_UNSET:I
-Landroid/net/DataUsageRequest;->template:Landroid/net/NetworkTemplate;
-Landroid/net/DataUsageRequest;->thresholdInBytes:J
Landroid/net/DhcpResults;->addDns(Ljava/lang/String;)Z
Landroid/net/DhcpResults;->clear()V
Landroid/net/DhcpResults;->CREATOR:Landroid/os/Parcelable$Creator;
@@ -35446,51 +35367,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
@@ -35838,68 +35714,6 @@
Landroid/net/INetworkScoreService;->setActiveScorer(Ljava/lang/String;)Z
Landroid/net/INetworkScoreService;->unregisterNetworkScoreCache(ILandroid/net/INetworkScoreCache;)V
Landroid/net/INetworkScoreService;->updateScores([Landroid/net/ScoredNetwork;)Z
-Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdate()V
-Landroid/net/INetworkStatsService$Stub$Proxy;->forceUpdateIfaces([Landroid/net/Network;)V
-Landroid/net/INetworkStatsService$Stub$Proxy;->getDataLayerSnapshotForUid(I)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsService$Stub$Proxy;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsService$Stub$Proxy;->getIfaceStats(Ljava/lang/String;I)J
-Landroid/net/INetworkStatsService$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
-Landroid/net/INetworkStatsService$Stub$Proxy;->getTotalStats(I)J
-Landroid/net/INetworkStatsService$Stub$Proxy;->getUidStats(II)J
-Landroid/net/INetworkStatsService$Stub$Proxy;->incrementOperationCount(III)V
-Landroid/net/INetworkStatsService$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/net/INetworkStatsService$Stub$Proxy;->openSession()Landroid/net/INetworkStatsSession;
-Landroid/net/INetworkStatsService$Stub$Proxy;->openSessionForUsageStats(ILjava/lang/String;)Landroid/net/INetworkStatsSession;
-Landroid/net/INetworkStatsService$Stub$Proxy;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest;
-Landroid/net/INetworkStatsService$Stub$Proxy;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V
-Landroid/net/INetworkStatsService$Stub;-><init>()V
-Landroid/net/INetworkStatsService$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdate:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_forceUpdateIfaces:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDataLayerSnapshotForUid:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getDetailedUidStats:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getIfaceStats:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getMobileIfaces:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getTotalStats:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_getUidStats:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_incrementOperationCount:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSession:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_openSessionForUsageStats:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_registerUsageCallback:I
-Landroid/net/INetworkStatsService$Stub;->TRANSACTION_unregisterUsageRequest:I
-Landroid/net/INetworkStatsService;->forceUpdateIfaces([Landroid/net/Network;)V
-Landroid/net/INetworkStatsService;->getDetailedUidStats([Ljava/lang/String;)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsService;->getIfaceStats(Ljava/lang/String;I)J
-Landroid/net/INetworkStatsService;->getTotalStats(I)J
-Landroid/net/INetworkStatsService;->getUidStats(II)J
-Landroid/net/INetworkStatsService;->incrementOperationCount(III)V
-Landroid/net/INetworkStatsService;->registerUsageCallback(Ljava/lang/String;Landroid/net/DataUsageRequest;Landroid/os/Messenger;Landroid/os/IBinder;)Landroid/net/DataUsageRequest;
-Landroid/net/INetworkStatsService;->unregisterUsageRequest(Landroid/net/DataUsageRequest;)V
-Landroid/net/INetworkStatsSession$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/net/INetworkStatsSession$Stub$Proxy;->close()V
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForNetwork(Landroid/net/NetworkTemplate;I)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryForUid(Landroid/net/NetworkTemplate;IIII)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getInterfaceDescriptor()Ljava/lang/String;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getRelevantUids()[I
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForAllUid(Landroid/net/NetworkTemplate;JJZ)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->getSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsSession$Stub$Proxy;->mRemote:Landroid/os/IBinder;
-Landroid/net/INetworkStatsSession$Stub;-><init>()V
-Landroid/net/INetworkStatsSession$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsSession;
-Landroid/net/INetworkStatsSession$Stub;->DESCRIPTOR:Ljava/lang/String;
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_close:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getDeviceSummaryForNetwork:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForNetwork:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryForUid:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getHistoryIntervalForUid:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getRelevantUids:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForAllUid:I
-Landroid/net/INetworkStatsSession$Stub;->TRANSACTION_getSummaryForNetwork:I
-Landroid/net/INetworkStatsSession;->getDeviceSummaryForNetwork(Landroid/net/NetworkTemplate;JJ)Landroid/net/NetworkStats;
-Landroid/net/INetworkStatsSession;->getHistoryIntervalForUid(Landroid/net/NetworkTemplate;IIIIJJ)Landroid/net/NetworkStatsHistory;
-Landroid/net/INetworkStatsSession;->getRelevantUids()[I
Landroid/net/InterfaceConfiguration;->CREATOR:Landroid/os/Parcelable$Creator;
Landroid/net/InterfaceConfiguration;->FLAG_DOWN:Ljava/lang/String;
Landroid/net/InterfaceConfiguration;->FLAG_UP:Ljava/lang/String;
@@ -35914,174 +35728,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;
@@ -36742,41 +36388,6 @@
Landroid/net/StringNetworkSpecifier;-><init>(Ljava/lang/String;)V
Landroid/net/StringNetworkSpecifier;->CREATOR:Landroid/os/Parcelable$Creator;
Landroid/net/StringNetworkSpecifier;->satisfiedBy(Landroid/net/NetworkSpecifier;)Z
-Landroid/net/TrafficStats;->addIfSupported(J)J
-Landroid/net/TrafficStats;->closeQuietly(Landroid/net/INetworkStatsSession;)V
-Landroid/net/TrafficStats;->GB_IN_BYTES:J
-Landroid/net/TrafficStats;->getDataLayerSnapshotForUid(Landroid/content/Context;)Landroid/net/NetworkStats;
-Landroid/net/TrafficStats;->getRxPackets(Ljava/lang/String;)J
-Landroid/net/TrafficStats;->getTxPackets(Ljava/lang/String;)J
-Landroid/net/TrafficStats;->KB_IN_BYTES:J
-Landroid/net/TrafficStats;->LOOPBACK_IFACE:Ljava/lang/String;
-Landroid/net/TrafficStats;->MB_IN_BYTES:J
-Landroid/net/TrafficStats;->PB_IN_BYTES:J
-Landroid/net/TrafficStats;->sActiveProfilingStart:Landroid/net/NetworkStats;
-Landroid/net/TrafficStats;->sProfilingLock:Ljava/lang/Object;
-Landroid/net/TrafficStats;->sStatsService:Landroid/net/INetworkStatsService;
-Landroid/net/TrafficStats;->startDataProfiling(Landroid/content/Context;)V
-Landroid/net/TrafficStats;->stopDataProfiling(Landroid/content/Context;)Landroid/net/NetworkStats;
-Landroid/net/TrafficStats;->TAG_SYSTEM_APP:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_BACKUP:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_DHCP:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_DOWNLOAD:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_GPS:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_MEDIA:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_NEIGHBOR:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_NTP:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_PAC:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_PROBE:I
-Landroid/net/TrafficStats;->TAG_SYSTEM_RESTORE:I
-Landroid/net/TrafficStats;->TB_IN_BYTES:J
-Landroid/net/TrafficStats;->TYPE_RX_BYTES:I
-Landroid/net/TrafficStats;->TYPE_RX_PACKETS:I
-Landroid/net/TrafficStats;->TYPE_TCP_RX_PACKETS:I
-Landroid/net/TrafficStats;->TYPE_TCP_TX_PACKETS:I
-Landroid/net/TrafficStats;->TYPE_TX_BYTES:I
-Landroid/net/TrafficStats;->TYPE_TX_PACKETS:I
-Landroid/net/TrafficStats;->UID_REMOVED:I
-Landroid/net/TrafficStats;->UID_TETHERING:I
Landroid/net/Uri$AbstractHierarchicalUri;-><init>()V
Landroid/net/Uri$AbstractHierarchicalUri;->getUserInfoPart()Landroid/net/Uri$Part;
Landroid/net/Uri$AbstractHierarchicalUri;->host:Ljava/lang/String;
diff --git a/boot/hiddenapi/hiddenapi-unsupported.txt b/boot/hiddenapi/hiddenapi-unsupported.txt
index 522e88f..033afb6 100644
--- a/boot/hiddenapi/hiddenapi-unsupported.txt
+++ b/boot/hiddenapi/hiddenapi-unsupported.txt
@@ -168,9 +168,6 @@
Landroid/net/INetworkManagementEventObserver$Stub;-><init>()V
Landroid/net/INetworkPolicyManager$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkPolicyManager;
Landroid/net/INetworkScoreService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkScoreService;
-Landroid/net/INetworkStatsService$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
-Landroid/net/INetworkStatsService$Stub$Proxy;->getMobileIfaces()[Ljava/lang/String;
-Landroid/net/INetworkStatsService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetworkStatsService;
Landroid/os/IBatteryPropertiesRegistrar$Stub$Proxy;-><init>(Landroid/os/IBinder;)V
Landroid/os/IDeviceIdentifiersPolicyService$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdentifiersPolicyService;
Landroid/os/IDeviceIdleController$Stub;->asInterface(Landroid/os/IBinder;)Landroid/os/IDeviceIdleController;
diff --git a/core/api/current.txt b/core/api/current.txt
index c29f3ae..fd53d88 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -135,6 +135,9 @@
field public static final String READ_HOME_APP_SEARCH_DATA = "android.permission.READ_HOME_APP_SEARCH_DATA";
field @Deprecated public static final String READ_INPUT_STATE = "android.permission.READ_INPUT_STATE";
field public static final String READ_LOGS = "android.permission.READ_LOGS";
+ field public static final String READ_MEDIA_AUDIO = "android.permission.READ_MEDIA_AUDIO";
+ field public static final String READ_MEDIA_IMAGE = "android.permission.READ_MEDIA_IMAGE";
+ field public static final String READ_MEDIA_VIDEO = "android.permission.READ_MEDIA_VIDEO";
field public static final String READ_NEARBY_STREAMING_POLICY = "android.permission.READ_NEARBY_STREAMING_POLICY";
field public static final String READ_PHONE_NUMBERS = "android.permission.READ_PHONE_NUMBERS";
field public static final String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE";
@@ -220,6 +223,8 @@
field public static final String NEARBY_DEVICES = "android.permission-group.NEARBY_DEVICES";
field public static final String NOTIFICATIONS = "android.permission-group.NOTIFICATIONS";
field public static final String PHONE = "android.permission-group.PHONE";
+ field public static final String READ_MEDIA_AURAL = "android.permission-group.READ_MEDIA_AURAL";
+ field public static final String READ_MEDIA_VISUAL = "android.permission-group.READ_MEDIA_VISUAL";
field public static final String SENSORS = "android.permission-group.SENSORS";
field public static final String SMS = "android.permission-group.SMS";
field public static final String STORAGE = "android.permission-group.STORAGE";
@@ -326,6 +331,9 @@
field public static final int allowClearUserData = 16842757; // 0x1010005
field public static final int allowClickWhenDisabled = 16844312; // 0x1010618
field public static final int allowEmbedded = 16843765; // 0x10103f5
+ field public static final int allowGameAngleDriver;
+ field public static final int allowGameDownscaling;
+ field public static final int allowGameFpsOverride;
field public static final int allowNativeHeapPointerTagging = 16844306; // 0x1010612
field public static final int allowParallelSyncs = 16843570; // 0x1010332
field public static final int allowSingleTap = 16843353; // 0x1010259
@@ -1435,10 +1443,12 @@
field public static final int summaryOn = 16843247; // 0x10101ef
field public static final int supportedTypes;
field public static final int supportsAssist = 16844016; // 0x10104f0
+ field public static final int supportsBatteryGameMode;
field public static final int supportsInlineSuggestions = 16844301; // 0x101060d
field public static final int supportsLaunchVoiceAssistFromKeyguard = 16844017; // 0x10104f1
field public static final int supportsLocalInteraction = 16844047; // 0x101050f
field public static final int supportsMultipleDisplays = 16844182; // 0x1010596
+ field public static final int supportsPerformanceGameMode;
field public static final int supportsPictureInPicture = 16844023; // 0x10104f7
field public static final int supportsRtl = 16843695; // 0x10103af
field public static final int supportsStylusHandwriting;
@@ -3069,6 +3079,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);
@@ -3130,6 +3141,11 @@
field public static final int GLOBAL_ACTION_ACCESSIBILITY_SHORTCUT = 13; // 0xd
field public static final int GLOBAL_ACTION_BACK = 1; // 0x1
field public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15; // 0xf
+ field public static final int GLOBAL_ACTION_DPAD_CENTER = 20; // 0x14
+ field public static final int GLOBAL_ACTION_DPAD_DOWN = 17; // 0x11
+ field public static final int GLOBAL_ACTION_DPAD_LEFT = 18; // 0x12
+ field public static final int GLOBAL_ACTION_DPAD_RIGHT = 19; // 0x13
+ field public static final int GLOBAL_ACTION_DPAD_UP = 16; // 0x10
field public static final int GLOBAL_ACTION_HOME = 2; // 0x2
field public static final int GLOBAL_ACTION_KEYCODE_HEADSETHOOK = 10; // 0xa
field public static final int GLOBAL_ACTION_LOCK_SCREEN = 8; // 0x8
@@ -3581,17 +3597,17 @@
}
public static interface Animator.AnimatorListener {
- method public void onAnimationCancel(android.animation.Animator);
- method public default void onAnimationEnd(android.animation.Animator, boolean);
- method public void onAnimationEnd(android.animation.Animator);
- method public void onAnimationRepeat(android.animation.Animator);
- method public default void onAnimationStart(android.animation.Animator, boolean);
- method public void onAnimationStart(android.animation.Animator);
+ method public void onAnimationCancel(@NonNull android.animation.Animator);
+ method public default void onAnimationEnd(@NonNull android.animation.Animator, boolean);
+ method public void onAnimationEnd(@NonNull android.animation.Animator);
+ method public void onAnimationRepeat(@NonNull android.animation.Animator);
+ method public default void onAnimationStart(@NonNull android.animation.Animator, boolean);
+ method public void onAnimationStart(@NonNull android.animation.Animator);
}
public static interface Animator.AnimatorPauseListener {
- method public void onAnimationPause(android.animation.Animator);
- method public void onAnimationResume(android.animation.Animator);
+ method public void onAnimationPause(@NonNull android.animation.Animator);
+ method public void onAnimationResume(@NonNull android.animation.Animator);
}
public class AnimatorInflater {
@@ -3878,7 +3894,7 @@
}
public static interface ValueAnimator.AnimatorUpdateListener {
- method public void onAnimationUpdate(android.animation.ValueAnimator);
+ method public void onAnimationUpdate(@NonNull android.animation.ValueAnimator);
}
}
@@ -4059,7 +4075,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);
@@ -4865,6 +4881,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
@@ -4960,7 +4977,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();
@@ -6939,6 +6956,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);
@@ -7316,6 +7334,8 @@
method public CharSequence getDeviceOwnerLockScreenInfo();
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method @Nullable public android.graphics.drawable.Drawable getDrawable(@NonNull String, @NonNull String, @NonNull String, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
+ method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon);
+ method @Nullable public android.graphics.drawable.Icon getDrawableAsIcon(@NonNull String, @NonNull String, @Nullable android.graphics.drawable.Icon);
method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method @Nullable public android.graphics.drawable.Drawable getDrawableForDensity(@NonNull String, @NonNull String, @NonNull String, int, @NonNull java.util.concurrent.Callable<android.graphics.drawable.Drawable>);
method public CharSequence getEndUserSessionMessage(@NonNull android.content.ComponentName);
@@ -7601,7 +7621,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";
@@ -7920,11 +7940,10 @@
}
public final class WifiSsidPolicy implements android.os.Parcelable {
- method @NonNull public static android.app.admin.WifiSsidPolicy createAllowlistPolicy(@NonNull java.util.Set<java.lang.String>);
- method @NonNull public static android.app.admin.WifiSsidPolicy createDenylistPolicy(@NonNull java.util.Set<java.lang.String>);
+ ctor public WifiSsidPolicy(int, @NonNull java.util.Set<android.net.wifi.WifiSsid>);
method public int describeContents();
method public int getPolicyType();
- method @NonNull public java.util.Set<java.lang.String> getSsids();
+ method @NonNull public java.util.Set<android.net.wifi.WifiSsid> getSsids();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.admin.WifiSsidPolicy> CREATOR;
field public static final int WIFI_SSID_POLICY_TYPE_ALLOWLIST = 0; // 0x0
@@ -8581,62 +8600,6 @@
field @NonNull public static final android.os.Parcelable.Creator<android.app.usage.ExternalStorageStats> CREATOR;
}
- public final class NetworkStats implements java.lang.AutoCloseable {
- method public void close();
- method public boolean getNextBucket(android.app.usage.NetworkStats.Bucket);
- method public boolean hasNextBucket();
- }
-
- public static class NetworkStats.Bucket {
- ctor public NetworkStats.Bucket();
- method public int getDefaultNetworkStatus();
- method public long getEndTimeStamp();
- method public int getMetered();
- method public int getRoaming();
- method public long getRxBytes();
- method public long getRxPackets();
- method public long getStartTimeStamp();
- method public int getState();
- method public int getTag();
- method public long getTxBytes();
- method public long getTxPackets();
- method public int getUid();
- field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
- field public static final int DEFAULT_NETWORK_NO = 1; // 0x1
- field public static final int DEFAULT_NETWORK_YES = 2; // 0x2
- field public static final int METERED_ALL = -1; // 0xffffffff
- field public static final int METERED_NO = 1; // 0x1
- field public static final int METERED_YES = 2; // 0x2
- field public static final int ROAMING_ALL = -1; // 0xffffffff
- field public static final int ROAMING_NO = 1; // 0x1
- field public static final int ROAMING_YES = 2; // 0x2
- field public static final int STATE_ALL = -1; // 0xffffffff
- field public static final int STATE_DEFAULT = 1; // 0x1
- field public static final int STATE_FOREGROUND = 2; // 0x2
- field public static final int TAG_NONE = 0; // 0x0
- field public static final int UID_ALL = -1; // 0xffffffff
- field public static final int UID_REMOVED = -4; // 0xfffffffc
- field public static final int UID_TETHERING = -5; // 0xfffffffb
- }
-
- public class NetworkStatsManager {
- method @WorkerThread public android.app.usage.NetworkStats queryDetails(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUid(int, String, long, long, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTag(int, String, long, long, int, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(int, String, long, long, int, int, int) throws java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats querySummary(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForUser(int, String, long, long) throws android.os.RemoteException, java.lang.SecurityException;
- method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback);
- method public void registerUsageCallback(int, String, long, android.app.usage.NetworkStatsManager.UsageCallback, @Nullable android.os.Handler);
- method public void unregisterUsageCallback(android.app.usage.NetworkStatsManager.UsageCallback);
- }
-
- public abstract static class NetworkStatsManager.UsageCallback {
- ctor public NetworkStatsManager.UsageCallback();
- method public abstract void onThresholdReached(int, String);
- }
-
public final class StorageStats implements android.os.Parcelable {
method public int describeContents();
method public long getAppBytes();
@@ -9814,7 +9777,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";
@@ -11919,7 +11881,7 @@
field public static final String FEATURE_TELEPHONY_IMS = "android.hardware.telephony.ims";
field public static final String FEATURE_TELEPHONY_MBMS = "android.hardware.telephony.mbms";
field public static final String FEATURE_TELEPHONY_MESSAGING = "android.hardware.telephony.messaging";
- field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio";
+ field public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio.access";
field public static final String FEATURE_TELEPHONY_SUBSCRIPTION = "android.hardware.telephony.subscription";
field @Deprecated public static final String FEATURE_TELEVISION = "android.hardware.type.television";
field public static final String FEATURE_TOUCHSCREEN = "android.hardware.touchscreen";
@@ -15457,8 +15419,6 @@
public class RuntimeShader extends android.graphics.Shader {
ctor public RuntimeShader(@NonNull String);
- ctor public RuntimeShader(@NonNull String, boolean);
- method public boolean isForceOpaque();
method public void setColorUniform(@NonNull String, @ColorInt int);
method public void setColorUniform(@NonNull String, @ColorLong long);
method public void setColorUniform(@NonNull String, @NonNull android.graphics.Color);
@@ -15756,9 +15716,9 @@
method public final void copyBounds(@NonNull android.graphics.Rect);
method @NonNull public final android.graphics.Rect copyBounds();
method @Nullable public static android.graphics.drawable.Drawable createFromPath(String);
- method public static android.graphics.drawable.Drawable createFromResourceStream(android.content.res.Resources, android.util.TypedValue, java.io.InputStream, String);
+ method @Nullable public static android.graphics.drawable.Drawable createFromResourceStream(@Nullable android.content.res.Resources, @Nullable android.util.TypedValue, @Nullable java.io.InputStream, @Nullable String);
method @Deprecated @Nullable public static android.graphics.drawable.Drawable createFromResourceStream(@Nullable android.content.res.Resources, @Nullable android.util.TypedValue, @Nullable java.io.InputStream, @Nullable String, @Nullable android.graphics.BitmapFactory.Options);
- method public static android.graphics.drawable.Drawable createFromStream(java.io.InputStream, String);
+ method @Nullable public static android.graphics.drawable.Drawable createFromStream(@Nullable java.io.InputStream, @Nullable String);
method @NonNull public static android.graphics.drawable.Drawable createFromXml(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method @NonNull public static android.graphics.drawable.Drawable createFromXml(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser, @Nullable android.content.res.Resources.Theme) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
method @NonNull public static android.graphics.drawable.Drawable createFromXmlInner(@NonNull android.content.res.Resources, @NonNull org.xmlpull.v1.XmlPullParser, @NonNull android.util.AttributeSet) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
@@ -15796,10 +15756,10 @@
method public final boolean isVisible();
method public void jumpToCurrentState();
method @NonNull public android.graphics.drawable.Drawable mutate();
- method protected void onBoundsChange(android.graphics.Rect);
+ method protected void onBoundsChange(@NonNull android.graphics.Rect);
method public boolean onLayoutDirectionChanged(int);
method protected boolean onLevelChange(int);
- method protected boolean onStateChange(int[]);
+ method protected boolean onStateChange(@NonNull int[]);
method public static int resolveOpacity(int, int);
method public void scheduleSelf(@NonNull Runnable, long);
method public abstract void setAlpha(@IntRange(from=0, to=255) int);
@@ -15960,27 +15920,27 @@
}
public final class Icon implements android.os.Parcelable {
- method public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
+ method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmap(android.graphics.Bitmap);
method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull String);
method @NonNull public static android.graphics.drawable.Icon createWithAdaptiveBitmapContentUri(@NonNull android.net.Uri);
- method public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
- method public static android.graphics.drawable.Icon createWithContentUri(String);
- method public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
- method public static android.graphics.drawable.Icon createWithData(byte[], int, int);
- method public static android.graphics.drawable.Icon createWithFilePath(String);
- method public static android.graphics.drawable.Icon createWithResource(android.content.Context, @DrawableRes int);
- method public static android.graphics.drawable.Icon createWithResource(String, @DrawableRes int);
+ method @NonNull public static android.graphics.drawable.Icon createWithBitmap(android.graphics.Bitmap);
+ method @NonNull public static android.graphics.drawable.Icon createWithContentUri(String);
+ method @NonNull public static android.graphics.drawable.Icon createWithContentUri(android.net.Uri);
+ method @NonNull public static android.graphics.drawable.Icon createWithData(byte[], int, int);
+ method @NonNull public static android.graphics.drawable.Icon createWithFilePath(String);
+ method @NonNull public static android.graphics.drawable.Icon createWithResource(android.content.Context, @DrawableRes int);
+ method @NonNull public static android.graphics.drawable.Icon createWithResource(String, @DrawableRes int);
method public int describeContents();
method @DrawableRes public int getResId();
method @NonNull public String getResPackage();
method public int getType();
method @NonNull public android.net.Uri getUri();
- method public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
- method public void loadDrawableAsync(android.content.Context, android.os.Message);
- method public void loadDrawableAsync(android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
- method public android.graphics.drawable.Icon setTint(@ColorInt int);
+ method @Nullable public android.graphics.drawable.Drawable loadDrawable(android.content.Context);
+ method public void loadDrawableAsync(@NonNull android.content.Context, @NonNull android.os.Message);
+ method public void loadDrawableAsync(@NonNull android.content.Context, android.graphics.drawable.Icon.OnDrawableLoadedListener, android.os.Handler);
+ method @NonNull public android.graphics.drawable.Icon setTint(@ColorInt int);
method @NonNull public android.graphics.drawable.Icon setTintBlendMode(@NonNull android.graphics.BlendMode);
- method public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList);
+ method @NonNull public android.graphics.drawable.Icon setTintList(android.content.res.ColorStateList);
method @NonNull public android.graphics.drawable.Icon setTintMode(@NonNull android.graphics.PorterDuff.Mode);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.graphics.drawable.Icon> CREATOR;
@@ -16858,13 +16818,18 @@
field public static final int REPORTING_MODE_ON_CHANGE = 1; // 0x1
field public static final int REPORTING_MODE_SPECIAL_TRIGGER = 3; // 0x3
field public static final String STRING_TYPE_ACCELEROMETER = "android.sensor.accelerometer";
+ field public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES = "android.sensor.accelerometer_limited_axes";
+ field public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = "android.sensor.accelerometer_limited_axes_uncalibrated";
field public static final String STRING_TYPE_ACCELEROMETER_UNCALIBRATED = "android.sensor.accelerometer_uncalibrated";
field public static final String STRING_TYPE_AMBIENT_TEMPERATURE = "android.sensor.ambient_temperature";
field public static final String STRING_TYPE_GAME_ROTATION_VECTOR = "android.sensor.game_rotation_vector";
field public static final String STRING_TYPE_GEOMAGNETIC_ROTATION_VECTOR = "android.sensor.geomagnetic_rotation_vector";
field public static final String STRING_TYPE_GRAVITY = "android.sensor.gravity";
field public static final String STRING_TYPE_GYROSCOPE = "android.sensor.gyroscope";
+ field public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES = "android.sensor.gyroscope_limited_axes";
+ field public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = "android.sensor.gyroscope_limited_axes_uncalibrated";
field public static final String STRING_TYPE_GYROSCOPE_UNCALIBRATED = "android.sensor.gyroscope_uncalibrated";
+ field public static final String STRING_TYPE_HEADING = "android.sensor.heading";
field public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
field public static final String STRING_TYPE_HEART_BEAT = "android.sensor.heart_beat";
field public static final String STRING_TYPE_HEART_RATE = "android.sensor.heart_rate";
@@ -16887,6 +16852,8 @@
field public static final String STRING_TYPE_STEP_DETECTOR = "android.sensor.step_detector";
field @Deprecated public static final String STRING_TYPE_TEMPERATURE = "android.sensor.temperature";
field public static final int TYPE_ACCELEROMETER = 1; // 0x1
+ field public static final int TYPE_ACCELEROMETER_LIMITED_AXES = 38; // 0x26
+ field public static final int TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40; // 0x28
field public static final int TYPE_ACCELEROMETER_UNCALIBRATED = 35; // 0x23
field public static final int TYPE_ALL = -1; // 0xffffffff
field public static final int TYPE_AMBIENT_TEMPERATURE = 13; // 0xd
@@ -16895,7 +16862,10 @@
field public static final int TYPE_GEOMAGNETIC_ROTATION_VECTOR = 20; // 0x14
field public static final int TYPE_GRAVITY = 9; // 0x9
field public static final int TYPE_GYROSCOPE = 4; // 0x4
+ field public static final int TYPE_GYROSCOPE_LIMITED_AXES = 39; // 0x27
+ field public static final int TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41; // 0x29
field public static final int TYPE_GYROSCOPE_UNCALIBRATED = 16; // 0x10
+ field public static final int TYPE_HEADING = 42; // 0x2a
field public static final int TYPE_HEAD_TRACKER = 37; // 0x25
field public static final int TYPE_HEART_BEAT = 31; // 0x1f
field public static final int TYPE_HEART_RATE = 21; // 0x15
@@ -17264,6 +17234,8 @@
method @NonNull public java.util.List<android.hardware.camera2.CameraCharacteristics.Key<?>> getKeysNeedingPermission();
method @NonNull public java.util.Set<java.lang.String> getPhysicalCameraIds();
method @Nullable public android.hardware.camera2.params.RecommendedStreamConfigurationMap getRecommendedStreamConfigurationMap(int);
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> AUTOMOTIVE_LENS_FACING;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> AUTOMOTIVE_LOCATION;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> COLOR_CORRECTION_AVAILABLE_ABERRATION_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_ANTIBANDING_MODES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> CONTROL_AE_AVAILABLE_MODES;
@@ -17325,12 +17297,14 @@
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> REQUEST_RECOMMENDED_TEN_BIT_DYNAMIC_RANGE_PROFILE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Float> SCALER_AVAILABLE_MAX_DIGITAL_ZOOM;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_ROTATE_AND_CROP_MODES;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<java.lang.Integer> SCALER_CROPPING_TYPE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.util.Size> SCALER_DEFAULT_SECURE_IMAGE_SIZE;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_CONCURRENT_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_MAXIMUM_RESOLUTION_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_TEN_BIT_OUTPUT_STREAM_COMBINATIONS;
+ field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.MultiResolutionStreamConfigurationMap> SCALER_MULTI_RESOLUTION_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP;
field @NonNull public static final android.hardware.camera2.CameraCharacteristics.Key<android.hardware.camera2.params.StreamConfigurationMap> SCALER_STREAM_CONFIGURATION_MAP_MAXIMUM_RESOLUTION;
@@ -17424,6 +17398,8 @@
}
public final class CameraExtensionCharacteristics {
+ method @NonNull public java.util.Set<android.hardware.camera2.CaptureRequest.Key> getAvailableCaptureRequestKeys(int);
+ method @NonNull public java.util.Set<android.hardware.camera2.CaptureResult.Key> getAvailableCaptureResultKeys(int);
method @Nullable public android.util.Range<java.lang.Long> getEstimatedCaptureLatencyRangeMillis(int, @NonNull android.util.Size, int);
method @NonNull public <T> java.util.List<android.util.Size> getExtensionSupportedSizes(int, @NonNull Class<T>);
method @NonNull public java.util.List<android.util.Size> getExtensionSupportedSizes(int, int);
@@ -17448,6 +17424,7 @@
ctor public CameraExtensionSession.ExtensionCaptureCallback();
method public void onCaptureFailed(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
method public void onCaptureProcessStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest);
+ method public void onCaptureResultAvailable(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, @NonNull android.hardware.camera2.TotalCaptureResult);
method public void onCaptureSequenceAborted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureSequenceCompleted(@NonNull android.hardware.camera2.CameraExtensionSession, int);
method public void onCaptureStarted(@NonNull android.hardware.camera2.CameraExtensionSession, @NonNull android.hardware.camera2.CaptureRequest, long);
@@ -17497,6 +17474,32 @@
public abstract class CameraMetadata<TKey> {
method @NonNull public java.util.List<TKey> getKeys();
+ field public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_FRONT = 1; // 0x1
+ field public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_LEFT = 3; // 0x3
+ field public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_OTHER = 0; // 0x0
+ field public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_REAR = 2; // 0x2
+ field public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_RIGHT = 4; // 0x4
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER = 5; // 0x5
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_CENTER = 7; // 0x7
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_LEFT = 6; // 0x6
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_RIGHT = 8; // 0x8
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_CENTER = 10; // 0xa
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_LEFT = 9; // 0x9
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_RIGHT = 11; // 0xb
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_CENTER = 13; // 0xd
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_LEFT = 12; // 0xc
+ field public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_RIGHT = 14; // 0xe
+ field public static final int AUTOMOTIVE_LOCATION_EXTERIOR_FRONT = 2; // 0x2
+ field public static final int AUTOMOTIVE_LOCATION_EXTERIOR_LEFT = 4; // 0x4
+ field public static final int AUTOMOTIVE_LOCATION_EXTERIOR_OTHER = 1; // 0x1
+ field public static final int AUTOMOTIVE_LOCATION_EXTERIOR_REAR = 3; // 0x3
+ field public static final int AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT = 5; // 0x5
+ field public static final int AUTOMOTIVE_LOCATION_EXTRA_FRONT = 7; // 0x7
+ field public static final int AUTOMOTIVE_LOCATION_EXTRA_LEFT = 9; // 0x9
+ field public static final int AUTOMOTIVE_LOCATION_EXTRA_OTHER = 6; // 0x6
+ field public static final int AUTOMOTIVE_LOCATION_EXTRA_REAR = 8; // 0x8
+ field public static final int AUTOMOTIVE_LOCATION_EXTRA_RIGHT = 10; // 0xa
+ field public static final int AUTOMOTIVE_LOCATION_INTERIOR = 0; // 0x0
field public static final int COLOR_CORRECTION_ABERRATION_MODE_FAST = 1; // 0x1
field public static final int COLOR_CORRECTION_ABERRATION_MODE_HIGH_QUALITY = 2; // 0x2
field public static final int COLOR_CORRECTION_ABERRATION_MODE_OFF = 0; // 0x0
@@ -17631,6 +17634,7 @@
field public static final int LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED = 0; // 0x0
field public static final int LENS_OPTICAL_STABILIZATION_MODE_OFF = 0; // 0x0
field public static final int LENS_OPTICAL_STABILIZATION_MODE_ON = 1; // 0x1
+ field public static final int LENS_POSE_REFERENCE_AUTOMOTIVE = 3; // 0x3
field public static final int LENS_POSE_REFERENCE_GYROSCOPE = 1; // 0x1
field public static final int LENS_POSE_REFERENCE_PRIMARY_CAMERA = 0; // 0x0
field public static final int LENS_POSE_REFERENCE_UNDEFINED = 2; // 0x2
@@ -17659,9 +17663,16 @@
field public static final int REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS = 5; // 0x5
field public static final int REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING = 17; // 0x11
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SECURE_IMAGE_DATA = 13; // 0xd
+ field public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19; // 0x13
field public static final int REQUEST_AVAILABLE_CAPABILITIES_SYSTEM_CAMERA = 14; // 0xe
field public static final int REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR = 16; // 0x10
field public static final int REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING = 7; // 0x7
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0; // 0x0
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 1; // 0x1
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 4; // 0x4
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 2; // 0x2
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 5; // 0x5
+ field public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 3; // 0x3
field public static final int SCALER_CROPPING_TYPE_CENTER_ONLY = 0; // 0x0
field public static final int SCALER_CROPPING_TYPE_FREEFORM = 1; // 0x1
field public static final int SCALER_ROTATE_AND_CROP_180 = 2; // 0x2
@@ -18069,6 +18080,7 @@
method public int get10BitFormat();
method @NonNull public java.util.List<android.util.Size> getAvailableSizes();
method public int getFormat();
+ method public int getStreamUseCase();
method public boolean is10BitCapable();
method public boolean isInput();
method public boolean isMaximumSize();
@@ -18125,6 +18137,7 @@
method public void enableSurfaceSharing();
method public int getDynamicRangeProfile();
method public int getMaxSharedSurfaceCount();
+ method public int getStreamUseCase();
method @Nullable public android.view.Surface getSurface();
method public int getSurfaceGroupId();
method @NonNull public java.util.List<android.view.Surface> getSurfaces();
@@ -18132,6 +18145,7 @@
method public void removeSurface(@NonNull android.view.Surface);
method public void setDynamicRangeProfile(int);
method public void setPhysicalCameraId(@Nullable String);
+ method public void setStreamUseCase(int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.hardware.camera2.params.OutputConfiguration> CREATOR;
field public static final int SURFACE_GROUP_ID_NONE = -1; // 0xffffffff
@@ -19667,10 +19681,13 @@
method public android.media.AudioAttributes.Builder setUsage(int);
}
- public class AudioDescriptor {
+ public class AudioDescriptor implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public byte[] getDescriptor();
method public int getEncapsulationType();
method public int getStandard();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioDescriptor> CREATOR;
field public static final int STANDARD_EDID = 1; // 0x1
field public static final int STANDARD_NONE = 0; // 0x0
}
@@ -20186,14 +20203,17 @@
method @NonNull public android.media.AudioPresentation.Builder setProgramId(int);
}
- public class AudioProfile {
+ public class AudioProfile implements android.os.Parcelable {
+ method public int describeContents();
method @NonNull public int[] getChannelIndexMasks();
method @NonNull public int[] getChannelMasks();
method public int getEncapsulationType();
method public int getFormat();
method @NonNull public int[] getSampleRates();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
field public static final int AUDIO_ENCAPSULATION_TYPE_IEC61937 = 1; // 0x1
field public static final int AUDIO_ENCAPSULATION_TYPE_NONE = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.media.AudioProfile> CREATOR;
}
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
@@ -26382,75 +26402,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;
@@ -26577,50 +26528,6 @@
method @NonNull public android.net.TelephonyNetworkSpecifier.Builder setSubscriptionId(int);
}
- public class TrafficStats {
- ctor public TrafficStats();
- method public static void clearThreadStatsTag();
- method public static void clearThreadStatsUid();
- method public static int getAndSetThreadStatsTag(int);
- method public static long getMobileRxBytes();
- method public static long getMobileRxPackets();
- method public static long getMobileTxBytes();
- method public static long getMobileTxPackets();
- method public static long getRxBytes(@NonNull String);
- method public static long getRxPackets(@NonNull String);
- method public static int getThreadStatsTag();
- method public static int getThreadStatsUid();
- method public static long getTotalRxBytes();
- method public static long getTotalRxPackets();
- method public static long getTotalTxBytes();
- method public static long getTotalTxPackets();
- method public static long getTxBytes(@NonNull String);
- method public static long getTxPackets(@NonNull String);
- method public static long getUidRxBytes(int);
- method public static long getUidRxPackets(int);
- method @Deprecated public static long getUidTcpRxBytes(int);
- method @Deprecated public static long getUidTcpRxSegments(int);
- method @Deprecated public static long getUidTcpTxBytes(int);
- method @Deprecated public static long getUidTcpTxSegments(int);
- method public static long getUidTxBytes(int);
- method public static long getUidTxPackets(int);
- method @Deprecated public static long getUidUdpRxBytes(int);
- method @Deprecated public static long getUidUdpRxPackets(int);
- method @Deprecated public static long getUidUdpTxBytes(int);
- method @Deprecated public static long getUidUdpTxPackets(int);
- method public static void incrementOperationCount(int);
- method public static void incrementOperationCount(int, int);
- method public static void setThreadStatsTag(int);
- method public static void setThreadStatsUid(int);
- method public static void tagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
- method public static void tagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
- method public static void tagSocket(java.net.Socket) throws java.net.SocketException;
- method public static void untagDatagramSocket(java.net.DatagramSocket) throws java.net.SocketException;
- method public static void untagFileDescriptor(java.io.FileDescriptor) throws java.io.IOException;
- method public static void untagSocket(java.net.Socket) throws java.net.SocketException;
- field public static final int UNSUPPORTED = -1; // 0xffffffff
- }
-
public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
method public abstract android.net.Uri.Builder buildUpon();
method public int compareTo(android.net.Uri);
@@ -31425,6 +31332,9 @@
method @Nullable public byte[] createByteArray();
method @Nullable public char[] createCharArray();
method @Nullable public double[] createDoubleArray();
+ method @Nullable public <T> T createFixedArray(@NonNull Class<T>, @NonNull int...);
+ method @Nullable public <T, S extends android.os.IInterface> T createFixedArray(@NonNull Class<T>, @NonNull java.util.function.Function<android.os.IBinder,S>, @NonNull int...);
+ method @Nullable public <T, S extends android.os.Parcelable> T createFixedArray(@NonNull Class<T>, @NonNull android.os.Parcelable.Creator<S>, @NonNull int...);
method @Nullable public float[] createFloatArray();
method @Nullable public int[] createIntArray();
method @Nullable public <T extends android.os.IInterface> T[] createInterfaceArray(@NonNull java.util.function.IntFunction<T[]>, @NonNull java.util.function.Function<android.os.IBinder,T>);
@@ -31466,6 +31376,9 @@
method public void readException();
method public void readException(int, String);
method public android.os.ParcelFileDescriptor readFileDescriptor();
+ method public <T> void readFixedArray(@NonNull T);
+ method public <T, S extends android.os.IInterface> void readFixedArray(@NonNull T, @NonNull java.util.function.Function<android.os.IBinder,S>);
+ method public <T, S extends android.os.Parcelable> void readFixedArray(@NonNull T, @NonNull android.os.Parcelable.Creator<S>);
method public float readFloat();
method public void readFloatArray(@NonNull float[]);
method @Deprecated @Nullable public java.util.HashMap readHashMap(@Nullable ClassLoader);
@@ -31509,6 +31422,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[]);
@@ -31526,6 +31440,7 @@
method public void writeDoubleArray(@Nullable double[]);
method public void writeException(@NonNull Exception);
method public void writeFileDescriptor(@NonNull java.io.FileDescriptor);
+ method public <T> void writeFixedArray(@Nullable T, int, @NonNull int...);
method public void writeFloat(float);
method public void writeFloatArray(@Nullable float[]);
method public void writeInt(int);
@@ -39419,6 +39334,7 @@
public interface RecognitionListener {
method public void onBeginningOfSpeech();
method public void onBufferReceived(byte[]);
+ method public default void onEndOfSegmentedSession();
method public void onEndOfSpeech();
method public void onError(int);
method public void onEvent(int, android.os.Bundle);
@@ -39426,6 +39342,7 @@
method public void onReadyForSpeech(android.os.Bundle);
method public void onResults(android.os.Bundle);
method public void onRmsChanged(float);
+ method public default void onSegmentResults(@NonNull android.os.Bundle);
}
public abstract class RecognitionService extends android.app.Service {
@@ -39443,6 +39360,7 @@
public class RecognitionService.Callback {
method public void beginningOfSpeech() throws android.os.RemoteException;
method public void bufferReceived(byte[]) throws android.os.RemoteException;
+ method public void endOfSegmentedSession() throws android.os.RemoteException;
method public void endOfSpeech() throws android.os.RemoteException;
method public void error(int) throws android.os.RemoteException;
method @NonNull public android.content.AttributionSource getCallingAttributionSource();
@@ -39451,6 +39369,7 @@
method public void readyForSpeech(android.os.Bundle) throws android.os.RemoteException;
method public void results(android.os.Bundle) throws android.os.RemoteException;
method public void rmsChanged(float) throws android.os.RemoteException;
+ method public void segmentResults(@NonNull android.os.Bundle) throws android.os.RemoteException;
}
public static class RecognitionService.SupportCallback {
@@ -39506,6 +39425,7 @@
field public static final String EXTRA_RESULTS_PENDINGINTENT = "android.speech.extra.RESULTS_PENDINGINTENT";
field public static final String EXTRA_RESULTS_PENDINGINTENT_BUNDLE = "android.speech.extra.RESULTS_PENDINGINTENT_BUNDLE";
field public static final String EXTRA_SECURE = "android.speech.extras.EXTRA_SECURE";
+ field public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
field public static final String EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS";
field public static final String EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_MINIMUM_LENGTH_MILLIS";
field public static final String EXTRA_SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS = "android.speech.extras.SPEECH_INPUT_POSSIBLY_COMPLETE_SILENCE_LENGTH_MILLIS";
@@ -41221,6 +41141,7 @@
field public static final String KEY_IMS_CONFERENCE_SIZE_LIMIT_INT = "ims_conference_size_limit_int";
field public static final String KEY_IMS_DTMF_TONE_DELAY_INT = "ims_dtmf_tone_delay_int";
field public static final String KEY_IS_IMS_CONFERENCE_SIZE_ENFORCED_BOOL = "is_ims_conference_size_enforced_bool";
+ field public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL = "is_opportunistic_subscription_bool";
field public static final String KEY_LTE_ENABLED_BOOL = "lte_enabled_bool";
field public static final String KEY_LTE_RSRQ_THRESHOLDS_INT_ARRAY = "lte_rsrq_thresholds_int_array";
field public static final String KEY_LTE_RSSNR_THRESHOLDS_INT_ARRAY = "lte_rssnr_thresholds_int_array";
@@ -41305,6 +41226,7 @@
field public static final String KEY_SIM_NETWORK_UNLOCK_ALLOW_DISMISS_BOOL = "sim_network_unlock_allow_dismiss_bool";
field public static final String KEY_SMDP_SERVER_ADDRESS_STRING = "smdp_server_address_string";
field public static final String KEY_SMS_REQUIRES_DESTINATION_NUMBER_CONVERSION_BOOL = "sms_requires_destination_number_conversion_bool";
+ field public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING = "subscription_group_uuid_string";
field public static final String KEY_SUPPORTS_CALL_COMPOSER_BOOL = "supports_call_composer_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_DTMF_BOOL = "supports_device_to_device_communication_using_dtmf_bool";
field public static final String KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL = "supports_device_to_device_communication_using_rtp_bool";
@@ -41348,6 +41270,7 @@
field public static final String KEY_WFC_EMERGENCY_ADDRESS_CARRIER_APP_STRING = "wfc_emergency_address_carrier_app_string";
field public static final String KEY_WORLD_MODE_ENABLED_BOOL = "world_mode_enabled_bool";
field public static final String KEY_WORLD_PHONE_BOOL = "world_phone_bool";
+ field public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
field public static final int SERVICE_CLASS_NONE = 0; // 0x0
field public static final int SERVICE_CLASS_VOICE = 1; // 0x1
field public static final int USSD_OVER_CS_ONLY = 2; // 0x2
@@ -42989,6 +42912,7 @@
field public static final int ENCODING_16BIT = 3; // 0x3
field public static final int ENCODING_7BIT = 1; // 0x1
field public static final int ENCODING_8BIT = 2; // 0x2
+ field public static final int ENCODING_KSC5601 = 4; // 0x4
field public static final int ENCODING_UNKNOWN = 0; // 0x0
field public static final String FORMAT_3GPP = "3gpp";
field public static final String FORMAT_3GPP2 = "3gpp2";
@@ -43083,7 +43007,8 @@
method public void setSubscriptionOverrideCongested(int, boolean, @NonNull int[], long);
method public void setSubscriptionOverrideUnmetered(int, boolean, long);
method public void setSubscriptionOverrideUnmetered(int, boolean, @NonNull int[], long);
- method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
+ method @Deprecated public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>);
+ method public void setSubscriptionPlans(int, @NonNull java.util.List<android.telephony.SubscriptionPlan>, long);
method @RequiresPermission("android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS") public void switchToSubscription(int, @NonNull android.app.PendingIntent);
field public static final String ACTION_DEFAULT_SMS_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SMS_SUBSCRIPTION_CHANGED";
field public static final String ACTION_DEFAULT_SUBSCRIPTION_CHANGED = "android.telephony.action.DEFAULT_SUBSCRIPTION_CHANGED";
@@ -43266,6 +43191,7 @@
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public boolean doesSwitchMultiSimConfigTriggerReboot();
method public int getActiveModemCount();
method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public java.util.List<android.telephony.CellInfo> getAllCellInfo();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public long getAllowedNetworkTypesForReason(int);
method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public int getCallComposerStatus();
method @Deprecated @RequiresPermission(value=android.Manifest.permission.READ_PHONE_STATE, conditional=true) public int getCallState();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getCallStateForSubscription();
@@ -43326,6 +43252,7 @@
method public int getSubscriptionId();
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public int getSubscriptionId(@NonNull android.telecom.PhoneAccountHandle);
method public int getSupportedModemCount();
+ method @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public long getSupportedRadioAccessFamily();
method @Nullable public String getTypeAllocationCode();
method @Nullable public String getTypeAllocationCode(int);
method @NonNull @RequiresPermission("android.permission.READ_PRIVILEGED_PHONE_STATE") public java.util.List<android.telephony.UiccCardInfo> getUiccCardsInfo();
@@ -43371,6 +43298,7 @@
method public String sendEnvelopeWithStatus(String);
method @RequiresPermission(android.Manifest.permission.CALL_PHONE) public void sendUssdRequest(String, android.telephony.TelephonyManager.UssdResponseCallback, android.os.Handler);
method public void sendVisualVoicemailSms(String, int, String, android.app.PendingIntent);
+ method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAllowedNetworkTypesForReason(int, long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallComposerStatus(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabled(boolean);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setDataEnabledForReason(int, boolean);
@@ -43407,6 +43335,8 @@
field public static final String ACTION_SHOW_VOICEMAIL_NOTIFICATION = "android.telephony.action.SHOW_VOICEMAIL_NOTIFICATION";
field public static final String ACTION_SUBSCRIPTION_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_CARRIER_IDENTITY_CHANGED";
field public static final String ACTION_SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED = "android.telephony.action.SUBSCRIPTION_SPECIFIC_CARRIER_IDENTITY_CHANGED";
+ field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2
+ field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
field public static final int APPTYPE_CSIM = 4; // 0x4
field public static final int APPTYPE_ISIM = 5; // 0x5
field public static final int APPTYPE_RUIM = 3; // 0x3
@@ -43481,6 +43411,26 @@
field public static final int NETWORK_SELECTION_MODE_MANUAL = 2; // 0x2
field public static final int NETWORK_SELECTION_MODE_UNKNOWN = 0; // 0x0
field public static final int NETWORK_TYPE_1xRTT = 7; // 0x7
+ field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
+ field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
+ field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
+ field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
+ field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
+ field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
+ field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
+ field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
+ field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
+ field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
+ field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
+ field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
+ field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
+ field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
+ field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
+ field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
+ field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
field public static final int NETWORK_TYPE_CDMA = 4; // 0x4
field public static final int NETWORK_TYPE_EDGE = 2; // 0x2
field public static final int NETWORK_TYPE_EHRPD = 14; // 0xe
@@ -47593,16 +47543,16 @@
}
public abstract class ActionProvider {
- ctor public ActionProvider(android.content.Context);
+ ctor public ActionProvider(@NonNull android.content.Context);
method public boolean hasSubMenu();
method public boolean isVisible();
- method @Deprecated public abstract android.view.View onCreateActionView();
- method public android.view.View onCreateActionView(android.view.MenuItem);
+ method @Deprecated @NonNull public abstract android.view.View onCreateActionView();
+ method @NonNull public android.view.View onCreateActionView(@NonNull android.view.MenuItem);
method public boolean onPerformDefaultAction();
- method public void onPrepareSubMenu(android.view.SubMenu);
+ method public void onPrepareSubMenu(@NonNull android.view.SubMenu);
method public boolean overridesItemVisibility();
method public void refreshVisibility();
- method public void setVisibilityListener(android.view.ActionProvider.VisibilityListener);
+ method public void setVisibilityListener(@Nullable android.view.ActionProvider.VisibilityListener);
}
public static interface ActionProvider.VisibilityListener {
@@ -47872,60 +47822,60 @@
}
public class GestureDetector {
- ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener, android.os.Handler);
- ctor @Deprecated public GestureDetector(android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener);
- ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler);
- ctor public GestureDetector(@UiContext android.content.Context, android.view.GestureDetector.OnGestureListener, android.os.Handler, boolean);
+ ctor @Deprecated public GestureDetector(@NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler);
+ ctor @Deprecated public GestureDetector(@NonNull android.view.GestureDetector.OnGestureListener);
+ ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener);
+ ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler);
+ ctor public GestureDetector(@Nullable @UiContext android.content.Context, @NonNull android.view.GestureDetector.OnGestureListener, @Nullable android.os.Handler, boolean);
method public boolean isLongpressEnabled();
- method public boolean onGenericMotionEvent(android.view.MotionEvent);
- method public boolean onTouchEvent(android.view.MotionEvent);
- method public void setContextClickListener(android.view.GestureDetector.OnContextClickListener);
+ method public boolean onGenericMotionEvent(@NonNull android.view.MotionEvent);
+ method public boolean onTouchEvent(@NonNull android.view.MotionEvent);
+ method public void setContextClickListener(@Nullable android.view.GestureDetector.OnContextClickListener);
method public void setIsLongpressEnabled(boolean);
- method public void setOnDoubleTapListener(android.view.GestureDetector.OnDoubleTapListener);
+ method public void setOnDoubleTapListener(@Nullable android.view.GestureDetector.OnDoubleTapListener);
}
public static interface GestureDetector.OnContextClickListener {
- method public boolean onContextClick(android.view.MotionEvent);
+ method public boolean onContextClick(@NonNull android.view.MotionEvent);
}
public static interface GestureDetector.OnDoubleTapListener {
- method public boolean onDoubleTap(android.view.MotionEvent);
- method public boolean onDoubleTapEvent(android.view.MotionEvent);
- method public boolean onSingleTapConfirmed(android.view.MotionEvent);
+ method public boolean onDoubleTap(@NonNull android.view.MotionEvent);
+ method public boolean onDoubleTapEvent(@NonNull android.view.MotionEvent);
+ method public boolean onSingleTapConfirmed(@NonNull android.view.MotionEvent);
}
public static interface GestureDetector.OnGestureListener {
- method public boolean onDown(android.view.MotionEvent);
- method public boolean onFling(android.view.MotionEvent, android.view.MotionEvent, float, float);
- method public void onLongPress(android.view.MotionEvent);
- method public boolean onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float);
- method public void onShowPress(android.view.MotionEvent);
- method public boolean onSingleTapUp(android.view.MotionEvent);
+ method public boolean onDown(@NonNull android.view.MotionEvent);
+ method public boolean onFling(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float);
+ method public void onLongPress(@NonNull android.view.MotionEvent);
+ method public boolean onScroll(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float);
+ method public void onShowPress(@NonNull android.view.MotionEvent);
+ method public boolean onSingleTapUp(@NonNull android.view.MotionEvent);
}
public static class GestureDetector.SimpleOnGestureListener implements android.view.GestureDetector.OnContextClickListener android.view.GestureDetector.OnDoubleTapListener android.view.GestureDetector.OnGestureListener {
ctor public GestureDetector.SimpleOnGestureListener();
- method public boolean onContextClick(android.view.MotionEvent);
- method public boolean onDoubleTap(android.view.MotionEvent);
- method public boolean onDoubleTapEvent(android.view.MotionEvent);
- method public boolean onDown(android.view.MotionEvent);
- method public boolean onFling(android.view.MotionEvent, android.view.MotionEvent, float, float);
- method public void onLongPress(android.view.MotionEvent);
- method public boolean onScroll(android.view.MotionEvent, android.view.MotionEvent, float, float);
- method public void onShowPress(android.view.MotionEvent);
- method public boolean onSingleTapConfirmed(android.view.MotionEvent);
- method public boolean onSingleTapUp(android.view.MotionEvent);
+ method public boolean onContextClick(@NonNull android.view.MotionEvent);
+ method public boolean onDoubleTap(@NonNull android.view.MotionEvent);
+ method public boolean onDoubleTapEvent(@NonNull android.view.MotionEvent);
+ method public boolean onDown(@NonNull android.view.MotionEvent);
+ method public boolean onFling(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float);
+ method public void onLongPress(@NonNull android.view.MotionEvent);
+ method public boolean onScroll(@NonNull android.view.MotionEvent, @NonNull android.view.MotionEvent, float, float);
+ method public void onShowPress(@NonNull android.view.MotionEvent);
+ method public boolean onSingleTapConfirmed(@NonNull android.view.MotionEvent);
+ method public boolean onSingleTapUp(@NonNull android.view.MotionEvent);
}
public class Gravity {
ctor public Gravity();
method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect);
- method public static void apply(int, int, int, android.graphics.Rect, android.graphics.Rect, int);
- method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect);
- method public static void apply(int, int, int, android.graphics.Rect, int, int, android.graphics.Rect, int);
- method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect);
- method public static void applyDisplay(int, android.graphics.Rect, android.graphics.Rect, int);
+ method public static void apply(int, int, int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect, int);
+ method public static void apply(int, int, int, @NonNull android.graphics.Rect, int, int, @NonNull android.graphics.Rect);
+ method public static void apply(int, int, int, @NonNull android.graphics.Rect, int, int, @NonNull android.graphics.Rect, int);
+ method public static void applyDisplay(int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect);
+ method public static void applyDisplay(int, @NonNull android.graphics.Rect, @NonNull android.graphics.Rect, int);
method public static int getAbsoluteGravity(int, int);
method public static boolean isHorizontal(int);
method public static boolean isVertical(int);
@@ -48657,60 +48607,60 @@
public interface MenuItem {
method public boolean collapseActionView();
method public boolean expandActionView();
- method public android.view.ActionProvider getActionProvider();
- method public android.view.View getActionView();
+ method @Nullable public android.view.ActionProvider getActionProvider();
+ method @Nullable public android.view.View getActionView();
method public default int getAlphabeticModifiers();
method public char getAlphabeticShortcut();
- method public default CharSequence getContentDescription();
+ method @Nullable public default CharSequence getContentDescription();
method public int getGroupId();
- method public android.graphics.drawable.Drawable getIcon();
+ method @Nullable public android.graphics.drawable.Drawable getIcon();
method @Nullable public default android.graphics.BlendMode getIconTintBlendMode();
method @Nullable public default android.content.res.ColorStateList getIconTintList();
method @Nullable public default android.graphics.PorterDuff.Mode getIconTintMode();
- method public android.content.Intent getIntent();
+ method @Nullable public android.content.Intent getIntent();
method public int getItemId();
- method public android.view.ContextMenu.ContextMenuInfo getMenuInfo();
+ method @Nullable public android.view.ContextMenu.ContextMenuInfo getMenuInfo();
method public default int getNumericModifiers();
method public char getNumericShortcut();
method public int getOrder();
- method public android.view.SubMenu getSubMenu();
- method public CharSequence getTitle();
- method public CharSequence getTitleCondensed();
- method public default CharSequence getTooltipText();
+ method @Nullable public android.view.SubMenu getSubMenu();
+ method @Nullable public CharSequence getTitle();
+ method @Nullable public CharSequence getTitleCondensed();
+ method @Nullable public default CharSequence getTooltipText();
method public boolean hasSubMenu();
method public boolean isActionViewExpanded();
method public boolean isCheckable();
method public boolean isChecked();
method public boolean isEnabled();
method public boolean isVisible();
- method public android.view.MenuItem setActionProvider(android.view.ActionProvider);
- method public android.view.MenuItem setActionView(android.view.View);
- method public android.view.MenuItem setActionView(@LayoutRes int);
- method public android.view.MenuItem setAlphabeticShortcut(char);
- method public default android.view.MenuItem setAlphabeticShortcut(char, int);
- method public android.view.MenuItem setCheckable(boolean);
- method public android.view.MenuItem setChecked(boolean);
- method public default android.view.MenuItem setContentDescription(CharSequence);
- method public android.view.MenuItem setEnabled(boolean);
- method public android.view.MenuItem setIcon(android.graphics.drawable.Drawable);
- method public android.view.MenuItem setIcon(@DrawableRes int);
+ method @NonNull public android.view.MenuItem setActionProvider(@Nullable android.view.ActionProvider);
+ method @NonNull public android.view.MenuItem setActionView(@Nullable android.view.View);
+ method @NonNull public android.view.MenuItem setActionView(@LayoutRes int);
+ method @NonNull public android.view.MenuItem setAlphabeticShortcut(char);
+ method @NonNull public default android.view.MenuItem setAlphabeticShortcut(char, int);
+ method @NonNull public android.view.MenuItem setCheckable(boolean);
+ method @NonNull public android.view.MenuItem setChecked(boolean);
+ method @NonNull public default android.view.MenuItem setContentDescription(@Nullable CharSequence);
+ method @NonNull public android.view.MenuItem setEnabled(boolean);
+ method @NonNull public android.view.MenuItem setIcon(@Nullable android.graphics.drawable.Drawable);
+ method @NonNull public android.view.MenuItem setIcon(@DrawableRes int);
method @NonNull public default android.view.MenuItem setIconTintBlendMode(@Nullable android.graphics.BlendMode);
- method public default android.view.MenuItem setIconTintList(@Nullable android.content.res.ColorStateList);
+ method @NonNull public default android.view.MenuItem setIconTintList(@Nullable android.content.res.ColorStateList);
method @NonNull public default android.view.MenuItem setIconTintMode(@Nullable android.graphics.PorterDuff.Mode);
- method public android.view.MenuItem setIntent(android.content.Intent);
- method public android.view.MenuItem setNumericShortcut(char);
- method public default android.view.MenuItem setNumericShortcut(char, int);
- method public android.view.MenuItem setOnActionExpandListener(android.view.MenuItem.OnActionExpandListener);
- method public android.view.MenuItem setOnMenuItemClickListener(android.view.MenuItem.OnMenuItemClickListener);
- method public android.view.MenuItem setShortcut(char, char);
- method public default android.view.MenuItem setShortcut(char, char, int, int);
+ method @NonNull public android.view.MenuItem setIntent(@Nullable android.content.Intent);
+ method @NonNull public android.view.MenuItem setNumericShortcut(char);
+ method @NonNull public default android.view.MenuItem setNumericShortcut(char, int);
+ method @NonNull public android.view.MenuItem setOnActionExpandListener(@Nullable android.view.MenuItem.OnActionExpandListener);
+ method @NonNull public android.view.MenuItem setOnMenuItemClickListener(@Nullable android.view.MenuItem.OnMenuItemClickListener);
+ method @NonNull public android.view.MenuItem setShortcut(char, char);
+ method @NonNull public default android.view.MenuItem setShortcut(char, char, int, int);
method public void setShowAsAction(int);
- method public android.view.MenuItem setShowAsActionFlags(int);
- method public android.view.MenuItem setTitle(CharSequence);
- method public android.view.MenuItem setTitle(@StringRes int);
- method public android.view.MenuItem setTitleCondensed(CharSequence);
- method public default android.view.MenuItem setTooltipText(CharSequence);
- method public android.view.MenuItem setVisible(boolean);
+ method @NonNull public android.view.MenuItem setShowAsActionFlags(int);
+ method @NonNull public android.view.MenuItem setTitle(@Nullable CharSequence);
+ method @NonNull public android.view.MenuItem setTitle(@StringRes int);
+ method @NonNull public android.view.MenuItem setTitleCondensed(@Nullable CharSequence);
+ method @NonNull public default android.view.MenuItem setTooltipText(@Nullable CharSequence);
+ method @NonNull public android.view.MenuItem setVisible(boolean);
field public static final int SHOW_AS_ACTION_ALWAYS = 2; // 0x2
field public static final int SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW = 8; // 0x8
field public static final int SHOW_AS_ACTION_IF_ROOM = 1; // 0x1
@@ -48719,12 +48669,12 @@
}
public static interface MenuItem.OnActionExpandListener {
- method public boolean onMenuItemActionCollapse(android.view.MenuItem);
- method public boolean onMenuItemActionExpand(android.view.MenuItem);
+ method public boolean onMenuItemActionCollapse(@NonNull android.view.MenuItem);
+ method public boolean onMenuItemActionExpand(@NonNull android.view.MenuItem);
}
public static interface MenuItem.OnMenuItemClickListener {
- method public boolean onMenuItemClick(android.view.MenuItem);
+ method public boolean onMenuItemClick(@NonNull android.view.MenuItem);
}
public final class MotionEvent extends android.view.InputEvent implements android.os.Parcelable {
@@ -48943,16 +48893,15 @@
method public default void onBackInvoked();
}
- public abstract class OnBackInvokedDispatcher {
- ctor public OnBackInvokedDispatcher();
- method public abstract void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, int);
- method public abstract void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
+ public interface OnBackInvokedDispatcher {
+ method public void registerOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback, @IntRange(from=0) int);
+ method public void unregisterOnBackInvokedCallback(@NonNull android.view.OnBackInvokedCallback);
field public static final int PRIORITY_DEFAULT = 0; // 0x0
field public static final int PRIORITY_OVERLAY = 1000000; // 0xf4240
}
public interface OnBackInvokedDispatcherOwner {
- method @Nullable public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
+ method @NonNull public android.view.OnBackInvokedDispatcher getOnBackInvokedDispatcher();
}
public interface OnReceiveContentListener {
@@ -49000,10 +48949,10 @@
}
public final class PointerIcon implements android.os.Parcelable {
- method public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float);
+ method @NonNull public static android.view.PointerIcon create(@NonNull android.graphics.Bitmap, float, float);
method public int describeContents();
- method public static android.view.PointerIcon getSystemIcon(@NonNull android.content.Context, int);
- method public static android.view.PointerIcon load(@NonNull android.content.res.Resources, @XmlRes int);
+ method @NonNull public static android.view.PointerIcon getSystemIcon(@NonNull android.content.Context, int);
+ method @NonNull public static android.view.PointerIcon load(@NonNull android.content.res.Resources, @XmlRes int);
method public void writeToParcel(android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.view.PointerIcon> CREATOR;
field public static final int TYPE_ALIAS = 1010; // 0x3f2
@@ -49046,8 +48995,8 @@
}
public class ScaleGestureDetector {
- ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener);
- ctor public ScaleGestureDetector(android.content.Context, android.view.ScaleGestureDetector.OnScaleGestureListener, android.os.Handler);
+ ctor public ScaleGestureDetector(@NonNull android.content.Context, @NonNull android.view.ScaleGestureDetector.OnScaleGestureListener);
+ ctor public ScaleGestureDetector(@NonNull android.content.Context, @NonNull android.view.ScaleGestureDetector.OnScaleGestureListener, @Nullable android.os.Handler);
method public float getCurrentSpan();
method public float getCurrentSpanX();
method public float getCurrentSpanY();
@@ -49062,22 +49011,22 @@
method public boolean isInProgress();
method public boolean isQuickScaleEnabled();
method public boolean isStylusScaleEnabled();
- method public boolean onTouchEvent(android.view.MotionEvent);
+ method public boolean onTouchEvent(@NonNull android.view.MotionEvent);
method public void setQuickScaleEnabled(boolean);
method public void setStylusScaleEnabled(boolean);
}
public static interface ScaleGestureDetector.OnScaleGestureListener {
- method public boolean onScale(android.view.ScaleGestureDetector);
- method public boolean onScaleBegin(android.view.ScaleGestureDetector);
- method public void onScaleEnd(android.view.ScaleGestureDetector);
+ method public boolean onScale(@NonNull android.view.ScaleGestureDetector);
+ method public boolean onScaleBegin(@NonNull android.view.ScaleGestureDetector);
+ method public void onScaleEnd(@NonNull android.view.ScaleGestureDetector);
}
public static class ScaleGestureDetector.SimpleOnScaleGestureListener implements android.view.ScaleGestureDetector.OnScaleGestureListener {
ctor public ScaleGestureDetector.SimpleOnScaleGestureListener();
- method public boolean onScale(android.view.ScaleGestureDetector);
- method public boolean onScaleBegin(android.view.ScaleGestureDetector);
- method public void onScaleEnd(android.view.ScaleGestureDetector);
+ method public boolean onScale(@NonNull android.view.ScaleGestureDetector);
+ method public boolean onScaleBegin(@NonNull android.view.ScaleGestureDetector);
+ method public void onScaleEnd(@NonNull android.view.ScaleGestureDetector);
}
@UiThread public interface ScrollCaptureCallback {
@@ -49195,7 +49144,7 @@
public static class SurfaceControl.Transaction implements java.io.Closeable android.os.Parcelable {
ctor public SurfaceControl.Transaction();
- method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.TransactionCommittedListener);
+ method @NonNull public android.view.SurfaceControl.Transaction addTransactionCommittedListener(@NonNull java.util.concurrent.Executor, @NonNull android.view.SurfaceControl.TransactionCommittedListener);
method public void apply();
method public void close();
method public int describeContents();
@@ -49219,6 +49168,10 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.SurfaceControl.Transaction> CREATOR;
}
+ public static interface SurfaceControl.TransactionCommittedListener {
+ method public void onTransactionCommitted();
+ }
+
public class SurfaceControlViewHost {
ctor public SurfaceControlViewHost(@NonNull android.content.Context, @NonNull android.view.Display, @Nullable android.os.IBinder);
method @Nullable public android.view.SurfaceControlViewHost.SurfacePackage getSurfacePackage();
@@ -49331,10 +49284,6 @@
field public static final int TO_RIGHT = 8; // 0x8
}
- public interface TransactionCommittedListener {
- method public void onTransactionCommitted();
- }
-
public final class VelocityTracker {
method public void addMovement(android.view.MotionEvent);
method public void clear();
@@ -49380,7 +49329,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);
@@ -49448,7 +49397,7 @@
method public boolean dispatchKeyShortcutEvent(android.view.KeyEvent);
method public boolean dispatchNestedFling(float, float, boolean);
method public boolean dispatchNestedPreFling(float, float);
- method public boolean dispatchNestedPrePerformAccessibilityAction(int, android.os.Bundle);
+ method public boolean dispatchNestedPrePerformAccessibilityAction(int, @Nullable android.os.Bundle);
method public boolean dispatchNestedPreScroll(int, int, @Nullable @Size(2) int[], @Nullable @Size(2) int[]);
method public boolean dispatchNestedScroll(int, int, int, int, @Nullable @Size(2) int[]);
method public void dispatchPointerCaptureChanged(boolean);
@@ -49584,7 +49533,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();
@@ -49810,7 +49758,7 @@
method @Deprecated public void onWindowSystemUiVisibilityChanged(int);
method protected void onWindowVisibilityChanged(int);
method protected boolean overScrollBy(int, int, int, int, int, int, int, int, boolean);
- method public boolean performAccessibilityAction(int, android.os.Bundle);
+ method public boolean performAccessibilityAction(int, @Nullable android.os.Bundle);
method public boolean performClick();
method public boolean performContextClick(float, float);
method public boolean performContextClick();
@@ -50221,15 +50169,15 @@
public static class View.AccessibilityDelegate {
ctor public View.AccessibilityDelegate();
method public void addExtraDataToAccessibilityNodeInfo(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityNodeInfo, @NonNull String, @Nullable android.os.Bundle);
- method public boolean dispatchPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
- method public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(android.view.View);
- method public void onInitializeAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
- method public void onInitializeAccessibilityNodeInfo(android.view.View, android.view.accessibility.AccessibilityNodeInfo);
- method public void onPopulateAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
- method public boolean onRequestSendAccessibilityEvent(android.view.ViewGroup, android.view.View, android.view.accessibility.AccessibilityEvent);
- method public boolean performAccessibilityAction(android.view.View, int, android.os.Bundle);
- method public void sendAccessibilityEvent(android.view.View, int);
- method public void sendAccessibilityEventUnchecked(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean dispatchPopulateAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent);
+ method @Nullable public android.view.accessibility.AccessibilityNodeProvider getAccessibilityNodeProvider(@NonNull android.view.View);
+ method public void onInitializeAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent);
+ method public void onInitializeAccessibilityNodeInfo(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityNodeInfo);
+ method public void onPopulateAccessibilityEvent(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent);
+ method public boolean onRequestSendAccessibilityEvent(@NonNull android.view.ViewGroup, @NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent);
+ method public boolean performAccessibilityAction(@NonNull android.view.View, int, @Nullable android.os.Bundle);
+ method public void sendAccessibilityEvent(@NonNull android.view.View, int);
+ method public void sendAccessibilityEventUnchecked(@NonNull android.view.View, @NonNull android.view.accessibility.AccessibilityEvent);
}
public static class View.BaseSavedState extends android.view.AbsSavedState {
@@ -50259,12 +50207,12 @@
}
public static interface View.OnApplyWindowInsetsListener {
- method public android.view.WindowInsets onApplyWindowInsets(android.view.View, android.view.WindowInsets);
+ method @NonNull public android.view.WindowInsets onApplyWindowInsets(@NonNull android.view.View, @NonNull android.view.WindowInsets);
}
public static interface View.OnAttachStateChangeListener {
- method public void onViewAttachedToWindow(android.view.View);
- method public void onViewDetachedFromWindow(android.view.View);
+ method public void onViewAttachedToWindow(@NonNull android.view.View);
+ method public void onViewDetachedFromWindow(@NonNull android.view.View);
}
public static interface View.OnCapturedPointerListener {
@@ -50333,7 +50281,7 @@
public class ViewConfiguration {
ctor @Deprecated public ViewConfiguration();
- method public static android.view.ViewConfiguration get(@UiContext android.content.Context);
+ method public static android.view.ViewConfiguration get(@NonNull @UiContext android.content.Context);
method @Deprecated @FloatRange(from=1.0) public static float getAmbiguousGestureMultiplier();
method public static long getDefaultActionModeHideDuration();
method public static int getDoubleTapTimeout();
@@ -50652,8 +50600,8 @@
method public boolean canResolveLayoutDirection();
method public boolean canResolveTextAlignment();
method public boolean canResolveTextDirection();
- method public void childDrawableStateChanged(android.view.View);
- method public void childHasTransientStateChanged(android.view.View, boolean);
+ method public void childDrawableStateChanged(@NonNull android.view.View);
+ method public void childHasTransientStateChanged(@NonNull android.view.View, boolean);
method public void clearChildFocus(android.view.View);
method public void createContextMenu(android.view.ContextMenu);
method public android.view.View focusSearch(android.view.View, int);
@@ -50671,23 +50619,23 @@
method public boolean isTextAlignmentResolved();
method public boolean isTextDirectionResolved();
method public android.view.View keyboardNavigationClusterSearch(android.view.View, int);
- method public void notifySubtreeAccessibilityStateChanged(android.view.View, @NonNull android.view.View, int);
+ method public void notifySubtreeAccessibilityStateChanged(@NonNull android.view.View, @NonNull android.view.View, int);
method public default void onDescendantInvalidated(@NonNull android.view.View, @NonNull android.view.View);
- method public boolean onNestedFling(android.view.View, float, float, boolean);
- method public boolean onNestedPreFling(android.view.View, float, float);
- method public boolean onNestedPrePerformAccessibilityAction(android.view.View, int, android.os.Bundle);
- method public void onNestedPreScroll(android.view.View, int, int, int[]);
- method public void onNestedScroll(android.view.View, int, int, int, int);
- method public void onNestedScrollAccepted(android.view.View, android.view.View, int);
- method public boolean onStartNestedScroll(android.view.View, android.view.View, int);
- method public void onStopNestedScroll(android.view.View);
+ method public boolean onNestedFling(@NonNull android.view.View, float, float, boolean);
+ method public boolean onNestedPreFling(@NonNull android.view.View, float, float);
+ method public boolean onNestedPrePerformAccessibilityAction(@NonNull android.view.View, int, @Nullable android.os.Bundle);
+ method public void onNestedPreScroll(@NonNull android.view.View, int, int, @NonNull int[]);
+ method public void onNestedScroll(@NonNull android.view.View, int, int, int, int);
+ method public void onNestedScrollAccepted(@NonNull android.view.View, @NonNull android.view.View, int);
+ method public boolean onStartNestedScroll(@NonNull android.view.View, @NonNull android.view.View, int);
+ method public void onStopNestedScroll(@NonNull android.view.View);
method public void recomputeViewAttributes(android.view.View);
method public void requestChildFocus(android.view.View, android.view.View);
- method public boolean requestChildRectangleOnScreen(android.view.View, android.graphics.Rect, boolean);
+ method public boolean requestChildRectangleOnScreen(@NonNull android.view.View, android.graphics.Rect, boolean);
method public void requestDisallowInterceptTouchEvent(boolean);
method public void requestFitSystemWindows();
method public void requestLayout();
- method public boolean requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent);
+ method public boolean requestSendAccessibilityEvent(@NonNull android.view.View, android.view.accessibility.AccessibilityEvent);
method public void requestTransparentRegion(android.view.View);
method public boolean showContextMenuForChild(android.view.View);
method public boolean showContextMenuForChild(android.view.View, float, float);
@@ -50696,43 +50644,43 @@
}
public class ViewPropertyAnimator {
- method public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
- method public android.view.ViewPropertyAnimator alphaBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator alpha(@FloatRange(from=0.0f, to=1.0f) float);
+ method @NonNull public android.view.ViewPropertyAnimator alphaBy(float);
method public void cancel();
method public long getDuration();
- method public android.animation.TimeInterpolator getInterpolator();
+ method @Nullable public android.animation.TimeInterpolator getInterpolator();
method public long getStartDelay();
- method public android.view.ViewPropertyAnimator rotation(float);
- method public android.view.ViewPropertyAnimator rotationBy(float);
- method public android.view.ViewPropertyAnimator rotationX(float);
- method public android.view.ViewPropertyAnimator rotationXBy(float);
- method public android.view.ViewPropertyAnimator rotationY(float);
- method public android.view.ViewPropertyAnimator rotationYBy(float);
- method public android.view.ViewPropertyAnimator scaleX(float);
- method public android.view.ViewPropertyAnimator scaleXBy(float);
- method public android.view.ViewPropertyAnimator scaleY(float);
- method public android.view.ViewPropertyAnimator scaleYBy(float);
- method public android.view.ViewPropertyAnimator setDuration(long);
- method public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator);
- method public android.view.ViewPropertyAnimator setListener(android.animation.Animator.AnimatorListener);
- method public android.view.ViewPropertyAnimator setStartDelay(long);
- method public android.view.ViewPropertyAnimator setUpdateListener(android.animation.ValueAnimator.AnimatorUpdateListener);
+ method @NonNull public android.view.ViewPropertyAnimator rotation(float);
+ method @NonNull public android.view.ViewPropertyAnimator rotationBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator rotationX(float);
+ method @NonNull public android.view.ViewPropertyAnimator rotationXBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator rotationY(float);
+ method @NonNull public android.view.ViewPropertyAnimator rotationYBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator scaleX(float);
+ method @NonNull public android.view.ViewPropertyAnimator scaleXBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator scaleY(float);
+ method @NonNull public android.view.ViewPropertyAnimator scaleYBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator setDuration(long);
+ method @NonNull public android.view.ViewPropertyAnimator setInterpolator(android.animation.TimeInterpolator);
+ method @NonNull public android.view.ViewPropertyAnimator setListener(@Nullable android.animation.Animator.AnimatorListener);
+ method @NonNull public android.view.ViewPropertyAnimator setStartDelay(long);
+ method @NonNull public android.view.ViewPropertyAnimator setUpdateListener(@Nullable android.animation.ValueAnimator.AnimatorUpdateListener);
method public void start();
- method public android.view.ViewPropertyAnimator translationX(float);
- method public android.view.ViewPropertyAnimator translationXBy(float);
- method public android.view.ViewPropertyAnimator translationY(float);
- method public android.view.ViewPropertyAnimator translationYBy(float);
- method public android.view.ViewPropertyAnimator translationZ(float);
- method public android.view.ViewPropertyAnimator translationZBy(float);
- method public android.view.ViewPropertyAnimator withEndAction(Runnable);
- method public android.view.ViewPropertyAnimator withLayer();
- method public android.view.ViewPropertyAnimator withStartAction(Runnable);
- method public android.view.ViewPropertyAnimator x(float);
- method public android.view.ViewPropertyAnimator xBy(float);
- method public android.view.ViewPropertyAnimator y(float);
- method public android.view.ViewPropertyAnimator yBy(float);
- method public android.view.ViewPropertyAnimator z(float);
- method public android.view.ViewPropertyAnimator zBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationX(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationXBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationY(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationYBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationZ(float);
+ method @NonNull public android.view.ViewPropertyAnimator translationZBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator withEndAction(Runnable);
+ method @NonNull public android.view.ViewPropertyAnimator withLayer();
+ method @NonNull public android.view.ViewPropertyAnimator withStartAction(Runnable);
+ method @NonNull public android.view.ViewPropertyAnimator x(float);
+ method @NonNull public android.view.ViewPropertyAnimator xBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator y(float);
+ method @NonNull public android.view.ViewPropertyAnimator yBy(float);
+ method @NonNull public android.view.ViewPropertyAnimator z(float);
+ method @NonNull public android.view.ViewPropertyAnimator zBy(float);
}
public abstract class ViewStructure {
@@ -51477,6 +51425,7 @@
method public CharSequence getPackageName();
method public android.view.accessibility.AccessibilityRecord getRecord(int);
method public int getRecordCount();
+ method public int getSpeechStateChangeTypes();
method public int getWindowChanges();
method public void initFromParcel(android.os.Parcel);
method @Deprecated public static android.view.accessibility.AccessibilityEvent obtain(int);
@@ -51488,6 +51437,7 @@
method public void setEventType(int);
method public void setMovementGranularity(int);
method public void setPackageName(CharSequence);
+ method public void setSpeechStateChangeTypes(int);
method public void writeToParcel(android.os.Parcel, int);
field public static final int CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION = 4; // 0x4
field public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 512; // 0x200
@@ -51503,12 +51453,17 @@
field @NonNull public static final android.os.Parcelable.Creator<android.view.accessibility.AccessibilityEvent> CREATOR;
field public static final int INVALID_POSITION = -1; // 0xffffffff
field @Deprecated public static final int MAX_TEXT_LENGTH = 500; // 0x1f4
+ field public static final int SPEECH_STATE_LISTENING_END = 8; // 0x8
+ field public static final int SPEECH_STATE_LISTENING_START = 4; // 0x4
+ field public static final int SPEECH_STATE_SPEAKING_END = 2; // 0x2
+ field public static final int SPEECH_STATE_SPEAKING_START = 1; // 0x1
field public static final int TYPES_ALL_MASK = -1; // 0xffffffff
field public static final int TYPE_ANNOUNCEMENT = 16384; // 0x4000
field public static final int TYPE_ASSIST_READING_CONTEXT = 16777216; // 0x1000000
field public static final int TYPE_GESTURE_DETECTION_END = 524288; // 0x80000
field public static final int TYPE_GESTURE_DETECTION_START = 262144; // 0x40000
field public static final int TYPE_NOTIFICATION_STATE_CHANGED = 64; // 0x40
+ field public static final int TYPE_SPEECH_STATE_CHANGE = 33554432; // 0x2000000
field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_END = 1024; // 0x400
field public static final int TYPE_TOUCH_EXPLORATION_GESTURE_START = 512; // 0x200
field public static final int TYPE_TOUCH_INTERACTION_END = 2097152; // 0x200000
@@ -51923,10 +51878,10 @@
public abstract class AccessibilityNodeProvider {
ctor public AccessibilityNodeProvider();
method public void addExtraDataToAccessibilityNodeInfo(int, android.view.accessibility.AccessibilityNodeInfo, String, android.os.Bundle);
- method public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
- method public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String, int);
- method public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
- method public boolean performAction(int, int, android.os.Bundle);
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo createAccessibilityNodeInfo(int);
+ method @Nullable public java.util.List<android.view.accessibility.AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String, int);
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo findFocus(int);
+ method public boolean performAction(int, int, @Nullable android.os.Bundle);
field public static final int HOST_VIEW_ID = -1; // 0xffffffff
}
@@ -51934,23 +51889,23 @@
ctor public AccessibilityRecord();
ctor public AccessibilityRecord(@NonNull android.view.accessibility.AccessibilityRecord);
method public int getAddedCount();
- method public CharSequence getBeforeText();
- method public CharSequence getClassName();
- method public CharSequence getContentDescription();
+ method @Nullable public CharSequence getBeforeText();
+ method @Nullable public CharSequence getClassName();
+ method @Nullable public CharSequence getContentDescription();
method public int getCurrentItemIndex();
method public int getDisplayId();
method public int getFromIndex();
method public int getItemCount();
method public int getMaxScrollX();
method public int getMaxScrollY();
- method public android.os.Parcelable getParcelableData();
+ method @Nullable public android.os.Parcelable getParcelableData();
method public int getRemovedCount();
method public int getScrollDeltaX();
method public int getScrollDeltaY();
method public int getScrollX();
method public int getScrollY();
- method public android.view.accessibility.AccessibilityNodeInfo getSource();
- method public java.util.List<java.lang.CharSequence> getText();
+ method @Nullable public android.view.accessibility.AccessibilityNodeInfo getSource();
+ method @NonNull public java.util.List<java.lang.CharSequence> getText();
method public int getToIndex();
method public int getWindowId();
method public boolean isChecked();
@@ -51958,14 +51913,14 @@
method public boolean isFullScreen();
method public boolean isPassword();
method public boolean isScrollable();
- method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain(android.view.accessibility.AccessibilityRecord);
- method @Deprecated public static android.view.accessibility.AccessibilityRecord obtain();
+ method @Deprecated @NonNull public static android.view.accessibility.AccessibilityRecord obtain(@NonNull android.view.accessibility.AccessibilityRecord);
+ method @Deprecated @NonNull public static android.view.accessibility.AccessibilityRecord obtain();
method @Deprecated public void recycle();
method public void setAddedCount(int);
- method public void setBeforeText(CharSequence);
+ method public void setBeforeText(@Nullable CharSequence);
method public void setChecked(boolean);
- method public void setClassName(CharSequence);
- method public void setContentDescription(CharSequence);
+ method public void setClassName(@Nullable CharSequence);
+ method public void setContentDescription(@Nullable CharSequence);
method public void setCurrentItemIndex(int);
method public void setEnabled(boolean);
method public void setFromIndex(int);
@@ -51973,7 +51928,7 @@
method public void setItemCount(int);
method public void setMaxScrollX(int);
method public void setMaxScrollY(int);
- method public void setParcelableData(android.os.Parcelable);
+ method public void setParcelableData(@Nullable android.os.Parcelable);
method public void setPassword(boolean);
method public void setRemovedCount(int);
method public void setScrollDeltaX(int);
@@ -51981,7 +51936,7 @@
method public void setScrollX(int);
method public void setScrollY(int);
method public void setScrollable(boolean);
- method public void setSource(android.view.View);
+ method public void setSource(@Nullable android.view.View);
method public void setSource(@Nullable android.view.View, int);
method public void setToIndex(int);
}
@@ -52304,7 +52259,7 @@
}
public class PathInterpolator extends android.view.animation.BaseInterpolator {
- ctor public PathInterpolator(android.graphics.Path);
+ ctor public PathInterpolator(@NonNull android.graphics.Path);
ctor public PathInterpolator(float, float);
ctor public PathInterpolator(float, float, float, float);
ctor public PathInterpolator(android.content.Context, android.util.AttributeSet);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index 6cde547..36d54f5 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -78,39 +78,14 @@
package android.app.admin {
public class DevicePolicyManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void acknowledgeNewUserDisclaimer();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public void acknowledgeNewUserDisclaimer();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getLogoutUser();
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS}) public int logoutUser();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public int logoutUser();
field public static final String ACTION_SHOW_NEW_USER_DISCLAIMER = "android.app.action.SHOW_NEW_USER_DISCLAIMER";
}
}
-package android.app.usage {
-
- public class NetworkStatsManager {
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void forceUpdate();
- method public static int getCollapsedRatType(int);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void notifyNetworkStatus(@NonNull java.util.List<android.net.Network>, @NonNull java.util.List<android.net.NetworkStateSnapshot>, @Nullable String, @NonNull java.util.List<android.net.UnderlyingNetworkInfo>);
- method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForDevice(@NonNull android.net.NetworkTemplate, long, long);
- method @NonNull @WorkerThread public android.app.usage.NetworkStats queryDetailsForUidTagState(@NonNull android.net.NetworkTemplate, long, long, int, int, int) throws java.lang.SecurityException;
- method @NonNull @WorkerThread public android.app.usage.NetworkStats querySummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
- method @NonNull @WorkerThread public android.app.usage.NetworkStats.Bucket querySummaryForDevice(@NonNull android.net.NetworkTemplate, long, long);
- method @NonNull @WorkerThread public android.app.usage.NetworkStats queryTaggedSummary(@NonNull android.net.NetworkTemplate, long, long) throws java.lang.SecurityException;
- method public void registerUsageCallback(@NonNull android.net.NetworkTemplate, long, @NonNull java.util.concurrent.Executor, @NonNull android.app.usage.NetworkStatsManager.UsageCallback);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setDefaultGlobalAlert(long);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setPollOnOpen(boolean);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setStatsProviderWarningAndLimitAsync(@NonNull String, long, long);
- method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK}) public void setUidForeground(int, boolean);
- field public static final int NETWORK_TYPE_5G_NSA = -2; // 0xfffffffe
- }
-
- public abstract static class NetworkStatsManager.UsageCallback {
- method public void onThresholdReached(@NonNull android.net.NetworkTemplate);
- }
-
-}
-
package android.content {
public abstract class ContentProvider implements android.content.ComponentCallbacks2 {
@@ -139,8 +114,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
}
}
@@ -191,7 +172,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);
@@ -201,18 +182,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 {
@@ -277,44 +258,10 @@
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);
}
- public class NetworkIdentity {
- method public int getOemManaged();
- method public int getRatType();
- method @Nullable public String getSubscriberId();
- method public int getType();
- method @Nullable public String getWifiNetworkKey();
- method public boolean isDefaultNetwork();
- method public boolean isMetered();
- method public boolean isRoaming();
- }
-
- public static final class NetworkIdentity.Builder {
- ctor public NetworkIdentity.Builder();
- method @NonNull public android.net.NetworkIdentity build();
- method @NonNull public android.net.NetworkIdentity.Builder clearRatType();
- method @NonNull public android.net.NetworkIdentity.Builder setDefaultNetwork(boolean);
- method @NonNull public android.net.NetworkIdentity.Builder setMetered(boolean);
- method @NonNull public android.net.NetworkIdentity.Builder setNetworkStateSnapshot(@NonNull android.net.NetworkStateSnapshot);
- method @NonNull public android.net.NetworkIdentity.Builder setOemManaged(int);
- method @NonNull public android.net.NetworkIdentity.Builder setRatType(int);
- method @NonNull public android.net.NetworkIdentity.Builder setRoaming(boolean);
- method @NonNull public android.net.NetworkIdentity.Builder setSubscriberId(@Nullable String);
- method @NonNull public android.net.NetworkIdentity.Builder setType(int);
- method @NonNull public android.net.NetworkIdentity.Builder setWifiNetworkKey(@Nullable String);
- }
-
public class NetworkPolicyManager {
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getMultipathPreference(@NonNull android.net.Network);
method @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public int getRestrictBackgroundStatus(int);
@@ -330,94 +277,6 @@
method public default void onUidBlockedReasonChanged(int, int);
}
- public final class NetworkStateSnapshot implements android.os.Parcelable {
- ctor public NetworkStateSnapshot(@NonNull android.net.Network, @NonNull android.net.NetworkCapabilities, @NonNull android.net.LinkProperties, @Nullable String, int);
- method public int describeContents();
- method public int getLegacyType();
- method @NonNull public android.net.LinkProperties getLinkProperties();
- method @NonNull public android.net.Network getNetwork();
- method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
- method @Nullable public String getSubscriberId();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStateSnapshot> CREATOR;
- }
-
- public class NetworkStatsCollection {
- method @NonNull public java.util.Map<android.net.NetworkStatsCollection.Key,android.net.NetworkStatsHistory> getEntries();
- }
-
- public static final class NetworkStatsCollection.Builder {
- ctor public NetworkStatsCollection.Builder(long);
- method @NonNull public android.net.NetworkStatsCollection.Builder addEntry(@NonNull android.net.NetworkStatsCollection.Key, @NonNull android.net.NetworkStatsHistory);
- method @NonNull public android.net.NetworkStatsCollection build();
- }
-
- public static class NetworkStatsCollection.Key {
- ctor public NetworkStatsCollection.Key(@NonNull java.util.Set<android.net.NetworkIdentity>, int, int, int);
- }
-
- public final class NetworkStatsHistory implements android.os.Parcelable {
- method public int describeContents();
- method @NonNull public java.util.List<android.net.NetworkStatsHistory.Entry> getEntries();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStatsHistory> CREATOR;
- }
-
- public static final class NetworkStatsHistory.Builder {
- ctor public NetworkStatsHistory.Builder(long, int);
- method @NonNull public android.net.NetworkStatsHistory.Builder addEntry(@NonNull android.net.NetworkStatsHistory.Entry);
- method @NonNull public android.net.NetworkStatsHistory build();
- }
-
- public static final class NetworkStatsHistory.Entry {
- ctor public NetworkStatsHistory.Entry(long, long, long, long, long, long, long);
- method public long getActiveTime();
- method public long getBucketStart();
- method public long getOperations();
- method public long getRxBytes();
- method public long getRxPackets();
- method public long getTxBytes();
- method public long getTxPackets();
- }
-
- public final class NetworkTemplate implements android.os.Parcelable {
- method public int describeContents();
- method public int getDefaultNetworkStatus();
- method public int getMatchRule();
- method public int getMeteredness();
- method public int getOemManaged();
- method public int getRatType();
- method public int getRoaming();
- method @NonNull public java.util.Set<java.lang.String> getSubscriberIds();
- method @NonNull public java.util.Set<java.lang.String> getWifiNetworkKeys();
- method public boolean matches(@NonNull android.net.NetworkIdentity);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkTemplate> CREATOR;
- field public static final int MATCH_BLUETOOTH = 8; // 0x8
- field public static final int MATCH_CARRIER = 10; // 0xa
- field public static final int MATCH_ETHERNET = 5; // 0x5
- field public static final int MATCH_MOBILE = 1; // 0x1
- field public static final int MATCH_WIFI = 4; // 0x4
- field public static final int NETWORK_TYPE_ALL = -1; // 0xffffffff
- field public static final int OEM_MANAGED_ALL = -1; // 0xffffffff
- field public static final int OEM_MANAGED_NO = 0; // 0x0
- field public static final int OEM_MANAGED_PAID = 1; // 0x1
- field public static final int OEM_MANAGED_PRIVATE = 2; // 0x2
- field public static final int OEM_MANAGED_YES = -2; // 0xfffffffe
- }
-
- public static final class NetworkTemplate.Builder {
- ctor public NetworkTemplate.Builder(int);
- method @NonNull public android.net.NetworkTemplate build();
- method @NonNull public android.net.NetworkTemplate.Builder setDefaultNetworkStatus(int);
- method @NonNull public android.net.NetworkTemplate.Builder setMeteredness(int);
- method @NonNull public android.net.NetworkTemplate.Builder setOemManaged(int);
- method @NonNull public android.net.NetworkTemplate.Builder setRatType(int);
- method @NonNull public android.net.NetworkTemplate.Builder setRoaming(int);
- method @NonNull public android.net.NetworkTemplate.Builder setSubscriberIds(@NonNull java.util.Set<java.lang.String>);
- method @NonNull public android.net.NetworkTemplate.Builder setWifiNetworkKeys(@NonNull java.util.Set<java.lang.String>);
- }
-
public class NetworkWatchlistManager {
method @Nullable public byte[] getWatchlistConfigHash();
}
@@ -436,21 +295,6 @@
method public static void setHttpProxyConfiguration(@Nullable android.net.ProxyInfo);
}
- public class TrafficStats {
- method public static void attachSocketTagger();
- method public static void init(@NonNull android.content.Context);
- }
-
- public final class UnderlyingNetworkInfo implements android.os.Parcelable {
- ctor public UnderlyingNetworkInfo(int, @NonNull String, @NonNull java.util.List<java.lang.String>);
- method public int describeContents();
- method @NonNull public String getInterface();
- method public int getOwnerUid();
- method @NonNull public java.util.List<java.lang.String> getUnderlyingInterfaces();
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.UnderlyingNetworkInfo> CREATOR;
- }
-
public class VpnManager {
field public static final int TYPE_VPN_LEGACY = 3; // 0x3
field public static final int TYPE_VPN_NONE = -1; // 0xffffffff
diff --git a/core/api/removed.txt b/core/api/removed.txt
index 311b110..608a9a4 100644
--- a/core/api/removed.txt
+++ b/core/api/removed.txt
@@ -250,10 +250,6 @@
method @Deprecated public static org.apache.http.conn.ssl.SSLSocketFactory getHttpSocketFactory(int, android.net.SSLSessionCache);
}
- public class TrafficStats {
- method @Deprecated public static void setThreadStatsUidSelf();
- }
-
}
package android.os {
@@ -513,7 +509,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 49a1d62..6267dbf3 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -76,6 +76,7 @@
field public static final String BIND_TRANSLATION_SERVICE = "android.permission.BIND_TRANSLATION_SERVICE";
field public static final String BIND_TRUST_AGENT = "android.permission.BIND_TRUST_AGENT";
field public static final String BIND_TV_REMOTE_SERVICE = "android.permission.BIND_TV_REMOTE_SERVICE";
+ field public static final String BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE = "android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE";
field public static final String BLUETOOTH_MAP = "android.permission.BLUETOOTH_MAP";
field public static final String BRICK = "android.permission.BRICK";
field public static final String BRIGHTNESS_SLIDER_USAGE = "android.permission.BRIGHTNESS_SLIDER_USAGE";
@@ -165,6 +166,7 @@
field public static final String MANAGE_CONTENT_SUGGESTIONS = "android.permission.MANAGE_CONTENT_SUGGESTIONS";
field public static final String MANAGE_DEBUGGING = "android.permission.MANAGE_DEBUGGING";
field public static final String MANAGE_DEVICE_ADMINS = "android.permission.MANAGE_DEVICE_ADMINS";
+ field public static final String MANAGE_ETHERNET_NETWORKS = "android.permission.MANAGE_ETHERNET_NETWORKS";
field public static final String MANAGE_FACTORY_RESET_PROTECTION = "android.permission.MANAGE_FACTORY_RESET_PROTECTION";
field public static final String MANAGE_GAME_ACTIVITY = "android.permission.MANAGE_GAME_ACTIVITY";
field public static final String MANAGE_GAME_MODE = "android.permission.MANAGE_GAME_MODE";
@@ -191,6 +193,7 @@
field public static final String MANAGE_USB = "android.permission.MANAGE_USB";
field public static final String MANAGE_USERS = "android.permission.MANAGE_USERS";
field public static final String MANAGE_USER_OEM_UNLOCK_STATE = "android.permission.MANAGE_USER_OEM_UNLOCK_STATE";
+ field public static final String MANAGE_WALLPAPER_EFFECTS_GENERATION = "android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION";
field public static final String MANAGE_WEAK_ESCROW_TOKEN = "android.permission.MANAGE_WEAK_ESCROW_TOKEN";
field public static final String MANAGE_WIFI_AUTO_JOIN = "android.permission.MANAGE_WIFI_AUTO_JOIN";
field public static final String MANAGE_WIFI_COUNTRY_CODE = "android.permission.MANAGE_WIFI_COUNTRY_CODE";
@@ -908,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
}
@@ -1069,6 +1085,7 @@
method public boolean isDeviceManaged();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioned();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public boolean isDeviceProvisioningConfigApplied();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public boolean isDpcDownloaded();
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isManagedKiosk();
method public boolean isSecondaryLockscreenEnabled(@NonNull android.os.UserHandle);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}) public boolean isUnattendedManagedKiosk();
@@ -1081,6 +1098,7 @@
method @RequiresPermission(android.Manifest.permission.SEND_LOST_MODE_LOCATION_UPDATES) public void sendLostModeLocationUpdate(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Boolean>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public boolean setActiveProfileOwner(@NonNull android.content.ComponentName, String) throws java.lang.IllegalArgumentException;
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public void setDeviceProvisioningConfigApplied();
+ method @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS) public void setDpcDownloaded(boolean);
method @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_MANAGEMENT_RESOURCES) public void setDrawables(@NonNull java.util.Set<android.app.admin.DevicePolicyDrawableResource>);
method @Deprecated @RequiresPermission(value=android.Manifest.permission.GRANT_PROFILE_OWNER_DEVICE_IDS_ACCESS, conditional=true) public void setProfileOwnerCanAccessDeviceIds(@NonNull android.content.ComponentName);
method public void setSecondaryLockscreenEnabled(@NonNull android.content.ComponentName, boolean);
@@ -1159,6 +1177,13 @@
field public static final String UNDEFINED = "UNDEFINED";
}
+ public static final class DevicePolicyResources.Strings.Dialer {
+ field public static final String NOTIFICATION_INCOMING_WORK_CALL_TITLE = "Dialer.NOTIFICATION_INCOMING_WORK_CALL_TITLE";
+ field public static final String NOTIFICATION_MISSED_WORK_CALL_TITLE = "Dialer.NOTIFICATION_MISSED_WORK_CALL_TITLE";
+ field public static final String NOTIFICATION_ONGOING_WORK_CALL_TITLE = "Dialer.NOTIFICATION_ONGOING_WORK_CALL_TITLE";
+ field public static final String NOTIFICATION_WIFI_WORK_CALL_LABEL = "Dialer.NOTIFICATION_WIFI_WORK_CALL_LABEL";
+ }
+
public static final class DevicePolicyResources.Strings.DocumentsUi {
field public static final String CANT_SAVE_TO_PERSONAL_MESSAGE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE";
field public static final String CANT_SAVE_TO_PERSONAL_TITLE = "DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE";
@@ -2235,9 +2260,9 @@
public static final class SmartspaceCarouselUiTemplateData.CarouselItem implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getImage();
- method @Nullable public CharSequence getLowerText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getLowerText();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getTapAction();
- method @Nullable public CharSequence getUpperText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getUpperText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem> CREATOR;
}
@@ -2246,9 +2271,9 @@
ctor public SmartspaceCarouselUiTemplateData.CarouselItem.Builder();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem build();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setImage(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setLowerText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setTapAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceCarouselUiTemplateData.CarouselItem.Builder setUpperText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText);
}
public final class SmartspaceCombinedCardsUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
@@ -2264,15 +2289,15 @@
public class SmartspaceDefaultUiTemplateData implements android.os.Parcelable {
method public int describeContents();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getPrimaryTapAction();
- method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubTitleIcon();
- method @Nullable public CharSequence getSubtitleText();
- method @Nullable public CharSequence getSupplementalAlarmText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubtitleIcon();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSubtitleText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSupplementalAlarmText();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSupplementalSubtitleIcon();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSupplementalSubtitleTapAction();
- method @Nullable public CharSequence getSupplementalSubtitleText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSupplementalSubtitleText();
method public int getTemplateType();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getTitleIcon();
- method @Nullable public CharSequence getTitleText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getTitleText();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData> CREATOR;
}
@@ -2281,23 +2306,23 @@
ctor public SmartspaceDefaultUiTemplateData.Builder(int);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData build();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setPrimaryTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull CharSequence);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSubtitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalAlarmText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleTapAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setSupplementalSubtitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder setTitleText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText);
}
public final class SmartspaceHeadToHeadUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getHeadToHeadAction();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadFirstCompetitorIcon();
- method @Nullable public CharSequence getHeadToHeadFirstCompetitorText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadFirstCompetitorText();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getHeadToHeadSecondCompetitorIcon();
- method @Nullable public CharSequence getHeadToHeadSecondCompetitorText();
- method @Nullable public CharSequence getHeadToHeadTitle();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadSecondCompetitorText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getHeadToHeadTitle();
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData> CREATOR;
}
@@ -2306,16 +2331,17 @@
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData build();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadAction(@Nullable android.app.smartspace.uitemplatedata.SmartspaceTapAction);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadFirstCompetitorText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorIcon(@Nullable android.app.smartspace.uitemplatedata.SmartspaceIcon);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable CharSequence);
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadSecondCompetitorText(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceHeadToHeadUiTemplateData.Builder setHeadToHeadTitle(@Nullable android.app.smartspace.uitemplatedata.SmartspaceText);
}
public final class SmartspaceIcon implements android.os.Parcelable {
method public int describeContents();
method @Nullable public CharSequence getContentDescription();
method @NonNull public android.graphics.drawable.Icon getIcon();
+ method public boolean shouldTint();
method public void writeToParcel(@NonNull android.os.Parcel, int);
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceIcon> CREATOR;
}
@@ -2324,46 +2350,47 @@
ctor public SmartspaceIcon.Builder(@NonNull android.graphics.drawable.Icon);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon build();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setContentDescription(@NonNull CharSequence);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon.Builder setShouldTint(boolean);
}
public final class SmartspaceSubCardUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubCardAction();
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubCardIcon();
- method @Nullable public CharSequence getSubCardText();
+ method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceText getSubCardText();
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData> CREATOR;
}
public static final class SmartspaceSubCardUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
ctor public SmartspaceSubCardUiTemplateData.Builder(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData build();
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull CharSequence);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubCardUiTemplateData.Builder setSubCardText(@NonNull android.app.smartspace.uitemplatedata.SmartspaceText);
}
public final class SmartspaceSubImageUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubImageAction();
- method @NonNull public java.util.List<java.lang.CharSequence> getSubImageTexts();
+ method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText> getSubImageTexts();
method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon> getSubImages();
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData> CREATOR;
}
public static final class SmartspaceSubImageUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
- ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>);
+ ctor public SmartspaceSubImageUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText>, @NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceIcon>);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData build();
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubImageUiTemplateData.Builder setSubImageAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
}
public final class SmartspaceSubListUiTemplateData extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData {
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceTapAction getSubListAction();
method @Nullable public android.app.smartspace.uitemplatedata.SmartspaceIcon getSubListIcon();
- method @NonNull public java.util.List<java.lang.CharSequence> getSubListTexts();
+ method @NonNull public java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText> getSubListTexts();
field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData> CREATOR;
}
public static final class SmartspaceSubListUiTemplateData.Builder extends android.app.smartspace.uitemplatedata.SmartspaceDefaultUiTemplateData.Builder {
- ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<java.lang.CharSequence>);
+ ctor public SmartspaceSubListUiTemplateData.Builder(@NonNull java.util.List<android.app.smartspace.uitemplatedata.SmartspaceText>);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData build();
- method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setCarouselAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListAction(@NonNull android.app.smartspace.uitemplatedata.SmartspaceTapAction);
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceSubListUiTemplateData.Builder setSubListIcon(@NonNull android.app.smartspace.uitemplatedata.SmartspaceIcon);
}
@@ -2387,6 +2414,21 @@
method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceTapAction.Builder setUserHandle(@Nullable android.os.UserHandle);
}
+ public final class SmartspaceText implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public CharSequence getText();
+ method @NonNull public android.text.TextUtils.TruncateAt getTruncateAtType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.smartspace.uitemplatedata.SmartspaceText> CREATOR;
+ }
+
+ public static final class SmartspaceText.Builder {
+ ctor public SmartspaceText.Builder(@NonNull CharSequence);
+ ctor public SmartspaceText.Builder(@NonNull CharSequence, @NonNull android.text.TextUtils.TruncateAt);
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceText build();
+ method @NonNull public android.app.smartspace.uitemplatedata.SmartspaceText.Builder setTruncateAtType(@NonNull android.text.TextUtils.TruncateAt);
+ }
+
}
package android.app.time {
@@ -2496,13 +2538,6 @@
field public static final String SERVICE_INTERFACE = "android.app.usage.CacheQuotaService";
}
- public class NetworkStatsManager {
- method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getMobileUidStats();
- method @NonNull @RequiresPermission(android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK) public android.net.NetworkStats getWifiUidStats();
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void registerNetworkStatsProvider(@NonNull String, @NonNull android.net.netstats.provider.NetworkStatsProvider);
- method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STATS_PROVIDER, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public void unregisterNetworkStatsProvider(@NonNull android.net.netstats.provider.NetworkStatsProvider);
- }
-
public static final class UsageEvents.Event {
method public int getInstanceId();
method @Nullable public String getNotificationChannelId();
@@ -2554,6 +2589,107 @@
}
+package android.app.wallpapereffectsgeneration {
+
+ public final class CameraAttributes implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public float[] getAnchorPointInOutputUvSpace();
+ method @NonNull public float[] getAnchorPointInWorldSpace();
+ method public float getCameraOrbitPitchDegrees();
+ method public float getCameraOrbitYawDegrees();
+ method public float getDollyDistanceInWorldSpace();
+ method public float getFrustumFarInWorldSpace();
+ method public float getFrustumNearInWorldSpace();
+ method public float getVerticalFovDegrees();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CameraAttributes> CREATOR;
+ }
+
+ public static final class CameraAttributes.Builder {
+ ctor public CameraAttributes.Builder(@NonNull float[], @NonNull float[]);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes build();
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitPitchDegrees(@FloatRange(from=-90.0F, to=90.0f) float);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setCameraOrbitYawDegrees(@FloatRange(from=-180.0F, to=180.0f) float);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setDollyDistanceInWorldSpace(float);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumFarInWorldSpace(@FloatRange(from=0.0f) float);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setFrustumNearInWorldSpace(@FloatRange(from=0.0f) float);
+ method @NonNull public android.app.wallpapereffectsgeneration.CameraAttributes.Builder setVerticalFovDegrees(@FloatRange(from=0.0f, to=180.0f, fromInclusive=false) float);
+ }
+
+ public final class CinematicEffectRequest implements android.os.Parcelable {
+ ctor public CinematicEffectRequest(@NonNull String, @NonNull android.graphics.Bitmap);
+ method public int describeContents();
+ method @NonNull public android.graphics.Bitmap getBitmap();
+ method @NonNull public String getTaskId();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectRequest> CREATOR;
+ }
+
+ public final class CinematicEffectResponse implements android.os.Parcelable {
+ method public int describeContents();
+ method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getEndKeyFrame();
+ method public int getImageContentType();
+ method @Nullable public android.app.wallpapereffectsgeneration.CameraAttributes getStartKeyFrame();
+ method public int getStatusCode();
+ method @NonNull public String getTaskId();
+ method @NonNull public java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh> getTexturedMeshes();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2; // 0x2
+ field public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3; // 0x3
+ field public static final int CINEMATIC_EFFECT_STATUS_OK = 1; // 0x1
+ field public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4; // 0x4
+ field public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5; // 0x5
+ field public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0; // 0x0
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.CinematicEffectResponse> CREATOR;
+ field public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2; // 0x2
+ field public static final int IMAGE_CONTENT_TYPE_OTHER = 3; // 0x3
+ field public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1; // 0x1
+ field public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0; // 0x0
+ }
+
+ public static final class CinematicEffectResponse.Builder {
+ ctor public CinematicEffectResponse.Builder(int, @NonNull String);
+ method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse build();
+ method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setEndKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
+ method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setImageContentType(int);
+ method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setStartKeyFrame(@Nullable android.app.wallpapereffectsgeneration.CameraAttributes);
+ method @NonNull public android.app.wallpapereffectsgeneration.CinematicEffectResponse.Builder setTexturedMeshes(@NonNull java.util.List<android.app.wallpapereffectsgeneration.TexturedMesh>);
+ }
+
+ public final class TexturedMesh implements android.os.Parcelable {
+ method public int describeContents();
+ method @NonNull public android.graphics.Bitmap getBitmap();
+ method @NonNull public int[] getIndices();
+ method @NonNull public int getIndicesLayoutType();
+ method @NonNull public float[] getVertices();
+ method @NonNull public int getVerticesLayoutType();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.app.wallpapereffectsgeneration.TexturedMesh> CREATOR;
+ field public static final int INDICES_LAYOUT_TRIANGLES = 1; // 0x1
+ field public static final int INDICES_LAYOUT_UNDEFINED = 0; // 0x0
+ field public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1; // 0x1
+ field public static final int VERTICES_LAYOUT_UNDEFINED = 0; // 0x0
+ }
+
+ public static final class TexturedMesh.Builder {
+ ctor public TexturedMesh.Builder(@NonNull android.graphics.Bitmap);
+ method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh build();
+ method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndices(@NonNull int[]);
+ method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setIndicesLayoutType(int);
+ method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVertices(@NonNull float[]);
+ method @NonNull public android.app.wallpapereffectsgeneration.TexturedMesh.Builder setVerticesLayoutType(int);
+ }
+
+ public final class WallpaperEffectsGenerationManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION) public void generateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest, @NonNull java.util.concurrent.Executor, @NonNull android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager.CinematicEffectListener);
+ }
+
+ public static interface WallpaperEffectsGenerationManager.CinematicEffectListener {
+ method public void onCinematicEffectGenerated(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
+ }
+
+}
+
package android.apphibernation {
public class AppHibernationManager {
@@ -2625,6 +2761,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public android.hardware.input.VirtualTouchscreen createVirtualTouchscreen(@NonNull android.hardware.display.VirtualDisplay, @NonNull String, int, int);
method public void launchPendingIntent(int, @NonNull android.app.PendingIntent, @NonNull java.util.concurrent.Executor, @NonNull android.companion.virtual.VirtualDeviceManager.LaunchCallback);
method public void removeActivityListener(@NonNull android.companion.virtual.VirtualDeviceManager.ActivityListener);
+ method @NonNull @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE) public void setShowPointerIcon(boolean);
}
public final class VirtualDeviceParams implements android.os.Parcelable {
@@ -2710,8 +2847,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";
@@ -2742,6 +2879,7 @@
field public static final String UI_TRANSLATION_SERVICE = "ui_translation";
field public static final String UWB_SERVICE = "uwb";
field public static final String VR_SERVICE = "vrmanager";
+ field public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE = "wallpaper_effects_generation";
field public static final String WIFI_NL80211_SERVICE = "wifinl80211";
field @Deprecated public static final String WIFI_RTT_SERVICE = "rttmanager";
field public static final String WIFI_SCANNING_SERVICE = "wifiscanner";
@@ -3713,6 +3851,7 @@
method public void sendKeyEvent(int, boolean);
method public void sendVendorCommand(int, byte[], boolean);
method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener);
+ method public void setVendorCommandListener(@NonNull android.hardware.hdmi.HdmiControlManager.VendorCommandListener, int);
}
public static interface HdmiClient.OnDeviceSelectedListener {
@@ -5807,11 +5946,20 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING) public android.media.AudioAttributes.Builder setSystemUsage(int);
}
+ public class AudioDescriptor implements android.os.Parcelable {
+ ctor public AudioDescriptor(int, int, @NonNull byte[]);
+ }
+
public final class AudioDeviceAttributes implements android.os.Parcelable {
ctor public AudioDeviceAttributes(@NonNull android.media.AudioDeviceInfo);
ctor public AudioDeviceAttributes(int, int, @NonNull String);
+ ctor public AudioDeviceAttributes(int, int, @NonNull String, @NonNull String, @NonNull java.util.List<android.media.AudioProfile>, @NonNull java.util.List<android.media.AudioDescriptor>);
method public int describeContents();
+ method public boolean equalTypeAddress(@Nullable Object);
method @NonNull public String getAddress();
+ method @NonNull public java.util.List<android.media.AudioDescriptor> getAudioDescriptors();
+ method @NonNull public java.util.List<android.media.AudioProfile> getAudioProfiles();
+ method @NonNull public String getName();
method public int getRole();
method public int getType();
method public void writeToParcel(@NonNull android.os.Parcel, int);
@@ -5968,6 +6116,10 @@
field public static final int PLAYER_TYPE_UNKNOWN = -1; // 0xffffffff
}
+ public class AudioProfile implements android.os.Parcelable {
+ ctor public AudioProfile(int, @NonNull int[], @NonNull int[], @NonNull int[], int);
+ }
+
public class AudioRecord implements android.media.AudioRecordingMonitor android.media.AudioRouting android.media.MicrophoneDirection {
ctor @RequiresPermission(android.Manifest.permission.RECORD_AUDIO) public AudioRecord(android.media.AudioAttributes, android.media.AudioFormat, int, int) throws java.lang.IllegalArgumentException;
method public static long getMaxSharedAudioHistoryMillis();
@@ -6914,8 +7066,8 @@
method @Nullable public String acquireSharedFilterToken();
method public void close();
method public int configure(@NonNull android.media.tv.tuner.filter.FilterConfiguration);
+ method public int delayCallbackForDurationMillis(long);
method public int delayCallbackUntilBytesAccumulated(int);
- method public int delayCallbackUntilMillisElapsed(long);
method public int flush();
method public void freeSharedFilterToken(@NonNull String);
method @Deprecated public int getId();
@@ -7834,7 +7986,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();
@@ -8206,7 +8358,10 @@
package android.net {
public class EthernetManager {
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void connectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void disconnectNetwork(@NonNull String, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.NETWORK_STACK, android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK}) public android.net.EthernetManager.TetheredInterfaceRequest requestTetheredInterface(@NonNull java.util.concurrent.Executor, @NonNull android.net.EthernetManager.TetheredInterfaceCallback);
+ method @RequiresPermission(anyOf={android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK, android.Manifest.permission.NETWORK_STACK, android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}) public void updateConfiguration(@NonNull String, @NonNull android.net.EthernetNetworkUpdateRequest, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.BiConsumer<android.net.Network,android.net.EthernetNetworkManagementException>);
}
public static interface EthernetManager.TetheredInterfaceCallback {
@@ -8218,21 +8373,20 @@
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 final class EthernetNetworkManagementException extends java.lang.RuntimeException implements android.os.Parcelable {
+ ctor public EthernetNetworkManagementException(@NonNull String);
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkManagementException> CREATOR;
}
- 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 EthernetNetworkUpdateRequest implements android.os.Parcelable {
+ ctor public EthernetNetworkUpdateRequest(@NonNull android.net.StaticIpConfiguration, @NonNull android.net.NetworkCapabilities);
+ method public int describeContents();
+ method @NonNull public android.net.StaticIpConfiguration getIpConfig();
+ method @NonNull public android.net.NetworkCapabilities getNetworkCapabilities();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.net.EthernetNetworkUpdateRequest> CREATOR;
}
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
@@ -8296,48 +8450,6 @@
field public static final String PERMISSION_MAINLINE_NETWORK_STACK = "android.permission.MAINLINE_NETWORK_STACK";
}
- public final class NetworkStats implements java.lang.Iterable<android.net.NetworkStats.Entry> android.os.Parcelable {
- ctor public NetworkStats(long, int);
- method @NonNull public android.net.NetworkStats add(@NonNull android.net.NetworkStats);
- method @NonNull public android.net.NetworkStats addEntry(@NonNull android.net.NetworkStats.Entry);
- method public int describeContents();
- method @NonNull public java.util.Iterator<android.net.NetworkStats.Entry> iterator();
- method @NonNull public android.net.NetworkStats subtract(@NonNull android.net.NetworkStats);
- method public void writeToParcel(@NonNull android.os.Parcel, int);
- field @NonNull public static final android.os.Parcelable.Creator<android.net.NetworkStats> CREATOR;
- field public static final int DEFAULT_NETWORK_ALL = -1; // 0xffffffff
- field public static final int DEFAULT_NETWORK_NO = 0; // 0x0
- field public static final int DEFAULT_NETWORK_YES = 1; // 0x1
- field public static final String IFACE_VT = "vt_data0";
- field public static final int METERED_ALL = -1; // 0xffffffff
- field public static final int METERED_NO = 0; // 0x0
- field public static final int METERED_YES = 1; // 0x1
- field public static final int ROAMING_ALL = -1; // 0xffffffff
- field public static final int ROAMING_NO = 0; // 0x0
- field public static final int ROAMING_YES = 1; // 0x1
- field public static final int SET_ALL = -1; // 0xffffffff
- field public static final int SET_DEFAULT = 0; // 0x0
- field public static final int SET_FOREGROUND = 1; // 0x1
- field public static final int TAG_NONE = 0; // 0x0
- field public static final int UID_ALL = -1; // 0xffffffff
- field public static final int UID_TETHERING = -5; // 0xfffffffb
- }
-
- public static class NetworkStats.Entry {
- ctor public NetworkStats.Entry(@Nullable String, int, int, int, int, int, int, long, long, long, long, long);
- method public int getDefaultNetwork();
- method public int getMetered();
- method public long getOperations();
- method public int getRoaming();
- method public long getRxBytes();
- method public long getRxPackets();
- method public int getSet();
- method public int getTag();
- method public long getTxBytes();
- method public long getTxPackets();
- method public int getUid();
- }
-
@Deprecated public class RssiCurve implements android.os.Parcelable {
ctor @Deprecated public RssiCurve(int, int, byte[]);
ctor @Deprecated public RssiCurve(int, int, byte[], int);
@@ -8369,19 +8481,6 @@
field @Deprecated public final android.net.RssiCurve rssiCurve;
}
- public class TrafficStats {
- method public static void setThreadStatsTagApp();
- method public static void setThreadStatsTagBackup();
- method public static void setThreadStatsTagDownload();
- method public static void setThreadStatsTagRestore();
- field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_END = -113; // 0xffffff8f
- field public static final int TAG_NETWORK_STACK_IMPERSONATION_RANGE_START = -128; // 0xffffff80
- field public static final int TAG_NETWORK_STACK_RANGE_END = -257; // 0xfffffeff
- field public static final int TAG_NETWORK_STACK_RANGE_START = -768; // 0xfffffd00
- field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_END = -241; // 0xffffff0f
- field public static final int TAG_SYSTEM_IMPERSONATION_RANGE_START = -256; // 0xffffff00
- }
-
public abstract class Uri implements java.lang.Comparable<android.net.Uri> android.os.Parcelable {
method @NonNull public String toSafeString();
}
@@ -8562,23 +8661,6 @@
}
-package android.net.netstats.provider {
-
- public abstract class NetworkStatsProvider {
- ctor public NetworkStatsProvider();
- method public void notifyAlertReached();
- method public void notifyLimitReached();
- method public void notifyStatsUpdated(int, @NonNull android.net.NetworkStats, @NonNull android.net.NetworkStats);
- method public void notifyWarningReached();
- method public abstract void onRequestStatsUpdate(int);
- method public abstract void onSetAlert(long);
- method public abstract void onSetLimit(@NonNull String, long);
- method public void onSetWarningAndLimit(@NonNull String, long, long);
- field public static final int QUOTA_UNLIMITED = -1; // 0xffffffff
- }
-
-}
-
package android.net.sip {
@Deprecated public class SipAudioCall {
@@ -9017,6 +9099,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;
}
@@ -9549,7 +9632,7 @@
method @NonNull public java.util.List<android.os.UserHandle> getAllProfiles();
method @NonNull public java.util.List<android.os.UserHandle> getEnabledProfiles();
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public android.os.UserHandle getProfileParent(@NonNull android.os.UserHandle);
- method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String, boolean);
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableProfileCount(@NonNull String);
method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public int getRemainingCreatableUserCount(@NonNull String);
method @Nullable @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.CREATE_USERS, android.Manifest.permission.QUERY_USERS}) public android.os.UserHandle getRestrictedProfileParent();
method @RequiresPermission(android.Manifest.permission.MANAGE_USERS) public String getSeedAccountName();
@@ -9845,7 +9928,8 @@
method @IntRange(from=0) @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public int getRuntimePermissionsVersion();
method @NonNull public java.util.List<android.permission.PermissionManager.SplitPermissionInfo> getSplitPermissions();
method @RequiresPermission(anyOf={android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, android.Manifest.permission.UPGRADE_RUNTIME_PERMISSIONS}) public void setRuntimePermissionsVersion(@IntRange(from=0) int);
- method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
+ method @Deprecated @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, int, int);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void startOneTimePermissionSession(@NonNull String, long, long, int, int);
method @RequiresPermission(android.Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS) public void stopOneTimePermissionSession(@NonNull String);
field @RequiresPermission(android.Manifest.permission.START_REVIEW_PERMISSION_DECISIONS) public static final String ACTION_REVIEW_PERMISSION_DECISIONS = "android.permission.action.REVIEW_PERMISSION_DECISIONS";
field public static final int PERMISSION_GRANTED = 0; // 0x0
@@ -10645,6 +10729,8 @@
method @Nullable public final android.os.IBinder onBind(@NonNull android.content.Intent);
method public abstract void onCancelAttentionCheck(@NonNull android.service.attention.AttentionService.AttentionCallback);
method public abstract void onCheckAttention(@NonNull android.service.attention.AttentionService.AttentionCallback);
+ method public void onStartProximityUpdates(@NonNull android.service.attention.AttentionService.ProximityCallback);
+ method public void onStopProximityUpdates();
field public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6; // 0x6
field public static final int ATTENTION_FAILURE_CANCELLED = 3; // 0x3
field public static final int ATTENTION_FAILURE_PREEMPTED = 4; // 0x4
@@ -10652,6 +10738,7 @@
field public static final int ATTENTION_FAILURE_UNKNOWN = 2; // 0x2
field public static final int ATTENTION_SUCCESS_ABSENT = 0; // 0x0
field public static final int ATTENTION_SUCCESS_PRESENT = 1; // 0x1
+ field public static final double PROXIMITY_UNKNOWN = -1.0;
field public static final String SERVICE_INTERFACE = "android.service.attention.AttentionService";
}
@@ -10660,6 +10747,10 @@
method public void onSuccess(int, long);
}
+ public static final class AttentionService.ProximityCallback {
+ method public void onProximityUpdate(double);
+ }
+
}
package android.service.autofill {
@@ -11010,6 +11101,7 @@
field public static final String EXTRA_RESOLUTION_CONFIRMATION_CODE_RETRIED = "android.service.euicc.extra.RESOLUTION_CONFIRMATION_CODE_RETRIED";
field public static final String EXTRA_RESOLUTION_CONSENT = "android.service.euicc.extra.RESOLUTION_CONSENT";
field public static final String EXTRA_RESOLUTION_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_PORT_INDEX";
+ field public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID = "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID";
field public static final String EXTRA_RESOLUTION_USE_PORT_INDEX = "android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX";
field public static final String EXTRA_RESOLVABLE_ERRORS = "android.service.euicc.extra.RESOLVABLE_ERRORS";
field public static final int RESOLVABLE_ERROR_CONFIRMATION_CODE = 1; // 0x1
@@ -11083,8 +11175,10 @@
method public void onCreate();
method public void onDestroy();
method public void onGameTaskFocusChanged(boolean);
+ method public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean);
method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final boolean restartGame();
method public void setTaskOverlayView(@NonNull android.view.View, @NonNull android.view.ViewGroup.LayoutParams);
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY) public final void startActivityFromGameSessionForResult(@NonNull android.content.Intent, @Nullable android.os.Bundle, @NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSessionActivityCallback);
method public void takeScreenshot(@NonNull java.util.concurrent.Executor, @NonNull android.service.games.GameSession.ScreenshotCallback);
}
@@ -11094,6 +11188,11 @@
field public static final int ERROR_TAKE_SCREENSHOT_INTERNAL_ERROR = 0; // 0x0
}
+ public interface GameSessionActivityCallback {
+ method public void onActivityResult(int, @Nullable android.content.Intent);
+ method public default void onActivityStartFailed(@NonNull Throwable);
+ }
+
public abstract class GameSessionService extends android.app.Service {
ctor public GameSessionService();
method @Nullable public final android.os.IBinder onBind(@Nullable android.content.Intent);
@@ -11512,6 +11611,7 @@
method @Deprecated public final void grantTrust(CharSequence, long, boolean);
method public final void grantTrust(CharSequence, long, int);
method public final void isEscrowTokenActive(long, android.os.UserHandle);
+ method public final void lockUser();
method public final android.os.IBinder onBind(android.content.Intent);
method public boolean onConfigure(java.util.List<android.os.PersistableBundle>);
method public void onDeviceLocked();
@@ -11522,13 +11622,16 @@
method public void onEscrowTokenStateReceived(long, int);
method public void onTrustTimeout();
method public void onUnlockAttempt(boolean);
+ method public void onUserRequestedUnlock();
method public final void removeEscrowToken(long, android.os.UserHandle);
method public final void revokeTrust();
method public final void setManagingTrust(boolean);
method public final void showKeyguardErrorMessage(@NonNull CharSequence);
method public final void unlockUserWithToken(long, byte[], android.os.UserHandle);
field public static final int FLAG_GRANT_TRUST_DISMISS_KEYGUARD = 2; // 0x2
+ field public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 8; // 0x8
field public static final int FLAG_GRANT_TRUST_INITIATED_BY_USER = 1; // 0x1
+ field public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 4; // 0x4
field public static final String SERVICE_INTERFACE = "android.service.trust.TrustAgentService";
field public static final int TOKEN_STATE_ACTIVE = 1; // 0x1
field public static final int TOKEN_STATE_INACTIVE = 0; // 0x0
@@ -11704,6 +11807,17 @@
}
+package android.service.wallpapereffectsgeneration {
+
+ public abstract class WallpaperEffectsGenerationService extends android.app.Service {
+ ctor public WallpaperEffectsGenerationService();
+ method @NonNull public final android.os.IBinder onBind(@NonNull android.content.Intent);
+ method public abstract void onGenerateCinematicEffect(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectRequest);
+ method public final void returnCinematicEffectResponse(@NonNull android.app.wallpapereffectsgeneration.CinematicEffectResponse);
+ }
+
+}
+
package android.service.watchdog {
public abstract class ExplicitHealthCheckService extends android.app.Service {
@@ -12107,6 +12221,7 @@
field public static final int CALL_SOURCE_EMERGENCY_SHORTCUT = 2; // 0x2
field public static final int CALL_SOURCE_UNSPECIFIED = 0; // 0x0
field public static final String EXTRA_CALL_BACK_INTENT = "android.telecom.extra.CALL_BACK_INTENT";
+ field public static final String EXTRA_CALL_HAS_IN_BAND_RINGTONE = "android.telecom.extra.CALL_HAS_IN_BAND_RINGTONE";
field public static final String EXTRA_CALL_SOURCE = "android.telecom.extra.CALL_SOURCE";
field public static final String EXTRA_CALL_TECHNOLOGY_TYPE = "android.telecom.extra.CALL_TECHNOLOGY_TYPE";
field public static final String EXTRA_CLEAR_MISSED_CALLS_INTENT = "android.telecom.extra.CLEAR_MISSED_CALLS_INTENT";
@@ -13050,7 +13165,6 @@
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.service.carrier.CarrierIdentifier> getAllowedCarriers(int);
method @Deprecated @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypes();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypesBitmask();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getAllowedNetworkTypesForReason(int);
method @Nullable @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public android.content.ComponentName getAndUpdateDefaultRespondViaMessageApplication();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallForwarding(int, @NonNull java.util.concurrent.Executor, @NonNull android.telephony.TelephonyManager.CallForwardingInfoCallback);
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public void getCallWaitingStatus(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
@@ -13096,7 +13210,6 @@
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public int getSimCardState(int, int);
method @Nullable @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Locale getSimLocale();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.Collection<android.telephony.UiccSlotMapping> getSimSlotMapping();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method @NonNull @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public java.util.List<android.telephony.RadioAccessSpecifier> getSystemSelectionChannels();
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public java.util.List<android.telephony.TelephonyHistogram> getTelephonyHistograms();
method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public android.telephony.UiccSlotInfo[] getUiccSlotsInfo();
@@ -13151,7 +13264,6 @@
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int sendThermalMitigationRequest(@NonNull android.telephony.ThermalMitigationRequest);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public int setAllowedCarriers(int, java.util.List<android.service.carrier.CarrierIdentifier>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public boolean setAllowedNetworkTypes(long);
- method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setAllowedNetworkTypesForReason(int, long);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallForwarding(@NonNull android.telephony.CallForwardingInfo, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCallWaitingEnabled(boolean, @Nullable java.util.concurrent.Executor, @Nullable java.util.function.Consumer<java.lang.Integer>);
method @Deprecated @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void setCarrierDataEnabled(boolean);
@@ -13201,10 +13313,8 @@
field public static final String ACTION_SIM_APPLICATION_STATE_CHANGED = "android.telephony.action.SIM_APPLICATION_STATE_CHANGED";
field public static final String ACTION_SIM_CARD_STATE_CHANGED = "android.telephony.action.SIM_CARD_STATE_CHANGED";
field public static final String ACTION_SIM_SLOT_STATUS_CHANGED = "android.telephony.action.SIM_SLOT_STATUS_CHANGED";
- field public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2; // 0x2
field public static final int ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G = 3; // 0x3
field public static final int ALLOWED_NETWORK_TYPES_REASON_POWER = 1; // 0x1
- field public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0; // 0x0
field public static final int CALL_WAITING_STATUS_DISABLED = 2; // 0x2
field public static final int CALL_WAITING_STATUS_ENABLED = 1; // 0x1
field public static final int CALL_WAITING_STATUS_FDN_CHECK_FAILURE = 5; // 0x5
@@ -13244,26 +13354,6 @@
field public static final int KEY_TYPE_WLAN = 2; // 0x2
field public static final int MOBILE_DATA_POLICY_DATA_ON_NON_DEFAULT_DURING_VOICE_CALL = 1; // 0x1
field public static final int MOBILE_DATA_POLICY_MMS_ALWAYS_ALLOWED = 2; // 0x2
- field public static final long NETWORK_TYPE_BITMASK_1xRTT = 64L; // 0x40L
- field public static final long NETWORK_TYPE_BITMASK_CDMA = 8L; // 0x8L
- field public static final long NETWORK_TYPE_BITMASK_EDGE = 2L; // 0x2L
- field public static final long NETWORK_TYPE_BITMASK_EHRPD = 8192L; // 0x2000L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_0 = 16L; // 0x10L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_A = 32L; // 0x20L
- field public static final long NETWORK_TYPE_BITMASK_EVDO_B = 2048L; // 0x800L
- field public static final long NETWORK_TYPE_BITMASK_GPRS = 1L; // 0x1L
- field public static final long NETWORK_TYPE_BITMASK_GSM = 32768L; // 0x8000L
- field public static final long NETWORK_TYPE_BITMASK_HSDPA = 128L; // 0x80L
- field public static final long NETWORK_TYPE_BITMASK_HSPA = 512L; // 0x200L
- field public static final long NETWORK_TYPE_BITMASK_HSPAP = 16384L; // 0x4000L
- field public static final long NETWORK_TYPE_BITMASK_HSUPA = 256L; // 0x100L
- field public static final long NETWORK_TYPE_BITMASK_IWLAN = 131072L; // 0x20000L
- field public static final long NETWORK_TYPE_BITMASK_LTE = 4096L; // 0x1000L
- field public static final long NETWORK_TYPE_BITMASK_LTE_CA = 262144L; // 0x40000L
- field public static final long NETWORK_TYPE_BITMASK_NR = 524288L; // 0x80000L
- field public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = 65536L; // 0x10000L
- field public static final long NETWORK_TYPE_BITMASK_UMTS = 4L; // 0x4L
- field public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L; // 0x0L
field public static final int NR_DUAL_CONNECTIVITY_DISABLE = 2; // 0x2
field public static final int NR_DUAL_CONNECTIVITY_DISABLE_IMMEDIATE = 3; // 0x3
field public static final int NR_DUAL_CONNECTIVITY_ENABLE = 1; // 0x1
@@ -14590,6 +14680,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 52a180b..4132c64 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -278,6 +278,7 @@
}
public final class GameManager {
+ method @RequiresPermission(android.Manifest.permission.MANAGE_GAME_MODE) public boolean isAngleEnabled(@NonNull String);
method public void setGameServiceProvider(@Nullable String);
}
@@ -497,6 +498,7 @@
method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS) public java.util.Set<java.lang.String> getPolicyExemptApps();
method public boolean isCurrentInputMethodSetByOwner();
method public boolean isFactoryResetProtectionPolicySupported();
+ method @RequiresPermission(anyOf={android.Manifest.permission.MANAGE_USERS, android.Manifest.permission.INTERACT_ACROSS_USERS}) public boolean isNewUserDisclaimerAcknowledged();
method @RequiresPermission(anyOf={android.Manifest.permission.MARK_DEVICE_ORGANIZATION_OWNED, android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS}, conditional=true) public void markProfileOwnerOnOrganizationOwnedDevice(@NonNull android.content.ComponentName);
method @NonNull public static String operationSafetyReasonToString(int);
method @NonNull public static String operationToString(int);
@@ -509,6 +511,8 @@
field public static final String ACTION_DATA_SHARING_RESTRICTION_APPLIED = "android.app.action.DATA_SHARING_RESTRICTION_APPLIED";
field public static final String ACTION_DEVICE_POLICY_CONSTANTS_CHANGED = "android.app.action.DEVICE_POLICY_CONSTANTS_CHANGED";
field @Deprecated public static final int CODE_SPLIT_SYSTEM_USER_DEVICE_SYSTEM_USER = 14; // 0xe
+ field public static final int DEVICE_OWNER_TYPE_DEFAULT = 0; // 0x0
+ field public static final int DEVICE_OWNER_TYPE_FINANCED = 1; // 0x1
field public static final int OPERATION_CLEAR_APPLICATION_USER_DATA = 23; // 0x17
field public static final int OPERATION_CREATE_AND_MANAGE_USER = 5; // 0x5
field public static final int OPERATION_INSTALL_CA_CERT = 24; // 0x18
@@ -612,10 +616,6 @@
package android.app.usage {
- public class NetworkStatsManager {
- method public void setPollForce(boolean);
- }
-
public class StorageStatsManager {
method public boolean isQuotaSupported(@NonNull java.util.UUID);
method public boolean isReservedSupported(@NonNull java.util.UUID);
@@ -666,6 +666,7 @@
ctor public AttributionSource(int, @Nullable String, @Nullable String);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @NonNull android.os.IBinder);
ctor public AttributionSource(int, @Nullable String, @Nullable String, @Nullable java.util.Set<java.lang.String>, @Nullable android.content.AttributionSource);
+ method public void enforceCallingPid();
}
public final class AutofillOptions implements android.os.Parcelable {
@@ -800,6 +801,7 @@
method @NonNull public String getPermissionControllerPackageName();
method @NonNull public abstract String getServicesSystemSharedLibraryPackageName();
method @NonNull public abstract String getSharedSystemSharedLibraryPackageName();
+ method @NonNull public String getSupplementalProcessPackageName();
method @Nullable public String getSystemTextClassifierPackageName();
method @Nullable public String getWellbeingPackageName();
method public void holdLock(android.os.IBinder, int);
@@ -982,7 +984,7 @@
package android.graphics {
public final class ImageDecoder implements java.lang.AutoCloseable {
- method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, java.io.InputStream, int);
+ method @AnyThread @NonNull public static android.graphics.ImageDecoder.Source createSource(android.content.res.Resources, @NonNull java.io.InputStream, int);
}
public final class Rect implements android.os.Parcelable {
@@ -1604,10 +1606,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);
@@ -1624,13 +1622,6 @@
method @Nullable public byte[] getWatchlistConfigHash();
}
- public class TrafficStats {
- method public static long getLoopbackRxBytes();
- method public static long getLoopbackRxPackets();
- method public static long getLoopbackTxBytes();
- method public static long getLoopbackTxPackets();
- }
-
}
package android.os {
@@ -1738,8 +1729,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 {
@@ -2516,7 +2510,6 @@
method @NonNull public java.util.List<android.telephony.data.ApnSetting> getDevicePolicyOverrideApns(@NonNull android.content.Context);
method @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE) public String getLine1AlphaTag();
method public android.util.Pair<java.lang.Integer,java.lang.Integer> getRadioHalVersion();
- method @RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE) public long getSupportedRadioAccessFamily();
method public boolean modifyDevicePolicyOverrideApn(@NonNull android.content.Context, int, @NonNull android.telephony.data.ApnSetting);
method @RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE) public void refreshUiccProfile();
method @Deprecated public void setCarrierTestOverride(String, String, String, String, String, String, String);
@@ -2738,6 +2731,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);
@@ -2797,6 +2791,10 @@
field public static final int FLAG_IS_ACCESSIBILITY_EVENT = 2048; // 0x800
}
+ public interface OnBackInvokedDispatcher {
+ field public static final long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L; // 0xbade858L
+ }
+
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME) @java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD}) public @interface RemotableViewMethod {
method public abstract String asyncImpl() default "";
}
@@ -2813,7 +2811,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.bp b/core/java/Android.bp
index 5649c5f..a5526bc 100644
--- a/core/java/Android.bp
+++ b/core/java/Android.bp
@@ -160,7 +160,6 @@
filegroup {
name: "framework-services-net-module-wifi-shared-srcs",
srcs: [
- "android/net/DhcpResults.java",
"android/util/LocalLog.java",
],
}
@@ -176,6 +175,18 @@
"com/android/internal/util/IndentingPrintWriter.java",
"com/android/internal/util/MessageUtils.java",
"com/android/internal/util/WakeupMessage.java",
+ // TODO: delete as soon as NetworkStatsFactory stops using
+ "com/android/internal/util/ProcFileReader.java",
+ ],
+}
+
+// keep these files in sync with the packages/modules/Connectivity jarjar-rules.txt for
+// the connectivity module.
+filegroup {
+ name: "framework-connectivity-api-shared-srcs",
+ srcs: [
+ "android/util/IndentingPrintWriter.java",
+ "com/android/internal/util/FileRotator.java",
],
}
diff --git a/core/java/android/accessibilityservice/AccessibilityService.java b/core/java/android/accessibilityservice/AccessibilityService.java
index 6b0aef8..50473f1 100644
--- a/core/java/android/accessibilityservice/AccessibilityService.java
+++ b/core/java/android/accessibilityservice/AccessibilityService.java
@@ -571,6 +571,31 @@
*/
public static final int GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE = 15;
+ /**
+ * Action to trigger dpad up keyevent.
+ */
+ public static final int GLOBAL_ACTION_DPAD_UP = 16;
+
+ /**
+ * Action to trigger dpad down keyevent.
+ */
+ public static final int GLOBAL_ACTION_DPAD_DOWN = 17;
+
+ /**
+ * Action to trigger dpad left keyevent.
+ */
+ public static final int GLOBAL_ACTION_DPAD_LEFT = 18;
+
+ /**
+ * Action to trigger dpad right keyevent.
+ */
+ public static final int GLOBAL_ACTION_DPAD_RIGHT = 19;
+
+ /**
+ * Action to trigger dpad center keyevent.
+ */
+ public static final int GLOBAL_ACTION_DPAD_CENTER = 20;
+
private static final String LOG_TAG = "AccessibilityService";
/**
@@ -2328,10 +2353,16 @@
* @param action The action to perform.
* @return Whether the action was successfully performed.
*
+ * Perform actions using ids like the id constants referenced below:
* @see #GLOBAL_ACTION_BACK
* @see #GLOBAL_ACTION_HOME
* @see #GLOBAL_ACTION_NOTIFICATIONS
* @see #GLOBAL_ACTION_RECENTS
+ * @see #GLOBAL_ACTION_DPAD_UP
+ * @see #GLOBAL_ACTION_DPAD_DOWN
+ * @see #GLOBAL_ACTION_DPAD_LEFT
+ * @see #GLOBAL_ACTION_DPAD_RIGHT
+ * @see #GLOBAL_ACTION_DPAD_CENTER
*/
public final boolean performGlobalAction(int action) {
IAccessibilityServiceConnection connection =
@@ -3089,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/animation/Animator.java b/core/java/android/animation/Animator.java
index eb525d3..a8ff36a 100644
--- a/core/java/android/animation/Animator.java
+++ b/core/java/android/animation/Animator.java
@@ -16,6 +16,7 @@
package android.animation;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo.Config;
@@ -535,7 +536,7 @@
* @param animation The started animation.
* @param isReverse Whether the animation is playing in reverse.
*/
- default void onAnimationStart(Animator animation, boolean isReverse) {
+ default void onAnimationStart(@NonNull Animator animation, boolean isReverse) {
onAnimationStart(animation);
}
@@ -551,7 +552,7 @@
* @param animation The animation which reached its end.
* @param isReverse Whether the animation is playing in reverse.
*/
- default void onAnimationEnd(Animator animation, boolean isReverse) {
+ default void onAnimationEnd(@NonNull Animator animation, boolean isReverse) {
onAnimationEnd(animation);
}
@@ -560,7 +561,7 @@
*
* @param animation The started animation.
*/
- void onAnimationStart(Animator animation);
+ void onAnimationStart(@NonNull Animator animation);
/**
* <p>Notifies the end of the animation. This callback is not invoked
@@ -568,7 +569,7 @@
*
* @param animation The animation which reached its end.
*/
- void onAnimationEnd(Animator animation);
+ void onAnimationEnd(@NonNull Animator animation);
/**
* <p>Notifies the cancellation of the animation. This callback is not invoked
@@ -576,14 +577,14 @@
*
* @param animation The animation which was canceled.
*/
- void onAnimationCancel(Animator animation);
+ void onAnimationCancel(@NonNull Animator animation);
/**
* <p>Notifies the repetition of the animation.</p>
*
* @param animation The animation which was repeated.
*/
- void onAnimationRepeat(Animator animation);
+ void onAnimationRepeat(@NonNull Animator animation);
}
/**
@@ -599,7 +600,7 @@
* @param animation The animaton being paused.
* @see #pause()
*/
- void onAnimationPause(Animator animation);
+ void onAnimationPause(@NonNull Animator animation);
/**
* <p>Notifies that the animation was resumed, after being
@@ -608,7 +609,7 @@
* @param animation The animation being resumed.
* @see #resume()
*/
- void onAnimationResume(Animator animation);
+ void onAnimationResume(@NonNull Animator animation);
}
/**
diff --git a/core/java/android/animation/ValueAnimator.java b/core/java/android/animation/ValueAnimator.java
index 3cbae99..06b424b 100644
--- a/core/java/android/animation/ValueAnimator.java
+++ b/core/java/android/animation/ValueAnimator.java
@@ -18,6 +18,7 @@
import android.annotation.CallSuper;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
@@ -1626,7 +1627,7 @@
*
* @param animation The animation which was repeated.
*/
- void onAnimationUpdate(ValueAnimator animation);
+ void onAnimationUpdate(@NonNull ValueAnimator animation);
}
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 530666b..e022700 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -115,6 +115,7 @@
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedCallback;
import android.view.OnBackInvokedDispatcher;
import android.view.OnBackInvokedDispatcherOwner;
import android.view.RemoteAnimationDefinition;
@@ -144,6 +145,7 @@
import android.widget.Toast;
import android.widget.Toolbar;
import android.window.SplashScreen;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -791,6 +793,7 @@
private static final int LOG_AM_ON_ACTIVITY_RESULT_CALLED = 30062;
private static final int LOG_AM_ON_TOP_RESUMED_GAINED_CALLED = 30064;
private static final int LOG_AM_ON_TOP_RESUMED_LOST_CALLED = 30065;
+ private OnBackInvokedCallback mDefaultBackCallback;
/**
* After {@link Build.VERSION_CODES#TIRAMISU},
@@ -1617,7 +1620,16 @@
}
mRestoredFromBundle = savedInstanceState != null;
mCalled = true;
-
+ if (!WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Add onBackPressed as default back behavior.
+ mDefaultBackCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ navigateBack();
+ }
+ };
+ getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -2653,6 +2665,10 @@
if (mUiTranslationController != null) {
mUiTranslationController.onActivityDestroyed();
}
+
+ if (mDefaultBackCallback != null) {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -3773,10 +3789,13 @@
* @see KeyEvent
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (getApplicationInfo().targetSdkVersion
- >= Build.VERSION_CODES.ECLAIR) {
- if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
- && !event.isCanceled()) {
+ int sdkVersion = getApplicationInfo().targetSdkVersion;
+ if (sdkVersion >= Build.VERSION_CODES.ECLAIR) {
+ if (keyCode == KeyEvent.KEYCODE_BACK
+ && event.isTracking()
+ && !event.isCanceled()
+ && mDefaultBackCallback == null) {
+ // Using legacy back handling.
onBackPressed();
return true;
}
@@ -3841,6 +3860,10 @@
if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) {
return;
}
+ navigateBack();
+ }
+
+ private void navigateBack() {
if (!isTaskRoot()) {
// If the activity is not the root of the task, allow finish to proceed normally.
finishAfterTransition();
@@ -5503,6 +5526,17 @@
*/
public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
IBinder permissionToken, boolean ignoreTargetSecurity, int userId) {
+ startActivityAsCaller(intent, options, permissionToken, ignoreTargetSecurity, userId, -1);
+ }
+
+ /**
+ * @see #startActivityAsCaller(Intent, Bundle, IBinder, boolean, int)
+ * @param requestCode The request code used for returning a result or -1 if no result should be
+ * returned.
+ * @hide
+ */
+ public void startActivityAsCaller(Intent intent, @Nullable Bundle options,
+ IBinder permissionToken, boolean ignoreTargetSecurity, int userId, int requestCode) {
if (mParent != null) {
throw new RuntimeException("Can't be called from a child");
}
@@ -5510,11 +5544,11 @@
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivityAsCaller(
this, mMainThread.getApplicationThread(), mToken, this,
- intent, -1, options, permissionToken, ignoreTargetSecurity, userId);
+ intent, requestCode, options, permissionToken, ignoreTargetSecurity,
+ userId);
if (ar != null) {
mMainThread.sendActivityResult(
- mToken, mEmbeddedID, -1, ar.getResultCode(),
- ar.getResultData());
+ mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}
@@ -6139,6 +6173,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
@@ -7171,7 +7209,7 @@
// Handle special cases
switch (args[0]) {
case "--autofill":
- getAutofillClientController().dumpAutofillManager(prefix, writer);
+ dumpAutofillManager(prefix, writer, args);
return;
case "--contentcapture":
dumpContentCaptureManager(prefix, writer);
@@ -7239,10 +7277,6 @@
mHandler.getLooper().dump(new PrintWriterPrinter(writer), prefix);
- getAutofillClientController().dumpAutofillManager(prefix, writer);
- dumpContentCaptureManager(prefix, writer);
- dumpUiTranslation(prefix, writer);
-
ResourcesManager.getInstance().dump(prefix, writer);
if (mDumpableContainer != null) {
@@ -7250,21 +7284,26 @@
}
}
- void dumpContentCaptureManager(String prefix, PrintWriter writer) {
- final ContentCaptureManager cm = getContentCaptureManager();
- if (cm != null) {
- cm.dump(prefix, writer);
- } else {
- writer.print(prefix); writer.println("No ContentCaptureManager");
- }
+ private void dumpContentCaptureManager(String prefix, PrintWriter writer) {
+ getContentCaptureManager();
+ dumpLegacyDumpable(prefix, writer, ContentCaptureManager.DUMPABLE_NAME, /* args= */ null);
}
- void dumpUiTranslation(String prefix, PrintWriter writer) {
- if (mUiTranslationController != null) {
- mUiTranslationController.dump(prefix, writer);
- } else {
- writer.print(prefix); writer.println("No UiTranslationController");
+ private void dumpUiTranslation(String prefix, PrintWriter writer) {
+ dumpLegacyDumpable(prefix, writer, UiTranslationController.DUMPABLE_NAME, /* args= */ null);
+ }
+
+ private void dumpAutofillManager(String prefix, PrintWriter writer, String[] args) {
+ dumpLegacyDumpable(prefix, writer, AutofillClientController.DUMPABLE_NAME, args);
+ }
+
+ private void dumpLegacyDumpable(@NonNull String prefix, @NonNull PrintWriter writer,
+ @NonNull String dumpableName, @Nullable String[] args) {
+ if (mDumpableContainer == null) {
+ writer.print(prefix); writer.print("no "); writer.println(dumpableName);
+ return;
}
+ mDumpableContainer.dumpOneDumpable(prefix, writer, dumpableName, args);
}
/**
@@ -8742,17 +8781,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/AppOpsManager.java b/core/java/android/app/AppOpsManager.java
index fdf37f6..0d1bc05 100644
--- a/core/java/android/app/AppOpsManager.java
+++ b/core/java/android/app/AppOpsManager.java
@@ -2363,11 +2363,11 @@
Manifest.permission.USE_BIOMETRIC,
Manifest.permission.ACTIVITY_RECOGNITION,
Manifest.permission.SMS_FINANCIAL_TRANSACTIONS,
- null,
+ Manifest.permission.READ_MEDIA_AUDIO,
null, // no permission for OP_WRITE_MEDIA_AUDIO
- null,
+ Manifest.permission.READ_MEDIA_VIDEO,
null, // no permission for OP_WRITE_MEDIA_VIDEO
- null,
+ Manifest.permission.READ_MEDIA_IMAGE,
null, // no permission for OP_WRITE_MEDIA_IMAGES
null, // no permission for OP_LEGACY_STORAGE
null, // no permission for OP_ACCESS_ACCESSIBILITY
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/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index b1956ef..20ffa25 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -863,6 +863,18 @@
}
}
+ /**
+ * @hide
+ */
+ @Override
+ public String getSupplementalProcessPackageName() {
+ try {
+ return mPM.getSupplementalProcessPackageName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
@Override
public boolean addPermission(PermissionInfo info) {
return getPermissionManager().addPermission(info, false);
diff --git a/core/java/android/app/Dialog.java b/core/java/android/app/Dialog.java
index a7fb83b..2084775 100644
--- a/core/java/android/app/Dialog.java
+++ b/core/java/android/app/Dialog.java
@@ -52,6 +52,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.OnBackInvokedCallback;
import android.view.OnBackInvokedDispatcher;
import android.view.OnBackInvokedDispatcherOwner;
import android.view.SearchEvent;
@@ -62,6 +63,7 @@
import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
+import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.app.WindowDecorActionBar;
@@ -156,6 +158,7 @@
/** A {@link Runnable} to run instead of dismissing when {@link #dismiss()} is called. */
private Runnable mDismissOverride;
+ private OnBackInvokedCallback mDefaultBackCallback;
/**
* Creates a dialog window that uses the default dialog theme.
@@ -453,6 +456,16 @@
*/
protected void onStart() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
+ if (mContext != null && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Add onBackPressed as default back behavior.
+ mDefaultBackCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ onBackPressed();
+ }
+ };
+ getOnBackInvokedDispatcher().registerSystemOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
/**
@@ -460,6 +473,9 @@
*/
protected void onStop() {
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
+ if (mDefaultBackCallback != null) {
+ getOnBackInvokedDispatcher().unregisterOnBackInvokedCallback(mDefaultBackCallback);
+ }
}
private static final String DIALOG_SHOWING_TAG = "android:dialogShowing";
@@ -685,7 +701,8 @@
public boolean onKeyUp(int keyCode, @NonNull KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_ESCAPE)
&& event.isTracking()
- && !event.isCanceled()) {
+ && !event.isCanceled()
+ && WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
onBackPressed();
return true;
}
@@ -1449,15 +1466,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/GameManager.java b/core/java/android/app/GameManager.java
index 289b348..040399e 100644
--- a/core/java/android/app/GameManager.java
+++ b/core/java/android/app/GameManager.java
@@ -181,14 +181,18 @@
/**
* Returns if ANGLE is enabled for a given package and user ID.
* <p>
+ * ANGLE (Almost Native Graphics Layer Engine) can translate OpenGL ES commands to Vulkan
+ * commands. Enabling ANGLE may improve the performance and/or reduce the power consumption of
+ * applications.
* The caller must have {@link android.Manifest.permission#MANAGE_GAME_MODE}.
*
* @hide
*/
+ @TestApi
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
- public @GameMode boolean getAngleEnabled(@NonNull String packageName) {
+ public @GameMode boolean isAngleEnabled(@NonNull String packageName) {
try {
- return mService.getAngleEnabled(packageName, mContext.getUserId());
+ return mService.isAngleEnabled(packageName, mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
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/IActivityTaskManager.aidl b/core/java/android/app/IActivityTaskManager.aidl
index 0801b24..c5add66 100644
--- a/core/java/android/app/IActivityTaskManager.aidl
+++ b/core/java/android/app/IActivityTaskManager.aidl
@@ -130,6 +130,9 @@
in ProfilerInfo profilerInfo, in Bundle options, int userId);
int startAssistantActivity(in String callingPackage, in String callingFeatureId, int callingPid,
int callingUid, in Intent intent, in String resolvedType, in Bundle options, int userId);
+ int startActivityFromGameSession(IApplicationThread caller, in String callingPackage,
+ in String callingFeatureId, int callingPid, int callingUid, in Intent intent,
+ int taskId, int userId);
void startRecentsActivity(in Intent intent, in long eventTime,
in IRecentsAnimationRunner recentsAnimationRunner);
int startActivityFromRecents(int taskId, in Bundle options);
diff --git a/core/java/android/app/IGameManagerService.aidl b/core/java/android/app/IGameManagerService.aidl
index 3ea07676..7035ac0 100644
--- a/core/java/android/app/IGameManagerService.aidl
+++ b/core/java/android/app/IGameManagerService.aidl
@@ -26,7 +26,7 @@
int getGameMode(String packageName, int userId);
void setGameMode(String packageName, int gameMode, int userId);
int[] getAvailableGameModes(String packageName);
- boolean getAngleEnabled(String packageName, int userId);
+ boolean isAngleEnabled(String packageName, int userId);
void setGameState(String packageName, in GameState gameState, int userId);
GameModeInfo getGameModeInfo(String packageName, int userId);
void setGameServiceProvider(String packageName);
diff --git a/core/java/android/app/Instrumentation.java b/core/java/android/app/Instrumentation.java
index eb4585d..a74438a 100644
--- a/core/java/android/app/Instrumentation.java
+++ b/core/java/android/app/Instrumentation.java
@@ -376,14 +376,16 @@
Debug.stopMethodTracing();
}
}
-
+
/**
- * Force the global system in or out of touch mode. This can be used if
- * your instrumentation relies on the UI being in one more or the other
- * when it starts.
- *
- * @param inTouch Set to true to be in touch mode, false to be in
- * focus mode.
+ * Force the global system in or out of touch mode. This can be used if your
+ * instrumentation relies on the UI being in one more or the other when it starts.
+ *
+ * <p><b>Note:</b> Starting from Android {@link Build.VERSION_CODES#TIRAMISU}, this method
+ * will only have an effect if the calling process is also the focused window owner or has
+ * {@link android.permission#MODIFY_TOUCH_MODE_STATE} permission granted.
+ *
+ * @param inTouch Set to true to be in touch mode, false to be in focus mode.
*/
public void setInTouchMode(boolean inTouch) {
try {
@@ -393,11 +395,11 @@
// Shouldn't happen!
}
}
-
+
/**
* Schedule a callback for when the application's main thread goes idle
* (has no more events to process).
- *
+ *
* @param recipient Called the next time the thread's message queue is
* idle.
*/
diff --git a/core/java/android/app/StatusBarManager.java b/core/java/android/app/StatusBarManager.java
index 56c301f..da1ba52 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;
@@ -177,6 +180,8 @@
public static final int NAVIGATION_HINT_BACK_ALT = 1 << 0;
/** @hide */
public static final int NAVIGATION_HINT_IME_SHOWN = 1 << 1;
+ /** @hide */
+ public static final int NAVIGATION_HINT_IME_SWITCHER_SHOWN = 1 << 2;
/** @hide */
public static final int WINDOW_STATUS_BAR = 1;
@@ -338,6 +343,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 +954,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 +1311,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 bbdd705..f5f2fe0 100644
--- a/core/java/android/app/SystemServiceRegistry.java
+++ b/core/java/android/app/SystemServiceRegistry.java
@@ -48,9 +48,10 @@
import android.app.trust.TrustManager;
import android.app.usage.IStorageStatsManager;
import android.app.usage.IUsageStatsManager;
-import android.app.usage.NetworkStatsManager;
import android.app.usage.StorageStatsManager;
import android.app.usage.UsageStatsManager;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager;
import android.apphibernation.AppHibernationManager;
import android.appwidget.AppWidgetManager;
import android.bluetooth.BluetoothFrameworkInitializer;
@@ -137,12 +138,9 @@
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;
@@ -439,15 +437,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
@@ -1022,17 +1011,6 @@
return new UsageStatsManager(ctx.getOuterContext(), service);
}});
- registerService(Context.NETWORK_STATS_SERVICE, NetworkStatsManager.class,
- new CachedServiceFetcher<NetworkStatsManager>() {
- @Override
- public NetworkStatsManager createService(ContextImpl ctx) throws ServiceNotFoundException {
- // TODO: Replace with an initializer in the module, see
- // {@code ConnectivityFrameworkInitializer}.
- final INetworkStatsService service = INetworkStatsService.Stub.asInterface(
- ServiceManager.getServiceOrThrow(Context.NETWORK_STATS_SERVICE));
- return new NetworkStatsManager(ctx.getOuterContext(), service);
- }});
-
registerService(Context.PERSISTENT_DATA_BLOCK_SERVICE, PersistentDataBlockManager.class,
new StaticServiceFetcher<PersistentDataBlockManager>() {
@Override
@@ -1297,6 +1275,20 @@
}
});
+ registerService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE,
+ WallpaperEffectsGenerationManager.class,
+ new CachedServiceFetcher<WallpaperEffectsGenerationManager>() {
+ @Override
+ public WallpaperEffectsGenerationManager createService(ContextImpl ctx)
+ throws ServiceNotFoundException {
+ IBinder b = ServiceManager.getService(
+ Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
+ return b == null ? null :
+ new WallpaperEffectsGenerationManager(
+ IWallpaperEffectsGenerationManager.Stub.asInterface(b));
+ }
+ });
+
registerService(Context.VR_SERVICE, VrManager.class, new CachedServiceFetcher<VrManager>() {
@Override
public VrManager createService(ContextImpl ctx) throws ServiceNotFoundException {
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 a4227a4..108412d 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -56,9 +56,11 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.net.PrivateDnsConnectivityChecker;
import android.net.ProxyInfo;
import android.net.Uri;
+import android.net.wifi.WifiSsid;
import android.nfc.NfcAdapter;
import android.os.Binder;
import android.os.Build;
@@ -111,6 +113,7 @@
import java.lang.annotation.RetentionPolicy;
import java.net.InetSocketAddress;
import java.net.Proxy;
+import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.NoSuchAlgorithmException;
@@ -463,7 +466,9 @@
* <li>{@link #setUserControlDisabledPackages(ComponentName, List)}</li>
* <li>{@link #getUserControlDisabledPackages(ComponentName)}</li>
* <li>{@link #setOrganizationName(ComponentName, CharSequence)}</li>
+ * <li>{@link #getOrganizationName(ComponentName)} </li>
* <li>{@link #setShortSupportMessage(ComponentName, CharSequence)}</li>
+ * <li>{@link #getShortSupportMessage(ComponentName)}</li>
* <li>{@link #isBackupServiceEnabled(ComponentName)}</li>
* <li>{@link #setBackupServiceEnabled(ComponentName, boolean)}</li>
* <li>{@link #isLockTaskPermitted(String)}</li>
@@ -476,7 +481,9 @@
* <li>{@link #LOCK_TASK_FEATURE_GLOBAL_ACTIONS}</li>
* <li>{@link #LOCK_TASK_FEATURE_NOTIFICATIONS}</li>
* </ul>
+ * <li>{@link #getLockTaskFeatures(ComponentName)}</li>
* <li>{@link #setLockTaskPackages(ComponentName, String[])}</li>
+ * <li>{@link #getLockTaskPackages(ComponentName)}</li>
* <li>{@link #addPersistentPreferredActivity(ComponentName, IntentFilter, ComponentName)}</li>
* <li>{@link #clearPackagePersistentPreferredActivities(ComponentName, String)} </li>
* <li>{@link #wipeData(int)}</li>
@@ -487,6 +494,10 @@
* {@link #PERMISSION_GRANT_STATE_GRANTED}, {@link #PERMISSION_GRANT_STATE_DENIED}, or
* {@link #PERMISSION_GRANT_STATE_DEFAULT} and can <b>only</b> be applied to the device admin
* app (otherwise a {@link SecurityException} will be thrown)</li>
+ * <li>{@link #getPermissionGrantState(ComponentName, String, String)}, where
+ * {@link permission#READ_PHONE_STATE} is the <b>only</b> permission that can be
+ * used and device admin app is the only package that can be used to retrieve the permission
+ * permission grant state for (otherwise a {@link SecurityException} will be thrown)</li>
* <li>{@link #addUserRestriction(ComponentName, String)}, where the following user restrictions
* are permitted (otherwise a {@link SecurityException} will be thrown):</li>
* <ul>
@@ -497,7 +508,17 @@
* <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li>
* <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li>
* </ul>
- * <li>{@link #clearUserRestriction(ComponentName, String)}</li>
+ * <li>{@link #getUserRestrictions(ComponentName)}</li>
+ * <li>{@link #clearUserRestriction(ComponentName, String)}, where the following user
+ * restrictions are permitted (otherwise a {@link SecurityException} will be thrown):</li>
+ * <ul>
+ * <li>{@link UserManager#DISALLOW_ADD_USER}</li>
+ * <li>{@link UserManager#DISALLOW_DEBUGGING_FEATURES}</li>
+ * <li>{@link UserManager#DISALLOW_INSTALL_UNKNOWN_SOURCES}</li>
+ * <li>{@link UserManager#DISALLOW_SAFE_BOOT}</li>
+ * <li>{@link UserManager#DISALLOW_CONFIG_DATE_TIME}</li>
+ * <li>{@link UserManager#DISALLOW_OUTGOING_CALLS}</li>
+ * </ul>
* </ul>
*
* @hide
@@ -1330,7 +1351,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";
@@ -3254,6 +3278,7 @@
*
* @hide
*/
+ @TestApi
public static final int DEVICE_OWNER_TYPE_DEFAULT = 0;
/**
@@ -3261,6 +3286,7 @@
*
* @hide
*/
+ @TestApi
public static final int DEVICE_OWNER_TYPE_FINANCED = 1;
/**
@@ -3556,7 +3582,8 @@
*
* @hide
*/
- @RequiresPermission(android.Manifest.permission.MANAGE_USERS)
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public void acknowledgeNewUserDisclaimer() {
if (mService != null) {
@@ -3569,6 +3596,25 @@
}
/**
+ * Checks whether the new managed user disclaimer was viewed by the current user.
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
+ @TestApi
+ public boolean isNewUserDisclaimerAcknowledged() {
+ if (mService != null) {
+ try {
+ return mService.isNewUserDisclaimerAcknowledged();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
* Return true if the given administrator component is currently active (enabled) in the system.
*
* @param admin The administrator component to check for.
@@ -10113,7 +10159,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
@@ -10141,12 +10189,17 @@
}
/**
- * 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
*/
@RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS,
- android.Manifest.permission.CREATE_USERS})
+ android.Manifest.permission.INTERACT_ACROSS_USERS})
@SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
public @UserOperationResult int logoutUser() {
// TODO(b/214336184): add CTS test
@@ -10158,7 +10211,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
*/
@@ -14876,10 +14932,14 @@
mService.setSsidAllowlist(new ArrayList<>());
} else {
int policyType = policy.getPolicyType();
+ List<String> ssidList = new ArrayList<>();
+ for (WifiSsid ssid : policy.getSsids()) {
+ ssidList.add(new String(ssid.getBytes(), StandardCharsets.UTF_8));
+ }
if (policyType == WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST) {
- mService.setSsidAllowlist(new ArrayList<>(policy.getSsids()));
+ mService.setSsidAllowlist(ssidList);
} else {
- mService.setSsidDenylist(new ArrayList<>(policy.getSsids()));
+ mService.setSsidDenylist(ssidList);
}
}
} catch (RemoteException e) {
@@ -14905,11 +14965,23 @@
try {
List<String> allowlist = mService.getSsidAllowlist();
if (!allowlist.isEmpty()) {
- return WifiSsidPolicy.createAllowlistPolicy(new ArraySet<>(allowlist));
+ List<WifiSsid> wifiSsidAllowlist = new ArrayList<>();
+ for (String ssid : allowlist) {
+ wifiSsidAllowlist.add(
+ WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8)));
+ }
+ return new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST,
+ new ArraySet<>(wifiSsidAllowlist));
}
List<String> denylist = mService.getSsidDenylist();
if (!denylist.isEmpty()) {
- return WifiSsidPolicy.createDenylistPolicy(new ArraySet<>(denylist));
+ List<WifiSsid> wifiSsidDenylist = new ArrayList<>();
+ for (String ssid : denylist) {
+ wifiSsidDenylist.add(
+ WifiSsid.fromBytes(ssid.getBytes(StandardCharsets.UTF_8)));
+ }
+ return new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST,
+ new ArraySet<>(wifiSsidDenylist));
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -15173,6 +15245,66 @@
}
/**
+ * Similar to {@link #getDrawable(String, String, String, Callable)} but returns an
+ * {@link Icon} instead of a {@link Drawable}.
+ *
+ * @param drawableId The drawable ID to get the updated resource for.
+ * @param drawableStyle The drawable style to use.
+ * @param drawableSource The source for the caller.
+ * @param defaultIcon Returned if no updated drawable was set for the provided params.
+ */
+ @Nullable
+ public Icon getDrawableAsIcon(
+ @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+ @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+ @NonNull @DevicePolicyResources.UpdatableDrawableSource String drawableSource,
+ @Nullable Icon defaultIcon) {
+ Objects.requireNonNull(drawableId, "drawableId can't be null");
+ Objects.requireNonNull(drawableStyle, "drawableStyle can't be null");
+ Objects.requireNonNull(drawableSource, "drawableSource can't be null");
+ Objects.requireNonNull(defaultIcon, "defaultIcon can't be null");
+
+ if (Drawables.UNDEFINED.equals(drawableId)) {
+ return defaultIcon;
+ }
+ if (mService != null) {
+ try {
+ ParcelableResource resource = mService.getDrawable(
+ drawableId, drawableStyle, drawableSource);
+ if (resource == null) {
+ return defaultIcon;
+ }
+ return Icon.createWithResource(resource.getPackageName(), resource.getResourceId());
+ } catch (RemoteException e) {
+ Log.e(
+ TAG,
+ "Error getting the updated drawable from DevicePolicyManagerService.",
+ e);
+ return defaultIcon;
+ }
+ }
+ return defaultIcon;
+ }
+
+ /**
+ * Similar to {@link #getDrawable(String, String, Callable)} but returns an {@link Icon}
+ * instead of a {@link Drawable}.
+ *
+ * @param drawableId The drawable ID to get the updated resource for.
+ * @param drawableStyle The drawable style to use.
+ * @param defaultIcon Returned if no updated drawable was set for the provided params.
+ */
+ @Nullable
+ public Icon getDrawableAsIcon(
+ @NonNull @DevicePolicyResources.UpdatableDrawableId String drawableId,
+ @NonNull @DevicePolicyResources.UpdatableDrawableStyle String drawableStyle,
+ @Nullable Icon defaultIcon) {
+ return getDrawableAsIcon(
+ drawableId, drawableStyle, Drawables.Source.UNDEFINED, defaultIcon);
+ }
+
+
+ /**
* For each {@link DevicePolicyStringResource} item in {@code strings}, it updates the string
* resource for {@link DevicePolicyStringResource#getStringId()} to the string with ID
* {@code callingPackageResourceId} (see {@link DevicePolicyResources.Strings}), meaning any
@@ -15334,4 +15466,45 @@
}
return ParcelableResource.loadDefaultString(defaultStringLoader);
}
+
+ /**
+ * Returns a boolean for whether the DPC has been downloaded during provisioning.
+ *
+ * <p>If true is returned, then any attempts to begin setup again should result in factory reset
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public boolean isDpcDownloaded() {
+ throwIfParentInstance("isDpcDownloaded");
+ if (mService != null) {
+ try {
+ return mService.isDpcDownloaded();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Use to indicate that the DPC has or has not been downloaded during provisioning.
+ *
+ * @param downloaded {@code true} if the dpc has been downloaded during provisioning. false otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS)
+ public void setDpcDownloaded(boolean downloaded) {
+ throwIfParentInstance("setDpcDownloaded");
+ if (mService != null) {
+ try {
+ mService.setDpcDownloaded(downloaded);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/app/admin/DevicePolicyResources.java b/core/java/android/app/admin/DevicePolicyResources.java
index ac39cb4..7f2e5fd 100644
--- a/core/java/android/app/admin/DevicePolicyResources.java
+++ b/core/java/android/app/admin/DevicePolicyResources.java
@@ -58,6 +58,10 @@
import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_GENERIC_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_ORG_OWNED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.WORK_PROFILE_DELETED_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_INCOMING_WORK_CALL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_MISSED_WORK_CALL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_ONGOING_WORK_CALL_TITLE;
+import static android.app.admin.DevicePolicyResources.Strings.Dialer.NOTIFICATION_WIFI_WORK_CALL_LABEL;
import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_PERSONAL_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.DocumentsUi.CANT_SAVE_TO_WORK_MESSAGE;
@@ -512,7 +516,11 @@
WORK_PROFILE_DEFAULT_APPS_TITLE, HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE,
BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE, BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE,
BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE, FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE,
- LOCATION_AUTO_GRANTED_MESSAGE
+ LOCATION_AUTO_GRANTED_MESSAGE,
+
+ // Dialer Strings
+ NOTIFICATION_INCOMING_WORK_CALL_TITLE, NOTIFICATION_ONGOING_WORK_CALL_TITLE,
+ NOTIFICATION_MISSED_WORK_CALL_TITLE, NOTIFICATION_WIFI_WORK_CALL_LABEL,
})
public @interface UpdatableStringId {
}
@@ -709,6 +717,7 @@
strings.addAll(DocumentsUi.buildStringsSet());
strings.addAll(MediaProvider.buildStringsSet());
strings.addAll(PermissionController.buildStringsSet());
+ strings.addAll(Dialer.buildStringsSet());
return strings;
}
@@ -2934,5 +2943,54 @@
return strings;
}
}
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the Dialer app.
+ */
+ public static final class Dialer {
+
+ private Dialer() {
+ }
+
+ private static final String PREFIX = "Dialer.";
+
+ /**
+ * The title of the in-call notification for an incoming work call.
+ */
+ public static final String NOTIFICATION_INCOMING_WORK_CALL_TITLE =
+ PREFIX + "NOTIFICATION_INCOMING_WORK_CALL_TITLE";
+
+ /**
+ * The title of the in-call notification for an ongoing work call.
+ */
+ public static final String NOTIFICATION_ONGOING_WORK_CALL_TITLE =
+ PREFIX + "NOTIFICATION_ONGOING_WORK_CALL_TITLE";
+
+ /**
+ * Missed call notification label, used when there's exactly one missed call from work
+ * contact.
+ */
+ public static final String NOTIFICATION_MISSED_WORK_CALL_TITLE =
+ PREFIX + "NOTIFICATION_MISSED_WORK_CALL_TITLE";
+
+ /**
+ * Label for notification indicating that call is being made over wifi.
+ */
+ public static final String NOTIFICATION_WIFI_WORK_CALL_LABEL =
+ PREFIX + "NOTIFICATION_WIFI_WORK_CALL_LABEL";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(NOTIFICATION_INCOMING_WORK_CALL_TITLE);
+ strings.add(NOTIFICATION_ONGOING_WORK_CALL_TITLE);
+ strings.add(NOTIFICATION_MISSED_WORK_CALL_TITLE);
+ strings.add(NOTIFICATION_WIFI_WORK_CALL_LABEL);
+ return strings;
+ }
+ }
}
}
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 927ee0c..0e1caca 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -272,6 +272,7 @@
int getLogoutUserId();
List<UserHandle> getSecondaryUsers(in ComponentName who);
void acknowledgeNewUserDisclaimer();
+ boolean isNewUserDisclaimerAcknowledged();
void enableSystemApp(in ComponentName admin, in String callerPackage, in String packageName);
int enableSystemAppWithIntent(in ComponentName admin, in String callerPackage, in Intent intent);
@@ -554,6 +555,9 @@
void resetDrawables(in String[] drawableIds);
ParcelableResource getDrawable(String drawableId, String drawableStyle, String drawableSource);
+ boolean isDpcDownloaded();
+ void setDpcDownloaded(boolean downloaded);
+
void setStrings(in List<DevicePolicyStringResource> strings);
void resetStrings(in String[] stringIds);
ParcelableResource getString(String stringId);
diff --git a/core/java/android/app/admin/WifiSsidPolicy.java b/core/java/android/app/admin/WifiSsidPolicy.java
index 3715017..e918075 100644
--- a/core/java/android/app/admin/WifiSsidPolicy.java
+++ b/core/java/android/app/admin/WifiSsidPolicy.java
@@ -18,6 +18,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.net.wifi.WifiSsid;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArraySet;
@@ -70,50 +71,37 @@
public @interface WifiSsidPolicyType {}
private @WifiSsidPolicyType int mPolicyType;
- private ArraySet<String> mSsids;
+ private ArraySet<WifiSsid> mSsids;
- private WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<String> ssids) {
+ /**
+ * Create the Wi-Fi SSID Policy.
+ *
+ * @param policyType indicate whether the policy is an allowlist or a denylist
+ * @param ssids set of {@link WifiSsid}
+ * @throws IllegalArgumentException if the input ssids set is empty or the policyType is invalid
+ */
+ public WifiSsidPolicy(@WifiSsidPolicyType int policyType, @NonNull Set<WifiSsid> ssids) {
+ if (ssids.isEmpty()) {
+ throw new IllegalArgumentException("SSID list cannot be empty");
+ }
+ if (policyType != WIFI_SSID_POLICY_TYPE_ALLOWLIST
+ && policyType != WIFI_SSID_POLICY_TYPE_DENYLIST) {
+ throw new IllegalArgumentException("Invalid policy type");
+ }
mPolicyType = policyType;
mSsids = new ArraySet<>(ssids);
}
private WifiSsidPolicy(Parcel in) {
mPolicyType = in.readInt();
- mSsids = (ArraySet<String>) in.readArraySet(null);
- }
- /**
- * Create the allowlist Wi-Fi SSID Policy.
- *
- * @param ssids allowlist of SSIDs in UTF-8 without double quotes format
- * @throws IllegalArgumentException if the input ssids list is empty
- */
- @NonNull
- public static WifiSsidPolicy createAllowlistPolicy(@NonNull Set<String> ssids) {
- if (ssids.isEmpty()) {
- throw new IllegalArgumentException("SSID list cannot be empty");
- }
- return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
+ mSsids = (ArraySet<WifiSsid>) in.readArraySet(null);
}
/**
- * Create the denylist Wi-Fi SSID Policy.
- *
- * @param ssids denylist of SSIDs in UTF-8 without double quotes format
- * @throws IllegalArgumentException if the input ssids list is empty
+ * Returns the set of {@link WifiSsid}
*/
@NonNull
- public static WifiSsidPolicy createDenylistPolicy(@NonNull Set<String> ssids) {
- if (ssids.isEmpty()) {
- throw new IllegalArgumentException("SSID list cannot be empty");
- }
- return new WifiSsidPolicy(WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
- }
-
- /**
- * Returns the set of SSIDs in UTF-8 without double quotes format.
- */
- @NonNull
- public Set<String> getSsids() {
+ public Set<WifiSsid> getSsids() {
return mSsids;
}
diff --git a/core/java/android/app/cloudsearch/SearchResult.java b/core/java/android/app/cloudsearch/SearchResult.java
index 060931b..3403ab0 100644
--- a/core/java/android/app/cloudsearch/SearchResult.java
+++ b/core/java/android/app/cloudsearch/SearchResult.java
@@ -76,7 +76,7 @@
public @interface SearchResultExtraInfoKey {}
/** This App developer website's domain URL, String value expected. */
public static final String EXTRAINFO_APP_DOMAIN_URL = "APP_DOMAIN_URL";
- /** This App result's ICON URL, String value expected. */
+ /** This App icon, android.graphics.drawable.Icon expected. */
public static final String EXTRAINFO_APP_ICON = "APP_ICON";
/** This App developer's name, String value expected. */
public static final String EXTRAINFO_APP_DEVELOPER_NAME = "APP_DEVELOPER_NAME";
@@ -114,7 +114,7 @@
public static final String EXTRAINFO_ACTION_BUTTON_IMAGE_PREREGISTERING = "ACTION_BUTTON_IMAGE";
/** Web content's URL, String value expected. */
public static final String EXTRAINFO_WEB_URL = "WEB_URL";
- /** Web content's domain icon URL, String value expected. */
+ /** Web content's domain icon, android.graphics.drawable.Icon expected. */
public static final String EXTRAINFO_WEB_ICON = "WEB_ICON";
@NonNull
diff --git a/core/java/android/app/smartspace/SmartspaceUtils.java b/core/java/android/app/smartspace/SmartspaceUtils.java
index f058ffa..4545f43 100644
--- a/core/java/android/app/smartspace/SmartspaceUtils.java
+++ b/core/java/android/app/smartspace/SmartspaceUtils.java
@@ -17,6 +17,8 @@
package android.app.smartspace;
import android.annotation.Nullable;
+import android.app.smartspace.uitemplatedata.SmartspaceText;
+import android.text.TextUtils;
/**
* Utilities for Smartspace data.
@@ -28,10 +30,22 @@
private SmartspaceUtils() {
}
+ /** Returns true if the passed in {@link SmartspaceText} is null or its content is empty. */
+ public static boolean isEmpty(@Nullable SmartspaceText text) {
+ return text == null || TextUtils.isEmpty(text.getText());
+ }
+
+ /** Returns true if the passed-in {@link SmartspaceText}s are equal. */
+ public static boolean isEqual(@Nullable SmartspaceText text1, @Nullable SmartspaceText text2) {
+ if (text1 == null && text2 == null) return true;
+ if (text1 == null || text2 == null) return false;
+ return text1.equals(text2);
+ }
+
/** Returns true if the passed-in {@link CharSequence}s are equal. */
public static boolean isEqual(@Nullable CharSequence cs1, @Nullable CharSequence cs2) {
- if ((cs1 == null && cs2 != null) || (cs1 != null && cs2 == null)) return false;
if (cs1 == null && cs2 == null) return true;
+ if (cs1 == null || cs2 == null) return false;
return cs1.toString().contentEquals(cs2);
}
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
index c4c4fde..e996056 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCarouselUiTemplateData.java
@@ -23,7 +23,6 @@
import android.app.smartspace.SmartspaceUtils;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
import java.util.List;
import java.util.Objects;
@@ -51,15 +50,15 @@
}
private SmartspaceCarouselUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
+ @Nullable SmartspaceText supplementalAlarmText,
@NonNull List<CarouselItem> carouselItems,
@Nullable SmartspaceTapAction carouselAction) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
@@ -170,11 +169,11 @@
if (mCarouselItems.isEmpty()) {
throw new IllegalStateException("Carousel data is empty");
}
+
return new SmartspaceCarouselUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
- getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
- mCarouselItems,
+ getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mCarouselItems,
mCarouselAction);
}
}
@@ -184,7 +183,7 @@
/** Text which is above the image item. */
@Nullable
- private final CharSequence mUpperText;
+ private final SmartspaceText mUpperText;
/** Image item. Can be empty. */
@Nullable
@@ -192,7 +191,7 @@
/** Text which is under the image item. */
@Nullable
- private final CharSequence mLowerText;
+ private final SmartspaceText mLowerText;
/**
* Tap action for this {@link CarouselItem} instance. {@code mCarouselAction} is used if not
@@ -202,14 +201,14 @@
private final SmartspaceTapAction mTapAction;
CarouselItem(@NonNull Parcel in) {
- mUpperText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mUpperText = in.readTypedObject(SmartspaceText.CREATOR);
mImage = in.readTypedObject(SmartspaceIcon.CREATOR);
- mLowerText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mLowerText = in.readTypedObject(SmartspaceText.CREATOR);
mTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
}
- private CarouselItem(@Nullable CharSequence upperText, @Nullable SmartspaceIcon image,
- @Nullable CharSequence lowerText, @Nullable SmartspaceTapAction tapAction) {
+ private CarouselItem(@Nullable SmartspaceText upperText, @Nullable SmartspaceIcon image,
+ @Nullable SmartspaceText lowerText, @Nullable SmartspaceTapAction tapAction) {
mUpperText = upperText;
mImage = image;
mLowerText = lowerText;
@@ -217,7 +216,7 @@
}
@Nullable
- public CharSequence getUpperText() {
+ public SmartspaceText getUpperText() {
return mUpperText;
}
@@ -227,7 +226,7 @@
}
@Nullable
- public CharSequence getLowerText() {
+ public SmartspaceText getLowerText() {
return mLowerText;
}
@@ -260,9 +259,9 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
- TextUtils.writeToParcel(mUpperText, out, flags);
+ out.writeTypedObject(mUpperText, flags);
out.writeTypedObject(mImage, flags);
- TextUtils.writeToParcel(mLowerText, out, flags);
+ out.writeTypedObject(mLowerText, flags);
out.writeTypedObject(mTapAction, flags);
}
@@ -300,16 +299,16 @@
@SystemApi
public static final class Builder {
- private CharSequence mUpperText;
+ private SmartspaceText mUpperText;
private SmartspaceIcon mImage;
- private CharSequence mLowerText;
+ private SmartspaceText mLowerText;
private SmartspaceTapAction mTapAction;
/**
* Sets the upper text.
*/
@NonNull
- public Builder setUpperText(@Nullable CharSequence upperText) {
+ public Builder setUpperText(@Nullable SmartspaceText upperText) {
mUpperText = upperText;
return this;
}
@@ -328,7 +327,7 @@
* Sets the lower text.
*/
@NonNull
- public Builder setLowerText(@Nullable CharSequence lowerText) {
+ public Builder setLowerText(@Nullable SmartspaceText lowerText) {
mLowerText = lowerText;
return this;
}
@@ -349,7 +348,8 @@
*/
@NonNull
public CarouselItem build() {
- if (TextUtils.isEmpty(mUpperText) && mImage == null && TextUtils.isEmpty(
+ if (SmartspaceUtils.isEmpty(mUpperText) && mImage == null
+ && SmartspaceUtils.isEmpty(
mLowerText)) {
throw new IllegalStateException("Carousel data is empty");
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
index 7e2f74e..9d4c8e2 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceCombinedCardsUiTemplateData.java
@@ -47,15 +47,15 @@
}
private SmartspaceCombinedCardsUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
+ @Nullable SmartspaceText supplementalAlarmText,
@NonNull List<SmartspaceDefaultUiTemplateData> combinedCardDataList) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
@@ -146,7 +146,7 @@
throw new IllegalStateException("Please assign a value to all @NonNull args.");
}
return new SmartspaceCombinedCardsUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
mCombinedCardDataList);
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
index 742d5c9..a7ac9c7 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceDefaultUiTemplateData.java
@@ -24,7 +24,6 @@
import android.app.smartspace.SmartspaceUtils;
import android.os.Parcel;
import android.os.Parcelable;
-import android.text.TextUtils;
import java.util.Objects;
@@ -50,17 +49,17 @@
* will be used, which has its own tap action applied to the title area.
*/
@Nullable
- private final CharSequence mTitleText;
+ private final SmartspaceText mTitleText;
@Nullable
private final SmartspaceIcon mTitleIcon;
/** Subtitle text and icon are shown at the second row. */
@Nullable
- private final CharSequence mSubtitleText;
+ private final SmartspaceText mSubtitleText;
@Nullable
- private final SmartspaceIcon mSubTitleIcon;
+ private final SmartspaceIcon mSubtitleIcon;
/**
* Primary tap action for the entire card, including the blank spaces, except: 1. When title is
@@ -75,7 +74,7 @@
* Mainly used for weather info on non-weather card.
*/
@Nullable
- private final CharSequence mSupplementalSubtitleText;
+ private final SmartspaceText mSupplementalSubtitleText;
@Nullable
private final SmartspaceIcon mSupplementalSubtitleIcon;
@@ -92,19 +91,19 @@
* alarm".
*/
@Nullable
- private final CharSequence mSupplementalAlarmText;
+ private final SmartspaceText mSupplementalAlarmText;
SmartspaceDefaultUiTemplateData(@NonNull Parcel in) {
mTemplateType = in.readInt();
- mTitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mTitleText = in.readTypedObject(SmartspaceText.CREATOR);
mTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
- mSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mSubTitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
+ mSubtitleText = in.readTypedObject(SmartspaceText.CREATOR);
+ mSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
mPrimaryTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
- mSupplementalSubtitleText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSupplementalSubtitleText = in.readTypedObject(SmartspaceText.CREATOR);
mSupplementalSubtitleIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
mSupplementalSubtitleTapAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
- mSupplementalAlarmText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSupplementalAlarmText = in.readTypedObject(SmartspaceText.CREATOR);
}
/**
@@ -112,20 +111,20 @@
* SmartspaceDefaultUiTemplateData.Builder.
*/
SmartspaceDefaultUiTemplateData(@UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
- @Nullable SmartspaceIcon subTitleIcon,
+ @Nullable SmartspaceText subtitleText,
+ @Nullable SmartspaceIcon subtitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText) {
+ @Nullable SmartspaceText supplementalAlarmText) {
mTemplateType = templateType;
mTitleText = titleText;
mTitleIcon = titleIcon;
mSubtitleText = subtitleText;
- mSubTitleIcon = subTitleIcon;
+ mSubtitleIcon = subtitleIcon;
mPrimaryTapAction = primaryTapAction;
mSupplementalSubtitleText = supplementalSubtitleText;
mSupplementalSubtitleIcon = supplementalSubtitleIcon;
@@ -139,7 +138,7 @@
}
@Nullable
- public CharSequence getTitleText() {
+ public SmartspaceText getTitleText() {
return mTitleText;
}
@@ -149,17 +148,22 @@
}
@Nullable
- public CharSequence getSubtitleText() {
+ public SmartspaceText getSubtitleText() {
return mSubtitleText;
}
@Nullable
- public SmartspaceIcon getSubTitleIcon() {
- return mSubTitleIcon;
+ public SmartspaceIcon getSubtitleIcon() {
+ return mSubtitleIcon;
}
@Nullable
- public CharSequence getSupplementalSubtitleText() {
+ public SmartspaceTapAction getPrimaryTapAction() {
+ return mPrimaryTapAction;
+ }
+
+ @Nullable
+ public SmartspaceText getSupplementalSubtitleText() {
return mSupplementalSubtitleText;
}
@@ -169,17 +173,12 @@
}
@Nullable
- public SmartspaceTapAction getPrimaryTapAction() {
- return mPrimaryTapAction;
- }
-
- @Nullable
public SmartspaceTapAction getSupplementalSubtitleTapAction() {
return mSupplementalSubtitleTapAction;
}
@Nullable
- public CharSequence getSupplementalAlarmText() {
+ public SmartspaceText getSupplementalAlarmText() {
return mSupplementalAlarmText;
}
@@ -208,15 +207,15 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeInt(mTemplateType);
- TextUtils.writeToParcel(mTitleText, out, flags);
+ out.writeTypedObject(mTitleText, flags);
out.writeTypedObject(mTitleIcon, flags);
- TextUtils.writeToParcel(mSubtitleText, out, flags);
- out.writeTypedObject(mSubTitleIcon, flags);
+ out.writeTypedObject(mSubtitleText, flags);
+ out.writeTypedObject(mSubtitleIcon, flags);
out.writeTypedObject(mPrimaryTapAction, flags);
- TextUtils.writeToParcel(mSupplementalSubtitleText, out, flags);
+ out.writeTypedObject(mSupplementalSubtitleText, flags);
out.writeTypedObject(mSupplementalSubtitleIcon, flags);
out.writeTypedObject(mSupplementalSubtitleTapAction, flags);
- TextUtils.writeToParcel(mSupplementalAlarmText, out, flags);
+ out.writeTypedObject(mSupplementalAlarmText, flags);
}
@Override
@@ -228,7 +227,7 @@
that.mTitleText)
&& Objects.equals(mTitleIcon, that.mTitleIcon)
&& SmartspaceUtils.isEqual(mSubtitleText, that.mSubtitleText)
- && Objects.equals(mSubTitleIcon, that.mSubTitleIcon)
+ && Objects.equals(mSubtitleIcon, that.mSubtitleIcon)
&& Objects.equals(mPrimaryTapAction, that.mPrimaryTapAction)
&& SmartspaceUtils.isEqual(mSupplementalSubtitleText,
that.mSupplementalSubtitleText)
@@ -240,7 +239,7 @@
@Override
public int hashCode() {
- return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubTitleIcon,
+ return Objects.hash(mTemplateType, mTitleText, mTitleIcon, mSubtitleText, mSubtitleIcon,
mPrimaryTapAction, mSupplementalSubtitleText, mSupplementalSubtitleIcon,
mSupplementalSubtitleTapAction, mSupplementalAlarmText);
}
@@ -252,7 +251,7 @@
+ ", mTitleText=" + mTitleText
+ ", mTitleIcon=" + mTitleIcon
+ ", mSubtitleText=" + mSubtitleText
- + ", mSubTitleIcon=" + mSubTitleIcon
+ + ", mSubTitleIcon=" + mSubtitleIcon
+ ", mPrimaryTapAction=" + mPrimaryTapAction
+ ", mSupplementalSubtitleText=" + mSupplementalSubtitleText
+ ", mSupplementalSubtitleIcon=" + mSupplementalSubtitleIcon
@@ -271,15 +270,15 @@
public static class Builder {
@UiTemplateType
private final int mTemplateType;
- private CharSequence mTitleText;
+ private SmartspaceText mTitleText;
private SmartspaceIcon mTitleIcon;
- private CharSequence mSubtitleText;
- private SmartspaceIcon mSubTitleIcon;
+ private SmartspaceText mSubtitleText;
+ private SmartspaceIcon mSubtitleIcon;
private SmartspaceTapAction mPrimaryTapAction;
- private CharSequence mSupplementalSubtitleText;
+ private SmartspaceText mSupplementalSubtitleText;
private SmartspaceIcon mSupplementalSubtitleIcon;
private SmartspaceTapAction mSupplementalSubtitleTapAction;
- private CharSequence mSupplementalAlarmText;
+ private SmartspaceText mSupplementalAlarmText;
/**
* A builder for {@link SmartspaceDefaultUiTemplateData}.
@@ -300,7 +299,7 @@
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- CharSequence getTitleText() {
+ SmartspaceText getTitleText() {
return mTitleText;
}
@@ -314,15 +313,15 @@
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- CharSequence getSubtitleText() {
+ SmartspaceText getSubtitleText() {
return mSubtitleText;
}
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- SmartspaceIcon getSubTitleIcon() {
- return mSubTitleIcon;
+ SmartspaceIcon getSubtitleIcon() {
+ return mSubtitleIcon;
}
/** Should ONLY be used by the subclasses */
@@ -335,7 +334,7 @@
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- CharSequence getSupplementalSubtitleText() {
+ SmartspaceText getSupplementalSubtitleText() {
return mSupplementalSubtitleText;
}
@@ -356,7 +355,7 @@
/** Should ONLY be used by the subclasses */
@Nullable
@SuppressLint("GetterOnBuilder")
- CharSequence getSupplementalAlarmText() {
+ SmartspaceText getSupplementalAlarmText() {
return mSupplementalAlarmText;
}
@@ -364,7 +363,7 @@
* Sets the card title.
*/
@NonNull
- public Builder setTitleText(@NonNull CharSequence titleText) {
+ public Builder setTitleText(@NonNull SmartspaceText titleText) {
mTitleText = titleText;
return this;
}
@@ -382,7 +381,7 @@
* Sets the card subtitle.
*/
@NonNull
- public Builder setSubtitleText(@NonNull CharSequence subtitleText) {
+ public Builder setSubtitleText(@NonNull SmartspaceText subtitleText) {
mSubtitleText = subtitleText;
return this;
}
@@ -391,8 +390,8 @@
* Sets the card subtitle icon.
*/
@NonNull
- public Builder setSubTitleIcon(@NonNull SmartspaceIcon subTitleIcon) {
- mSubTitleIcon = subTitleIcon;
+ public Builder setSubtitleIcon(@NonNull SmartspaceIcon subtitleIcon) {
+ mSubtitleIcon = subtitleIcon;
return this;
}
@@ -409,7 +408,8 @@
* Sets the supplemental subtitle text.
*/
@NonNull
- public Builder setSupplementalSubtitleText(@NonNull CharSequence supplementalSubtitleText) {
+ public Builder setSupplementalSubtitleText(
+ @NonNull SmartspaceText supplementalSubtitleText) {
mSupplementalSubtitleText = supplementalSubtitleText;
return this;
}
@@ -440,7 +440,7 @@
* Sets the supplemental alarm text.
*/
@NonNull
- public Builder setSupplementalAlarmText(@NonNull CharSequence supplementalAlarmText) {
+ public Builder setSupplementalAlarmText(@NonNull SmartspaceText supplementalAlarmText) {
mSupplementalAlarmText = supplementalAlarmText;
return this;
}
@@ -451,7 +451,7 @@
@NonNull
public SmartspaceDefaultUiTemplateData build() {
return new SmartspaceDefaultUiTemplateData(mTemplateType, mTitleText, mTitleIcon,
- mSubtitleText, mSubTitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
+ mSubtitleText, mSubtitleIcon, mPrimaryTapAction, mSupplementalSubtitleText,
mSupplementalSubtitleIcon, mSupplementalSubtitleTapAction,
mSupplementalAlarmText);
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
index c76af27..bcd12eb 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceHeadToHeadUiTemplateData.java
@@ -22,7 +22,6 @@
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceUtils;
import android.os.Parcel;
-import android.text.TextUtils;
import java.util.Objects;
@@ -35,15 +34,15 @@
public final class SmartspaceHeadToHeadUiTemplateData extends SmartspaceDefaultUiTemplateData {
@Nullable
- private final CharSequence mHeadToHeadTitle;
+ private final SmartspaceText mHeadToHeadTitle;
@Nullable
private final SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
@Nullable
private final SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
@Nullable
- private final CharSequence mHeadToHeadFirstCompetitorText;
+ private final SmartspaceText mHeadToHeadFirstCompetitorText;
@Nullable
- private final CharSequence mHeadToHeadSecondCompetitorText;
+ private final SmartspaceText mHeadToHeadSecondCompetitorText;
/** Tap action for the head-to-head secondary card. */
@Nullable
@@ -51,29 +50,29 @@
SmartspaceHeadToHeadUiTemplateData(@NonNull Parcel in) {
super(in);
- mHeadToHeadTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mHeadToHeadTitle = in.readTypedObject(SmartspaceText.CREATOR);
mHeadToHeadFirstCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
mHeadToHeadSecondCompetitorIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
- mHeadToHeadFirstCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
- mHeadToHeadSecondCompetitorText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mHeadToHeadFirstCompetitorText = in.readTypedObject(SmartspaceText.CREATOR);
+ mHeadToHeadSecondCompetitorText = in.readTypedObject(SmartspaceText.CREATOR);
mHeadToHeadAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
}
private SmartspaceHeadToHeadUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
- @Nullable CharSequence headToHeadTitle,
+ @Nullable SmartspaceText supplementalAlarmText,
+ @Nullable SmartspaceText headToHeadTitle,
@Nullable SmartspaceIcon headToHeadFirstCompetitorIcon,
@Nullable SmartspaceIcon headToHeadSecondCompetitorIcon,
- @Nullable CharSequence headToHeadFirstCompetitorText,
- @Nullable CharSequence headToHeadSecondCompetitorText,
+ @Nullable SmartspaceText headToHeadFirstCompetitorText,
+ @Nullable SmartspaceText headToHeadSecondCompetitorText,
@Nullable SmartspaceTapAction headToHeadAction) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
@@ -87,7 +86,7 @@
}
@Nullable
- public CharSequence getHeadToHeadTitle() {
+ public SmartspaceText getHeadToHeadTitle() {
return mHeadToHeadTitle;
}
@@ -102,12 +101,12 @@
}
@Nullable
- public CharSequence getHeadToHeadFirstCompetitorText() {
+ public SmartspaceText getHeadToHeadFirstCompetitorText() {
return mHeadToHeadFirstCompetitorText;
}
@Nullable
- public CharSequence getHeadToHeadSecondCompetitorText() {
+ public SmartspaceText getHeadToHeadSecondCompetitorText() {
return mHeadToHeadSecondCompetitorText;
}
@@ -141,11 +140,11 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
- TextUtils.writeToParcel(mHeadToHeadTitle, out, flags);
+ out.writeTypedObject(mHeadToHeadTitle, flags);
out.writeTypedObject(mHeadToHeadFirstCompetitorIcon, flags);
out.writeTypedObject(mHeadToHeadSecondCompetitorIcon, flags);
- TextUtils.writeToParcel(mHeadToHeadFirstCompetitorText, out, flags);
- TextUtils.writeToParcel(mHeadToHeadSecondCompetitorText, out, flags);
+ out.writeTypedObject(mHeadToHeadFirstCompetitorText, flags);
+ out.writeTypedObject(mHeadToHeadSecondCompetitorText, flags);
out.writeTypedObject(mHeadToHeadAction, flags);
}
@@ -195,11 +194,11 @@
@SystemApi
public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
- private CharSequence mHeadToHeadTitle;
+ private SmartspaceText mHeadToHeadTitle;
private SmartspaceIcon mHeadToHeadFirstCompetitorIcon;
private SmartspaceIcon mHeadToHeadSecondCompetitorIcon;
- private CharSequence mHeadToHeadFirstCompetitorText;
- private CharSequence mHeadToHeadSecondCompetitorText;
+ private SmartspaceText mHeadToHeadFirstCompetitorText;
+ private SmartspaceText mHeadToHeadSecondCompetitorText;
private SmartspaceTapAction mHeadToHeadAction;
/**
@@ -213,7 +212,7 @@
* Sets the head-to-head card's title
*/
@NonNull
- public Builder setHeadToHeadTitle(@Nullable CharSequence headToHeadTitle) {
+ public Builder setHeadToHeadTitle(@Nullable SmartspaceText headToHeadTitle) {
mHeadToHeadTitle = headToHeadTitle;
return this;
}
@@ -243,7 +242,7 @@
*/
@NonNull
public Builder setHeadToHeadFirstCompetitorText(
- @Nullable CharSequence headToHeadFirstCompetitorText) {
+ @Nullable SmartspaceText headToHeadFirstCompetitorText) {
mHeadToHeadFirstCompetitorText = headToHeadFirstCompetitorText;
return this;
}
@@ -253,7 +252,7 @@
*/
@NonNull
public Builder setHeadToHeadSecondCompetitorText(
- @Nullable CharSequence headToHeadSecondCompetitorText) {
+ @Nullable SmartspaceText headToHeadSecondCompetitorText) {
mHeadToHeadSecondCompetitorText = headToHeadSecondCompetitorText;
return this;
}
@@ -273,7 +272,7 @@
@NonNull
public SmartspaceHeadToHeadUiTemplateData build() {
return new SmartspaceHeadToHeadUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
getSupplementalSubtitleTapAction(), getSupplementalAlarmText(),
mHeadToHeadTitle,
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
index 70b3095..1efbaeb 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceIcon.java
@@ -42,14 +42,19 @@
@Nullable
private final CharSequence mContentDescription;
+ private final boolean mShouldTint;
+
SmartspaceIcon(@NonNull Parcel in) {
mIcon = in.readTypedObject(Icon.CREATOR);
mContentDescription = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mShouldTint = in.readBoolean();
}
- private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription) {
+ private SmartspaceIcon(@NonNull Icon icon, @Nullable CharSequence contentDescription,
+ boolean shouldTint) {
mIcon = icon;
mContentDescription = contentDescription;
+ mShouldTint = shouldTint;
}
@NonNull
@@ -62,6 +67,11 @@
return mContentDescription;
}
+ /** Return shouldTint value. The default value is true. */
+ public boolean shouldTint() {
+ return mShouldTint;
+ }
+
@NonNull
public static final Creator<SmartspaceIcon> CREATOR = new Creator<SmartspaceIcon>() {
@Override
@@ -80,13 +90,14 @@
if (this == o) return true;
if (!(o instanceof SmartspaceIcon)) return false;
SmartspaceIcon that = (SmartspaceIcon) o;
- return mIcon.equals(that.mIcon) && SmartspaceUtils.isEqual(mContentDescription,
- that.mContentDescription);
+ return mIcon.toString().equals(that.mIcon.toString()) && SmartspaceUtils.isEqual(
+ mContentDescription,
+ that.mContentDescription) && mShouldTint == that.mShouldTint;
}
@Override
public int hashCode() {
- return Objects.hash(mIcon, mContentDescription);
+ return Objects.hash(mIcon.toString(), mContentDescription, mShouldTint);
}
@Override
@@ -98,13 +109,15 @@
public void writeToParcel(@NonNull Parcel out, int flags) {
out.writeTypedObject(mIcon, flags);
TextUtils.writeToParcel(mContentDescription, out, flags);
+ out.writeBoolean(mShouldTint);
}
@Override
public String toString() {
return "SmartspaceIcon{"
- + "mImage=" + mIcon
- + ", mContentDescription='" + mContentDescription + '\''
+ + "mIcon=" + mIcon
+ + ", mContentDescription=" + mContentDescription
+ + ", mShouldTint=" + mShouldTint
+ '}';
}
@@ -118,14 +131,16 @@
private Icon mIcon;
private CharSequence mContentDescription;
+ private boolean mShouldTint;
/**
- * A builder for {@link SmartspaceIcon}.
+ * A builder for {@link SmartspaceIcon}, which sets shouldTint to true by default.
*
- * @param icon the icon image of this smartspace icon.
+ * @param icon the icon image of this {@link SmartspaceIcon} instance.
*/
public Builder(@NonNull Icon icon) {
mIcon = Objects.requireNonNull(icon);
+ mShouldTint = true;
}
/**
@@ -138,11 +153,20 @@
}
/**
+ * Sets should tint icon.
+ */
+ @NonNull
+ public Builder setShouldTint(boolean shouldTint) {
+ mShouldTint = shouldTint;
+ return this;
+ }
+
+ /**
* Builds a new SmartspaceIcon instance.
*/
@NonNull
public SmartspaceIcon build() {
- return new SmartspaceIcon(mIcon, mContentDescription);
+ return new SmartspaceIcon(mIcon, mContentDescription, mShouldTint);
}
}
}
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
index 287cf8e..2db13d31 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubCardUiTemplateData.java
@@ -22,7 +22,6 @@
import android.app.smartspace.SmartspaceTarget;
import android.app.smartspace.SmartspaceUtils;
import android.os.Parcel;
-import android.text.TextUtils;
import java.util.Objects;
@@ -40,7 +39,7 @@
/** Text for the sub-card, which shows below the icon when being set. */
@Nullable
- private final CharSequence mSubCardText;
+ private final SmartspaceText mSubCardText;
/** Tap action for the sub-card secondary card. */
@Nullable
@@ -49,22 +48,22 @@
SmartspaceSubCardUiTemplateData(@NonNull Parcel in) {
super(in);
mSubCardIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
- mSubCardText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mSubCardText = in.readTypedObject(SmartspaceText.CREATOR);
mSubCardAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
}
private SmartspaceSubCardUiTemplateData(int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
+ @Nullable SmartspaceText supplementalAlarmText,
@NonNull SmartspaceIcon subCardIcon,
- @Nullable CharSequence subCardText,
+ @Nullable SmartspaceText subCardText,
@Nullable SmartspaceTapAction subCardAction) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
@@ -80,7 +79,7 @@
}
@Nullable
- public CharSequence getSubCardText() {
+ public SmartspaceText getSubCardText() {
return mSubCardText;
}
@@ -115,7 +114,7 @@
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeTypedObject(mSubCardIcon, flags);
- TextUtils.writeToParcel(mSubCardText, out, flags);
+ out.writeTypedObject(mSubCardText, flags);
out.writeTypedObject(mSubCardAction, flags);
}
@@ -153,7 +152,7 @@
public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
private final SmartspaceIcon mSubCardIcon;
- private CharSequence mSubCardText;
+ private SmartspaceText mSubCardText;
private SmartspaceTapAction mSubCardAction;
/**
@@ -165,11 +164,11 @@
}
/**
- * Sets the card title text.
+ * Sets the card text.
*/
@NonNull
- public Builder setSubCardAction(@NonNull CharSequence subCardTitleText) {
- mSubCardText = subCardTitleText;
+ public Builder setSubCardText(@NonNull SmartspaceText subCardText) {
+ mSubCardText = subCardText;
return this;
}
@@ -188,7 +187,7 @@
@NonNull
public SmartspaceSubCardUiTemplateData build() {
return new SmartspaceSubCardUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubCardIcon,
mSubCardText,
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
index c479993..2fe4cf8 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubImageUiTemplateData.java
@@ -22,8 +22,6 @@
import android.app.smartspace.SmartspaceTarget;
import android.os.Parcel;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
@@ -37,7 +35,7 @@
/** Texts are shown next to the image as a vertical list */
@NonNull
- private final List<CharSequence> mSubImageTexts;
+ private final List<SmartspaceText> mSubImageTexts;
/** If multiple images are passed in, they will be rendered as GIF. */
@NonNull
@@ -49,22 +47,22 @@
SmartspaceSubImageUiTemplateData(@NonNull Parcel in) {
super(in);
- mSubImageTexts = Arrays.asList(in.readCharSequenceArray());
+ mSubImageTexts = in.createTypedArrayList(SmartspaceText.CREATOR);
mSubImages = in.createTypedArrayList(SmartspaceIcon.CREATOR);
mSubImageAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
}
private SmartspaceSubImageUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
- @NonNull List<CharSequence> subImageTexts,
+ @Nullable SmartspaceText supplementalAlarmText,
+ @NonNull List<SmartspaceText> subImageTexts,
@NonNull List<SmartspaceIcon> subImages,
@Nullable SmartspaceTapAction subImageAction) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
@@ -76,7 +74,7 @@
}
@NonNull
- public List<CharSequence> getSubImageTexts() {
+ public List<SmartspaceText> getSubImageTexts() {
return mSubImageTexts;
}
@@ -115,7 +113,7 @@
@Override
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
- out.writeCharSequenceList(new ArrayList<>(mSubImageTexts));
+ out.writeTypedList(mSubImageTexts);
out.writeTypedList(mSubImages);
out.writeTypedObject(mSubImageAction, flags);
}
@@ -153,14 +151,14 @@
@SystemApi
public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
- private final List<CharSequence> mSubImageTexts;
+ private final List<SmartspaceText> mSubImageTexts;
private final List<SmartspaceIcon> mSubImages;
private SmartspaceTapAction mSubImageAction;
/**
* A builder for {@link SmartspaceSubImageUiTemplateData}.
*/
- public Builder(@NonNull List<CharSequence> subImageTexts,
+ public Builder(@NonNull List<SmartspaceText> subImageTexts,
@NonNull List<SmartspaceIcon> subImages) {
super(SmartspaceTarget.UI_TEMPLATE_SUB_IMAGE);
mSubImageTexts = Objects.requireNonNull(subImageTexts);
@@ -171,7 +169,7 @@
* Sets the card tap action.
*/
@NonNull
- public Builder setCarouselAction(@NonNull SmartspaceTapAction subImageAction) {
+ public Builder setSubImageAction(@NonNull SmartspaceTapAction subImageAction) {
mSubImageAction = subImageAction;
return this;
}
@@ -182,7 +180,7 @@
@NonNull
public SmartspaceSubImageUiTemplateData build() {
return new SmartspaceSubImageUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubImageTexts,
mSubImages,
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
index b5d9645..9512c7f 100644
--- a/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceSubListUiTemplateData.java
@@ -22,11 +22,10 @@
import android.app.smartspace.SmartspaceTarget;
import android.os.Parcel;
-import java.util.ArrayList;
-import java.util.Arrays;
import java.util.List;
import java.util.Objects;
+
/**
* Holds all the relevant data needed to render a Smartspace card with the sub-list Ui Template.
*
@@ -38,7 +37,7 @@
@Nullable
private final SmartspaceIcon mSubListIcon;
@NonNull
- private final List<CharSequence> mSubListTexts;
+ private final List<SmartspaceText> mSubListTexts;
/** Tap action for the sub-list secondary card. */
@Nullable
@@ -47,22 +46,22 @@
SmartspaceSubListUiTemplateData(@NonNull Parcel in) {
super(in);
mSubListIcon = in.readTypedObject(SmartspaceIcon.CREATOR);
- mSubListTexts = Arrays.asList(in.readCharSequenceArray());
+ mSubListTexts = in.createTypedArrayList(SmartspaceText.CREATOR);
mSubListAction = in.readTypedObject(SmartspaceTapAction.CREATOR);
}
private SmartspaceSubListUiTemplateData(@SmartspaceTarget.UiTemplateType int templateType,
- @Nullable CharSequence titleText,
+ @Nullable SmartspaceText titleText,
@Nullable SmartspaceIcon titleIcon,
- @Nullable CharSequence subtitleText,
+ @Nullable SmartspaceText subtitleText,
@Nullable SmartspaceIcon subTitleIcon,
@Nullable SmartspaceTapAction primaryTapAction,
- @Nullable CharSequence supplementalSubtitleText,
+ @Nullable SmartspaceText supplementalSubtitleText,
@Nullable SmartspaceIcon supplementalSubtitleIcon,
@Nullable SmartspaceTapAction supplementalSubtitleTapAction,
- @Nullable CharSequence supplementalAlarmText,
+ @Nullable SmartspaceText supplementalAlarmText,
@Nullable SmartspaceIcon subListIcon,
- @NonNull List<CharSequence> subListTexts,
+ @NonNull List<SmartspaceText> subListTexts,
@Nullable SmartspaceTapAction subListAction) {
super(templateType, titleText, titleIcon, subtitleText, subTitleIcon, primaryTapAction,
supplementalSubtitleText, supplementalSubtitleIcon, supplementalSubtitleTapAction,
@@ -78,7 +77,7 @@
}
@NonNull
- public List<CharSequence> getSubListTexts() {
+ public List<SmartspaceText> getSubListTexts() {
return mSubListTexts;
}
@@ -113,7 +112,7 @@
public void writeToParcel(@NonNull Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeTypedObject(mSubListIcon, flags);
- out.writeCharSequenceList(new ArrayList<>(mSubListTexts));
+ out.writeTypedList(mSubListTexts);
out.writeTypedObject(mSubListAction, flags);
}
@@ -151,13 +150,13 @@
public static final class Builder extends SmartspaceDefaultUiTemplateData.Builder {
private SmartspaceIcon mSubListIcon;
- private final List<CharSequence> mSubListTexts;
+ private final List<SmartspaceText> mSubListTexts;
private SmartspaceTapAction mSubListAction;
/**
* A builder for {@link SmartspaceSubListUiTemplateData}.
*/
- public Builder(@NonNull List<CharSequence> subListTexts) {
+ public Builder(@NonNull List<SmartspaceText> subListTexts) {
super(SmartspaceTarget.UI_TEMPLATE_SUB_LIST);
mSubListTexts = Objects.requireNonNull(subListTexts);
}
@@ -175,7 +174,7 @@
* Sets the card tap action.
*/
@NonNull
- public Builder setCarouselAction(@NonNull SmartspaceTapAction subListAction) {
+ public Builder setSubListAction(@NonNull SmartspaceTapAction subListAction) {
mSubListAction = subListAction;
return this;
}
@@ -186,7 +185,7 @@
@NonNull
public SmartspaceSubListUiTemplateData build() {
return new SmartspaceSubListUiTemplateData(getTemplateType(), getTitleText(),
- getTitleIcon(), getSubtitleText(), getSubTitleIcon(), getPrimaryTapAction(),
+ getTitleIcon(), getSubtitleText(), getSubtitleIcon(), getPrimaryTapAction(),
getSupplementalSubtitleText(), getSupplementalSubtitleIcon(),
getSupplementalSubtitleTapAction(), getSupplementalAlarmText(), mSubListIcon,
mSubListTexts,
diff --git a/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java b/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java
new file mode 100644
index 0000000..25d13e6
--- /dev/null
+++ b/core/java/android/app/smartspace/uitemplatedata/SmartspaceText.java
@@ -0,0 +1,143 @@
+/*
+ * 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.app.smartspace.uitemplatedata;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.smartspace.SmartspaceUtils;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+/**
+ * Holds the information for a Smartspace-card text: the text content and
+ * the truncate_at information.
+ *
+ * @hide
+ */
+@SystemApi
+public final class SmartspaceText implements Parcelable {
+
+ @NonNull
+ private final CharSequence mText;
+
+ private final TextUtils.TruncateAt mTruncateAtType;
+
+ SmartspaceText(Parcel in) {
+ mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+ mTruncateAtType = TextUtils.TruncateAt.valueOf(in.readString());
+ }
+
+ private SmartspaceText(@NonNull CharSequence text, TextUtils.TruncateAt truncateAtType) {
+ mText = text;
+ mTruncateAtType = truncateAtType;
+ }
+
+ @NonNull
+ public CharSequence getText() {
+ return mText;
+ }
+
+ @NonNull
+ public TextUtils.TruncateAt getTruncateAtType() {
+ return mTruncateAtType;
+ }
+
+ @NonNull
+ public static final Creator<SmartspaceText> CREATOR = new Creator<SmartspaceText>() {
+ @Override
+ public SmartspaceText createFromParcel(Parcel in) {
+ return new SmartspaceText(in);
+ }
+
+ @Override
+ public SmartspaceText[] newArray(int size) {
+ return new SmartspaceText[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof SmartspaceText)) return false;
+ SmartspaceText that = (SmartspaceText) o;
+ return mTruncateAtType == that.mTruncateAtType && SmartspaceUtils.isEqual(mText,
+ that.mText);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mText, mTruncateAtType);
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ TextUtils.writeToParcel(mText, out, flags);
+ out.writeString(mTruncateAtType.name());
+ }
+
+ /**
+ * A builder for {@link SmartspaceText} object.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private final CharSequence mText;
+ private TextUtils.TruncateAt mTruncateAtType;
+
+ /**
+ * A builder for {@link SmartspaceText}, which sets TruncateAtType to AT_END by default.
+ */
+ public Builder(@NonNull CharSequence text) {
+ mText = Objects.requireNonNull(text);
+ mTruncateAtType = TextUtils.TruncateAt.END;
+ }
+
+ /**
+ * A builder for {@link SmartspaceText}.
+ */
+ public Builder(@NonNull CharSequence text, @NonNull TextUtils.TruncateAt truncateAtType) {
+ mText = Objects.requireNonNull(text);
+ mTruncateAtType = Objects.requireNonNull(truncateAtType);
+ }
+
+ /**
+ * Sets truncateAtType.
+ */
+ @NonNull
+ public Builder setTruncateAtType(@NonNull TextUtils.TruncateAt truncateAtType) {
+ mTruncateAtType = Objects.requireNonNull(truncateAtType);
+ return this;
+ }
+
+ /**
+ * Builds a new SmartspaceText instance.
+ */
+ @NonNull
+ public SmartspaceText build() {
+ return new SmartspaceText(mText, mTruncateAtType);
+ }
+ }
+}
diff --git a/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java
new file mode 100644
index 0000000..dfbc7a4
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CameraAttributes.java
@@ -0,0 +1,301 @@
+/*
+ * 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.app.wallpapereffectsgeneration;
+
+import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Representing the position and other parameters of camera of a single frame.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CameraAttributes implements Parcelable {
+ /**
+ * The location of the anchor within the 3D scene.
+ * Expecting 3 floats representing the x, y, z coordinates
+ * of the anchor point.
+ */
+ @NonNull
+ private float[] mAnchorPointInWorldSpace;
+ /**
+ * Where the anchor point should project to in the output image.
+ * Expecting 2 floats representing the u,v coordinates of the
+ * anchor point.
+ */
+ @NonNull
+ private float[] mAnchorPointInOutputUvSpace;
+ /**
+ * Specifies the amount of yaw orbit rotation the camera should perform
+ * around the anchor point in world space.
+ */
+ private float mCameraOrbitYawDegrees;
+ /**
+ * Specifies the amount of pitch orbit rotation the camera should perform
+ * around the anchor point in world space.
+ */
+ private float mCameraOrbitPitchDegrees;
+ /**
+ * Specifies by how much the camera should be placed towards the anchor
+ * point in world space, which is also called dolly distance.
+ */
+ private float mDollyDistanceInWorldSpace;
+ /**
+ * Specifies the vertical fov degrees of the virtual image.
+ */
+ private float mVerticalFovDegrees;
+ /**
+ * The frustum of near plane.
+ */
+ private float mFrustumNearInWorldSpace;
+ /**
+ * The frustum of far plane.
+ */
+ private float mFrustumFarInWorldSpace;
+
+ private CameraAttributes(Parcel in) {
+ this.mCameraOrbitYawDegrees = in.readFloat();
+ this.mCameraOrbitPitchDegrees = in.readFloat();
+ this.mDollyDistanceInWorldSpace = in.readFloat();
+ this.mVerticalFovDegrees = in.readFloat();
+ this.mFrustumNearInWorldSpace = in.readFloat();
+ this.mFrustumFarInWorldSpace = in.readFloat();
+ this.mAnchorPointInWorldSpace = in.createFloatArray();
+ this.mAnchorPointInOutputUvSpace = in.createFloatArray();
+ }
+
+ private CameraAttributes(float[] anchorPointInWorldSpace, float[] anchorPointInOutputUvSpace,
+ float cameraOrbitYawDegrees, float cameraOrbitPitchDegrees,
+ float dollyDistanceInWorldSpace,
+ float verticalFovDegrees, float frustumNearInWorldSpace, float frustumFarInWorldSpace) {
+ mAnchorPointInWorldSpace = anchorPointInWorldSpace;
+ mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
+ mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
+ mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
+ mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
+ mVerticalFovDegrees = verticalFovDegrees;
+ mFrustumNearInWorldSpace = frustumNearInWorldSpace;
+ mFrustumFarInWorldSpace = frustumFarInWorldSpace;
+ }
+
+ /**
+ * Get the location of the anchor within the 3D scene. The response float array contains
+ * 3 floats representing the x, y, z coordinates
+ */
+ @NonNull
+ public float[] getAnchorPointInWorldSpace() {
+ return mAnchorPointInWorldSpace;
+ }
+
+ /**
+ * Get where the anchor point should project to in the output image. The response float
+ * array contains 2 floats representing the u,v coordinates of the anchor point.
+ */
+ @NonNull
+ public float[] getAnchorPointInOutputUvSpace() {
+ return mAnchorPointInOutputUvSpace;
+ }
+
+ /**
+ * Get the camera yaw orbit rotation.
+ */
+ public float getCameraOrbitYawDegrees() {
+ return mCameraOrbitYawDegrees;
+ }
+
+ /**
+ * Get the camera pitch orbit rotation.
+ */
+ public float getCameraOrbitPitchDegrees() {
+ return mCameraOrbitPitchDegrees;
+ }
+
+ /**
+ * Get how many units the camera should be placed towards the anchor point in world space.
+ */
+ public float getDollyDistanceInWorldSpace() {
+ return mDollyDistanceInWorldSpace;
+ }
+
+ /**
+ * Get the camera vertical fov degrees.
+ */
+ public float getVerticalFovDegrees() {
+ return mVerticalFovDegrees;
+ }
+
+ /**
+ * Get the frustum in near plane.
+ */
+ public float getFrustumNearInWorldSpace() {
+ return mFrustumNearInWorldSpace;
+ }
+
+ /**
+ * Get the frustum in far plane.
+ */
+ public float getFrustumFarInWorldSpace() {
+ return mFrustumFarInWorldSpace;
+ }
+
+ @NonNull
+ public static final Creator<CameraAttributes> CREATOR = new Creator<CameraAttributes>() {
+ @Override
+ public CameraAttributes createFromParcel(Parcel in) {
+ return new CameraAttributes(in);
+ }
+
+ @Override
+ public CameraAttributes[] newArray(int size) {
+ return new CameraAttributes[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeFloat(mCameraOrbitYawDegrees);
+ out.writeFloat(mCameraOrbitPitchDegrees);
+ out.writeFloat(mDollyDistanceInWorldSpace);
+ out.writeFloat(mVerticalFovDegrees);
+ out.writeFloat(mFrustumNearInWorldSpace);
+ out.writeFloat(mFrustumFarInWorldSpace);
+ out.writeFloatArray(mAnchorPointInWorldSpace);
+ out.writeFloatArray(mAnchorPointInOutputUvSpace);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * Builder for {@link CameraAttributes}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @NonNull
+ private float[] mAnchorPointInWorldSpace;
+ @NonNull
+ private float[] mAnchorPointInOutputUvSpace;
+ private float mCameraOrbitYawDegrees;
+ private float mCameraOrbitPitchDegrees;
+ private float mDollyDistanceInWorldSpace;
+ private float mVerticalFovDegrees;
+ private float mFrustumNearInWorldSpace;
+ private float mFrustumFarInWorldSpace;
+
+ /**
+ * Constructor with anchor point in world space and anchor point in output image
+ * space.
+ *
+ * @param anchorPointInWorldSpace the location of the anchor within the 3D scene. The
+ * float array contains 3 floats representing the x, y, z coordinates.
+ * @param anchorPointInOutputUvSpace where the anchor point should project to in the
+ * output image. The float array contains 2 floats representing the u,v coordinates
+ * of the anchor point.
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull float[] anchorPointInWorldSpace,
+ @NonNull float[] anchorPointInOutputUvSpace) {
+ mAnchorPointInWorldSpace = anchorPointInWorldSpace;
+ mAnchorPointInOutputUvSpace = anchorPointInOutputUvSpace;
+ }
+
+ /**
+ * Sets the camera orbit yaw rotation.
+ */
+ @NonNull
+ public Builder setCameraOrbitYawDegrees(
+ @FloatRange(from = -180.0f, to = 180.0f) float cameraOrbitYawDegrees) {
+ mCameraOrbitYawDegrees = cameraOrbitYawDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the camera orbit pitch rotation.
+ */
+ @NonNull
+ public Builder setCameraOrbitPitchDegrees(
+ @FloatRange(from = -90.0f, to = 90.0f) float cameraOrbitPitchDegrees) {
+ mCameraOrbitPitchDegrees = cameraOrbitPitchDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the camera dolly distance.
+ */
+ @NonNull
+ public Builder setDollyDistanceInWorldSpace(float dollyDistanceInWorldSpace) {
+ mDollyDistanceInWorldSpace = dollyDistanceInWorldSpace;
+ return this;
+ }
+
+ /**
+ * Sets the camera vertical fov degree.
+ */
+ @NonNull
+ public Builder setVerticalFovDegrees(
+ @FloatRange(from = 0.0f, to = 180.0f, fromInclusive = false)
+ float verticalFovDegrees) {
+ mVerticalFovDegrees = verticalFovDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the frustum in near plane.
+ */
+ @NonNull
+ public Builder setFrustumNearInWorldSpace(
+ @FloatRange(from = 0.0f) float frustumNearInWorldSpace) {
+ mFrustumNearInWorldSpace = frustumNearInWorldSpace;
+ return this;
+ }
+
+ /**
+ * Sets the frustum in far plane.
+ */
+ @NonNull
+ public Builder setFrustumFarInWorldSpace(
+ @FloatRange(from = 0.0f) float frustumFarInWorldSpace) {
+ mFrustumFarInWorldSpace = frustumFarInWorldSpace;
+ return this;
+ }
+
+ /**
+ * Builds a new {@link CameraAttributes} instance.
+ */
+ @NonNull
+ public CameraAttributes build() {
+ return new CameraAttributes(mAnchorPointInWorldSpace,
+ mAnchorPointInOutputUvSpace,
+ mCameraOrbitYawDegrees,
+ mCameraOrbitPitchDegrees,
+ mDollyDistanceInWorldSpace,
+ mVerticalFovDegrees,
+ mFrustumNearInWorldSpace,
+ mFrustumFarInWorldSpace);
+ }
+ }
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
similarity index 79%
rename from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
rename to core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
index cb602d79..2347746 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2021, The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+package android.app.wallpapereffectsgeneration;
-parcelable NetworkStateSnapshot;
+parcelable CinematicEffectRequest;
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java
new file mode 100644
index 0000000..f2e3313
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectRequest.java
@@ -0,0 +1,115 @@
+/*
+ * 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.app.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/**
+ * A {@link CinematicEffectRequest} is the data class having all the information
+ * passed to wallpaper effects generation service.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CinematicEffectRequest implements Parcelable {
+ /**
+ * Unique id of a cienmatic effect generation task.
+ */
+ @NonNull
+ private String mTaskId;
+
+ /**
+ * The bitmap to generate cinematic effect from.
+ */
+ @NonNull
+ private Bitmap mBitmap;
+
+ private CinematicEffectRequest(Parcel in) {
+ this.mTaskId = in.readString();
+ this.mBitmap = Bitmap.CREATOR.createFromParcel(in);
+ }
+
+ /**
+ * Constructor with task id and bitmap.
+ */
+ public CinematicEffectRequest(@NonNull String taskId, @NonNull Bitmap bitmap) {
+ mTaskId = taskId;
+ mBitmap = bitmap;
+ }
+
+ /**
+ * Returns the task id.
+ */
+ @NonNull
+ public String getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * Returns the bitmap of this request.
+ */
+ @NonNull
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CinematicEffectRequest that = (CinematicEffectRequest) o;
+ return mTaskId.equals(that.mTaskId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeString(mTaskId);
+ mBitmap.writeToParcel(out, flags);
+ }
+
+ @NonNull
+ public static final Creator<CinematicEffectRequest> CREATOR =
+ new Creator<CinematicEffectRequest>() {
+ @Override
+ public CinematicEffectRequest createFromParcel(Parcel in) {
+ return new CinematicEffectRequest(in);
+ }
+
+ @Override
+ public CinematicEffectRequest[] newArray(int size) {
+ return new CinematicEffectRequest[size];
+ }
+ };
+}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
similarity index 79%
copy from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
copy to core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
index cb602d79..1bd1e1e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2021, The Android Open Source Project
+ * 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.
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package android.net;
+ package android.app.wallpapereffectsgeneration;
-parcelable NetworkStateSnapshot;
+ parcelable CinematicEffectResponse;
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java
new file mode 100644
index 0000000..1254794
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/CinematicEffectResponse.java
@@ -0,0 +1,299 @@
+/*
+ * 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.app.wallpapereffectsgeneration;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A {@link CinematicEffectResponse} include textured meshes
+ * and camera attributes of key frames.
+ *
+ * @hide
+ */
+@SystemApi
+public final class CinematicEffectResponse implements Parcelable {
+ /** @hide */
+ @IntDef(prefix = {"CINEMATIC_EFFECT_STATUS_"},
+ value = {CINEMATIC_EFFECT_STATUS_UNKNOWN,
+ CINEMATIC_EFFECT_STATUS_OK,
+ CINEMATIC_EFFECT_STATUS_ERROR,
+ CINEMATIC_EFFECT_STATUS_NOT_READY,
+ CINEMATIC_EFFECT_STATUS_PENDING,
+ CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface CinematicEffectStatusCode {}
+
+ /** Cinematic effect generation unknown status. */
+ public static final int CINEMATIC_EFFECT_STATUS_UNKNOWN = 0;
+ /** Cinematic effect generation success. */
+ public static final int CINEMATIC_EFFECT_STATUS_OK = 1;
+ /** Cinematic effect generation failure. */
+ public static final int CINEMATIC_EFFECT_STATUS_ERROR = 2;
+ /** Service not ready for cinematic effect generation. */
+ public static final int CINEMATIC_EFFECT_STATUS_NOT_READY = 3;
+ /** Cienmatic effect generation process is pending. */
+ public static final int CINEMATIC_EFFECT_STATUS_PENDING = 4;
+ /** Too manay requests for server to handle. */
+ public static final int CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS = 5;
+
+ /** @hide */
+ @IntDef(prefix = {"IMAGE_CONTENT_TYPE_"},
+ value = {IMAGE_CONTENT_TYPE_UNKNOWN,
+ IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT,
+ IMAGE_CONTENT_TYPE_LANDSCAPE,
+ IMAGE_CONTENT_TYPE_OTHER
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface ImageContentType {}
+
+ /** Image content unknown. */
+ public static final int IMAGE_CONTENT_TYPE_UNKNOWN = 0;
+ /** Image content is people portrait. */
+ public static final int IMAGE_CONTENT_TYPE_PEOPLE_PORTRAIT = 1;
+ /** Image content is landscape. */
+ public static final int IMAGE_CONTENT_TYPE_LANDSCAPE = 2;
+ /** Image content is doesn't belong to other types. */
+ public static final int IMAGE_CONTENT_TYPE_OTHER = 3;
+
+
+ @CinematicEffectStatusCode
+ private int mStatusCode;
+
+ /** The id of the cinematic effect generation task. */
+ @NonNull
+ private String mTaskId;
+
+ @ImageContentType
+ private int mImageContentType;
+
+ /** The textured mesh required to render cinematic effect. */
+ @NonNull
+ private List<TexturedMesh> mTexturedMeshes;
+
+ /** The start camera position for animation. */
+ @Nullable
+ private CameraAttributes mStartKeyFrame;
+
+ /** The end camera position for animation. */
+ @Nullable
+ private CameraAttributes mEndKeyFrame;
+
+ private CinematicEffectResponse(Parcel in) {
+ mStatusCode = in.readInt();
+ mTaskId = in.readString();
+ mImageContentType = in.readInt();
+ mTexturedMeshes = new ArrayList<TexturedMesh>();
+ in.readTypedList(mTexturedMeshes, TexturedMesh.CREATOR);
+ mStartKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
+ mEndKeyFrame = in.readTypedObject(CameraAttributes.CREATOR);
+ }
+
+ private CinematicEffectResponse(@CinematicEffectStatusCode int statusCode,
+ String taskId,
+ @ImageContentType int imageContentType,
+ List<TexturedMesh> texturedMeshes,
+ CameraAttributes startKeyFrame,
+ CameraAttributes endKeyFrame) {
+ mStatusCode = statusCode;
+ mTaskId = taskId;
+ mImageContentType = imageContentType;
+ mStartKeyFrame = startKeyFrame;
+ mEndKeyFrame = endKeyFrame;
+ mTexturedMeshes = texturedMeshes;
+ }
+
+ /** Gets the cinematic effect generation status code. */
+ @CinematicEffectStatusCode
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /** Get the task id. */
+ @NonNull
+ public String getTaskId() {
+ return mTaskId;
+ }
+
+ /**
+ * Get the image content type, which briefly classifies what's
+ * the content of image, like people portrait, landscape etc.
+ */
+ @ImageContentType
+ public int getImageContentType() {
+ return mImageContentType;
+ }
+
+ /** Get the textured meshes. */
+ @NonNull
+ public List<TexturedMesh> getTexturedMeshes() {
+ return mTexturedMeshes;
+ }
+
+ /**
+ * Get the camera attributes (position info and other parameters, see docs of
+ * {@link CameraAttributes}) of the start key frame on the animation path.
+ */
+ @Nullable
+ public CameraAttributes getStartKeyFrame() {
+ return mStartKeyFrame;
+ }
+
+ /**
+ * Get the camera attributes (position info and other parameters, see docs of
+ * {@link CameraAttributes}) of the end key frame on the animation path.
+ */
+ @Nullable
+ public CameraAttributes getEndKeyFrame() {
+ return mEndKeyFrame;
+ }
+
+ @NonNull
+ public static final Creator<CinematicEffectResponse> CREATOR =
+ new Creator<CinematicEffectResponse>() {
+ @Override
+ public CinematicEffectResponse createFromParcel(Parcel in) {
+ return new CinematicEffectResponse(in);
+ }
+
+ @Override
+ public CinematicEffectResponse[] newArray(int size) {
+ return new CinematicEffectResponse[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mStatusCode);
+ out.writeString(mTaskId);
+ out.writeInt(mImageContentType);
+ out.writeTypedList(mTexturedMeshes, flags);
+ out.writeTypedObject(mStartKeyFrame, flags);
+ out.writeTypedObject(mEndKeyFrame, flags);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ CinematicEffectResponse that = (CinematicEffectResponse) o;
+ return mTaskId.equals(that.mTaskId);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mTaskId);
+ }
+ /**
+ * Builder of {@link CinematicEffectResponse}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ @CinematicEffectStatusCode
+ private int mStatusCode;
+ @NonNull
+ private String mTaskId;
+ @ImageContentType
+ private int mImageContentType;
+ @NonNull
+ private List<TexturedMesh> mTexturedMeshes;
+ @Nullable
+ private CameraAttributes mStartKeyFrame;
+ @Nullable
+ private CameraAttributes mEndKeyFrame;
+
+ /**
+ * Constructor with task id and status code.
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder(@CinematicEffectStatusCode int statusCode, @NonNull String taskId) {
+ mStatusCode = statusCode;
+ mTaskId = taskId;
+ }
+
+ /**
+ * Sets the image content type.
+ */
+ @NonNull
+ public Builder setImageContentType(@ImageContentType int imageContentType) {
+ mImageContentType = imageContentType;
+ return this;
+ }
+
+
+ /**
+ * Sets the textured meshes.
+ */
+ @NonNull
+ public Builder setTexturedMeshes(@NonNull List<TexturedMesh> texturedMeshes) {
+ mTexturedMeshes = texturedMeshes;
+ return this;
+ }
+
+ /**
+ * Sets start key frame.
+ */
+ @NonNull
+ public Builder setStartKeyFrame(@Nullable CameraAttributes startKeyFrame) {
+ mStartKeyFrame = startKeyFrame;
+ return this;
+ }
+
+ /**
+ * Sets end key frame.
+ */
+ @NonNull
+ public Builder setEndKeyFrame(@Nullable CameraAttributes endKeyFrame) {
+ mEndKeyFrame = endKeyFrame;
+ return this;
+ }
+
+ /**
+ * Builds a {@link CinematicEffectResponse} instance.
+ */
+ @NonNull
+ public CinematicEffectResponse build() {
+ if (mTexturedMeshes == null) {
+ // Place holder because build doesn't allow list to be nullable.
+ mTexturedMeshes = new ArrayList<>(0);
+ }
+ return new CinematicEffectResponse(mStatusCode, mTaskId, mImageContentType,
+ mTexturedMeshes, mStartKeyFrame, mEndKeyFrame);
+ }
+ }
+}
diff --git a/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl
new file mode 100644
index 0000000..c1a698d
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/ICinematicEffectListener.aidl
@@ -0,0 +1,30 @@
+/**
+ * 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.app.wallpapereffectsgeneration;
+
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+
+
+/**
+ * Callback used by system server to notify invoker of {@link WallpaperEffectsGenerationMAnager}
+ * of the cinematic effect generation result.
+ *
+ * @hide
+ */
+oneway interface ICinematicEffectListener {
+ void onCinematicEffectGenerated(in CinematicEffectResponse response);
+}
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl
new file mode 100644
index 0000000..706a89c
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/IWallpaperEffectsGenerationManager.aidl
@@ -0,0 +1,34 @@
+/**
+ * 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.app.wallpapereffectsgeneration;
+
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+
+/**
+ * Used by {@link android.app.wallpapereffectsgeneration.WallpaperEffectsGenerationManager}
+ * to to generate effects.
+ *
+ * @hide
+ */
+oneway interface IWallpaperEffectsGenerationManager {
+ void generateCinematicEffect(in CinematicEffectRequest request,
+ in ICinematicEffectListener listener);
+
+ void returnCinematicEffectResponse(in CinematicEffectResponse response);
+}
\ No newline at end of file
diff --git a/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java
new file mode 100644
index 0000000..630de45
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/TexturedMesh.java
@@ -0,0 +1,273 @@
+/*
+ * 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.app.wallpapereffectsgeneration;
+
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.graphics.Bitmap;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * The textured mesh representation, including a texture (bitmap) to sample from when rendering,
+ * and a mesh consisting of primitives such as triangles. The mesh is represented by an indices
+ * array describing the set of primitives in the mesh, and a vertices array that the indices
+ * refer to.
+ *
+ * @hide
+ */
+@SystemApi
+public final class TexturedMesh implements Parcelable {
+ /**
+ * The texture to sample from when rendering mesh.
+ */
+ @NonNull
+ private Bitmap mBitmap;
+
+ /**
+ * The set of primitives as pointers into the vertices.
+ */
+ @NonNull
+ private int[] mIndices;
+
+ /**
+ * The specific vertices that the indices refer to.
+ */
+ @NonNull
+ private float[] mVertices;
+
+ /** @hide */
+ @IntDef(prefix = {"INDICES_LAYOUT_"}, value = {
+ INDICES_LAYOUT_UNDEFINED,
+ INDICES_LAYOUT_TRIANGLES})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface IndicesLayoutType {
+ }
+
+ /** Undefined indices layout */
+ public static final int INDICES_LAYOUT_UNDEFINED = 0;
+ /**
+ * Indices layout is triangle. Vertices are grouped into 3 and each
+ * group forms a triangle.
+ */
+ public static final int INDICES_LAYOUT_TRIANGLES = 1;
+
+ @IndicesLayoutType
+ private int mIndicesLayoutType;
+
+ /** @hide */
+ @IntDef(prefix = {"VERTICES_LAYOUT_"}, value = {
+ VERTICES_LAYOUT_UNDEFINED,
+ VERTICES_LAYOUT_POSITION3_UV2})
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VerticesLayoutType {
+ }
+
+ /**
+ * Undefined vertices layout.
+ */
+ public static final int VERTICES_LAYOUT_UNDEFINED = 0;
+ /**
+ * The vertices array uses 5 numbers to represent a point, in the format
+ * of [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+ */
+ public static final int VERTICES_LAYOUT_POSITION3_UV2 = 1;
+
+ @VerticesLayoutType
+ private int mVerticesLayoutType;
+
+ private TexturedMesh(Parcel in) {
+ this.mIndicesLayoutType = in.readInt();
+ this.mVerticesLayoutType = in.readInt();
+ this.mBitmap = in.readTypedObject(Bitmap.CREATOR);
+ Parcel data = Parcel.obtain();
+ try {
+ byte[] bytes = in.readBlob();
+ data.unmarshall(bytes, 0, bytes.length);
+ data.setDataPosition(0);
+ this.mIndices = data.createIntArray();
+ this.mVertices = data.createFloatArray();
+ } finally {
+ data.recycle();
+ }
+ }
+
+ private TexturedMesh(@NonNull Bitmap bitmap, @NonNull int[] indices,
+ @NonNull float[] vertices, @IndicesLayoutType int indicesLayoutType,
+ @VerticesLayoutType int verticesLayoutType) {
+ mBitmap = bitmap;
+ mIndices = indices;
+ mVertices = vertices;
+ mIndicesLayoutType = indicesLayoutType;
+ mVerticesLayoutType = verticesLayoutType;
+ }
+
+ /** Get the bitmap, which is the texture to sample from when rendering. */
+ @NonNull
+ public Bitmap getBitmap() {
+ return mBitmap;
+ }
+
+ /**
+ * Get the indices as pointers to the vertices array. Depending on the getIndicesLayoutType(),
+ * the primitives may have different shapes. For example, with INDICES_LAYOUT_TRIANGLES,
+ * indices 0, 1, 2 forms a triangle, indices 3, 4, 5 form another triangle.
+ */
+ @NonNull
+ public int[] getIndices() {
+ return mIndices;
+ }
+
+ /**
+ * Get the vertices that the index array refers to. Depending on the getVerticesLayoutType()
+ * result, the vertices array can represent different per-vertex coordinates. For example,
+ * with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
+ * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+ */
+ @NonNull
+ public float[] getVertices() {
+ return mVertices;
+ }
+
+ /** Get the indices layout type. */
+ @IndicesLayoutType
+ @NonNull
+ public int getIndicesLayoutType() {
+ return mIndicesLayoutType;
+ }
+
+ /** Get the indices layout type. */
+ @VerticesLayoutType
+ @NonNull
+ public int getVerticesLayoutType() {
+ return mVerticesLayoutType;
+ }
+
+ @NonNull
+ public static final Creator<TexturedMesh> CREATOR = new Creator<TexturedMesh>() {
+ @Override
+ public TexturedMesh createFromParcel(Parcel in) {
+ return new TexturedMesh(in);
+ }
+
+ @Override
+ public TexturedMesh[] newArray(int size) {
+ return new TexturedMesh[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(@NonNull Parcel out, int flags) {
+ out.writeInt(mIndicesLayoutType);
+ out.writeInt(mVerticesLayoutType);
+ out.writeTypedObject(mBitmap, flags);
+
+ // Indices and vertices can reach 5MB. Write the data as a Blob,
+ // which will be written to ashmem if too large.
+ Parcel data = Parcel.obtain();
+ try {
+ data.writeIntArray(mIndices);
+ data.writeFloatArray(mVertices);
+ out.writeBlob(data.marshall());
+ } finally {
+ data.recycle();
+ }
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ /**
+ * A builder for {@link TexturedMesh}
+ *
+ * @hide
+ */
+ @SystemApi
+ public static final class Builder {
+ private Bitmap mBitmap;
+ private int[] mIndices;
+ private float[] mVertices;
+ @IndicesLayoutType
+ private int mIndicesLayoutType;
+ @VerticesLayoutType
+ private int mVerticesLayouttype;
+
+ /**
+ * Constructor with bitmap.
+ *
+ * @hide
+ */
+ @SystemApi
+ public Builder(@NonNull Bitmap bitmap) {
+ mBitmap = bitmap;
+ }
+
+ /**
+ * Set the required indices. The indices should represent the primitives. For example,
+ * with INDICES_LAYOUT_TRIANGLES, indices 0, 1, 2 forms a triangle, indices 3, 4, 5
+ * form another triangle.
+ */
+ @NonNull
+ public Builder setIndices(@NonNull int[] indices) {
+ mIndices = indices;
+ return this;
+ }
+
+ /**
+ * Set the required vertices. The vertices array should represent per-vertex coordinates.
+ * For example, with VERTICES_LAYOUT_POSITION3_UV2 type, vertices are in the format of
+ * [x1, y1, z1, u1, v1, x2, y2, z2, u2, v2, ...].
+ *
+ */
+ @NonNull
+ public Builder setVertices(@NonNull float[] vertices) {
+ mVertices = vertices;
+ return this;
+ }
+
+ /**
+ * Set the required indices layout type.
+ */
+ @NonNull
+ public Builder setIndicesLayoutType(@IndicesLayoutType int indicesLayoutType) {
+ mIndicesLayoutType = indicesLayoutType;
+ return this;
+ }
+
+ /**
+ * Set the required vertices layout type.
+ */
+ @NonNull
+ public Builder setVerticesLayoutType(@VerticesLayoutType int verticesLayoutype) {
+ mVerticesLayouttype = verticesLayoutype;
+ return this;
+ }
+
+ /** Builds a TexturedMesh based on the given parameters. */
+ @NonNull
+ public TexturedMesh build() {
+ return new TexturedMesh(mBitmap, mIndices, mVertices, mIndicesLayoutType,
+ mVerticesLayouttype);
+ }
+ }
+}
diff --git a/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java
new file mode 100644
index 0000000..5a1d27d
--- /dev/null
+++ b/core/java/android/app/wallpapereffectsgeneration/WallpaperEffectsGenerationManager.java
@@ -0,0 +1,114 @@
+/*
+ * 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.app.wallpapereffectsgeneration;
+
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.RemoteException;
+
+import java.util.concurrent.Executor;
+
+/**
+ * A {@link WallpaperEffectsGenerationManager} is the class that passes wallpaper effects
+ * generation requests to wallpaper effect generation service. For example, create a cinematic
+ * and render a cinematic live wallpaper with the response.
+ *
+ * Usage:
+ * <pre>{@code
+ * mWallpaperEffectsGenerationManager =
+ * context.getSystemService(WallpaperEffectsGenerationManager.class);
+ * mWallpaperEffectsGenerationManager.
+ * generateCinematicEffect(cinematicEffectRequest, response->{
+ * // proceed cinematic effect response.
+ * });
+ * }</pre>
+ *
+ * @hide
+ */
+@SystemApi
+@SystemService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE)
+public final class WallpaperEffectsGenerationManager {
+ /**
+ * Interface for the cinematic effect listener.
+ */
+ public interface CinematicEffectListener {
+ /**
+ * Async call when the cinematic effect response is generated.
+ * Client needs to check the status code of {@link CinematicEffectResponse}
+ * to determine if the effect generation is successful.
+ *
+ * @param response The generated cinematic effect response.
+ */
+ void onCinematicEffectGenerated(@NonNull CinematicEffectResponse response);
+ }
+
+ private final IWallpaperEffectsGenerationManager mService;
+
+ /** @hide */
+ public WallpaperEffectsGenerationManager(
+ @NonNull IWallpaperEffectsGenerationManager service) {
+ mService = service;
+ }
+
+ /**
+ * Execute a {@link android.app.wallpapereffectsgeneration.CinematicEffectRequest} from
+ * the given parameters to the wallpaper effects generation service. After the cinematic
+ * effect response is ready, the given listener is invoked by the system with the response.
+ * The listener may never receive a callback if unexpected error happened when proceeding
+ * request.
+ *
+ * @param request request to generate cinematic effect.
+ * @param executor where the listener is invoked.
+ * @param listener listener invoked when the cinematic effect response is available.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION)
+ public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull CinematicEffectListener listener) {
+ try {
+ mService.generateCinematicEffect(request,
+ new CinematicEffectListenerWrapper(listener, executor));
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static final class CinematicEffectListenerWrapper
+ extends ICinematicEffectListener.Stub {
+ @NonNull
+ private final CinematicEffectListener mListener;
+ @NonNull
+ private final Executor mExecutor;
+
+ CinematicEffectListenerWrapper(@NonNull CinematicEffectListener listener,
+ @NonNull Executor executor) {
+ mListener = listener;
+ mExecutor = executor;
+ }
+
+ @Override
+ public void onCinematicEffectGenerated(CinematicEffectResponse response) {
+ mExecutor.execute(() -> mListener.onCinematicEffectGenerated(response));
+ }
+ }
+}
diff --git a/core/java/android/attention/AttentionManagerInternal.java b/core/java/android/attention/AttentionManagerInternal.java
index 941e9e2e..4e00da1 100644
--- a/core/java/android/attention/AttentionManagerInternal.java
+++ b/core/java/android/attention/AttentionManagerInternal.java
@@ -46,6 +46,25 @@
*/
public abstract void cancelAttentionCheck(AttentionCallbackInternal callback);
+ /**
+ * Requests the continuous updates of proximity signal via the provided callback,
+ * until the given callback is unregistered. Currently, AttentionManagerService only
+ * anticipates one client and updates one client at a time. If a new client wants to
+ * onboard to receiving Proximity updates, please make a feature request to make proximity
+ * feature multi-client before depending on this feature.
+ *
+ * @param callback a callback that receives the proximity updates
+ * @return {@code true} if the registration should succeed.
+ */
+ public abstract boolean onStartProximityUpdates(ProximityCallbackInternal callback);
+
+ /**
+ * Requests to stop providing continuous updates until the callback is registered.
+ *
+ * @param callback a callback that was used in {@link #onStartProximityUpdates}
+ */
+ public abstract void onStopProximityUpdates(ProximityCallbackInternal callback);
+
/** Internal interface for attention callback. */
public abstract static class AttentionCallbackInternal {
/**
@@ -64,4 +83,13 @@
*/
public abstract void onFailure(int error);
}
+
+ /** Internal interface for proximity callback. */
+ public abstract static class ProximityCallbackInternal {
+ /**
+ * @param distance the estimated distance of the user (in meter)
+ * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
+ */
+ public abstract void onProximityUpdate(double distance);
+ }
}
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/companion/virtual/IVirtualDevice.aidl b/core/java/android/companion/virtual/IVirtualDevice.aidl
index 339e9a2..8fc24fd 100644
--- a/core/java/android/companion/virtual/IVirtualDevice.aidl
+++ b/core/java/android/companion/virtual/IVirtualDevice.aidl
@@ -77,4 +77,7 @@
void launchPendingIntent(
int displayId, in PendingIntent pendingIntent, in ResultReceiver resultReceiver);
PointF getCursorPosition(IBinder token);
+
+ /** Sets whether to show or hide the cursor while this virtual device is active. */
+ void setShowPointerIcon(boolean showPointerIcon);
}
diff --git a/core/java/android/companion/virtual/VirtualDeviceManager.java b/core/java/android/companion/virtual/VirtualDeviceManager.java
index bb9bb09..69033a6 100644
--- a/core/java/android/companion/virtual/VirtualDeviceManager.java
+++ b/core/java/android/companion/virtual/VirtualDeviceManager.java
@@ -338,6 +338,22 @@
}
/**
+ * Sets the visibility of the pointer icon for this VirtualDevice's associated displays.
+ *
+ * @param showPointerIcon True if the pointer should be shown; false otherwise. The default
+ * visibility is true.
+ */
+ @RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
+ @NonNull
+ public void setShowPointerIcon(boolean showPointerIcon) {
+ try {
+ mVirtualDevice.setShowPointerIcon(showPointerIcon);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Returns the display flags that should be added to a particular virtual display.
* Additional device-level flags from {@link
* com.android.server.companion.virtual.VirtualDeviceImpl#getBaseVirtualDisplayFlags()} will
diff --git a/core/java/android/content/AttributionSource.java b/core/java/android/content/AttributionSource.java
index 157e709..3f2fa21 100644
--- a/core/java/android/content/AttributionSource.java
+++ b/core/java/android/content/AttributionSource.java
@@ -155,8 +155,8 @@
this(AttributionSourceState.CREATOR.createFromParcel(in));
// Since we just unpacked this object as part of it transiting a Binder
- // call, this is the perfect time to enforce that its UID can be trusted
- enforceCallingUid();
+ // call, this is the perfect time to enforce that its UID and PID can be trusted
+ enforceCallingUidAndPid();
}
/** @hide */
@@ -259,13 +259,24 @@
}
/**
+ * If you are handling an IPC and you don't trust the caller you need to validate whether the
+ * attribution source is one for the calling app to prevent the caller to pass you a source from
+ * another app without including themselves in the attribution chain.
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ */
+ private void enforceCallingUidAndPid() {
+ enforceCallingUid();
+ enforceCallingPid();
+ }
+
+ /**
* If you are handling an IPC and you don't trust the caller you need to validate
* whether the attribution source is one for the calling app to prevent the caller
* to pass you a source from another app without including themselves in the
* attribution chain.
*
- * @throws SecurityException if the attribution source cannot be trusted to be
- * from the caller.
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
*/
public void enforceCallingUid() {
if (!checkCallingUid()) {
@@ -294,6 +305,33 @@
return true;
}
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @throws SecurityException if the attribution source cannot be trusted to be from the caller.
+ * @hide
+ */
+ @TestApi
+ public void enforceCallingPid() {
+ if (!checkCallingPid()) {
+ throw new SecurityException("Calling pid: " + Binder.getCallingPid()
+ + " doesn't match source pid: " + mAttributionSourceState.pid);
+ }
+ }
+
+ /**
+ * Validate that the pid being claimed for the calling app is not spoofed
+ *
+ * @return if the attribution source cannot be trusted to be from the caller.
+ */
+ private boolean checkCallingPid() {
+ final int callingPid = Binder.getCallingPid();
+ if (mAttributionSourceState.pid != -1 && callingPid != mAttributionSourceState.pid) {
+ return false;
+ }
+ return true;
+ }
+
@Override
public String toString() {
if (Build.IS_DEBUGGABLE) {
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a0864d6..2074125 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,
})
@@ -5031,6 +5032,20 @@
public static final String SOUND_TRIGGER_MIDDLEWARE_SERVICE = "soundtrigger_middleware";
/**
+ * Used for getting the wallpaper effects generation service.
+ *
+ * <p><b>NOTE: </b> this service is optional; callers of
+ * {@code Context.getSystemServiceName(WALLPAPER_EFFECTS_GENERATION_SERVICE)} should check for
+ * {@code null}.
+ *
+ * @hide
+ * @see #getSystemService(String)
+ */
+ @SystemApi
+ public static final String WALLPAPER_EFFECTS_GENERATION_SERVICE =
+ "wallpaper_effects_generation";
+
+ /**
* Used to access {@link MusicRecognitionManagerService}.
*
* @hide
@@ -5956,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.
*
@@ -6496,15 +6504,26 @@
* <li>Each permission in {@code permissions} must be a runtime permission.
* </ul>
* <p>
- * For every permission in {@code permissions}, the entire permission group it belongs to will
- * be revoked. The revocation happens asynchronously and kills all processes running in the
- * calling UID. It will be triggered once it is safe to do so. In particular, it will not be
- * triggered as long as the package remains in the foreground, or has any active manifest
- * components (e.g. when another app is accessing a content provider in the package).
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * The revocation happens asynchronously and kills all processes running in the calling UID. It
+ * will be triggered once it is safe to do so. In particular, it will not be triggered as long
+ * as the package remains in the foreground, or has any active manifest components (e.g. when
+ * another app is accessing a content provider in the package).
* <p>
* If you want to revoke the permissions right away, you could call {@code System.exit()}, but
* this could affect other apps that are accessing your app at the moment. For example, apps
* accessing a content provider in your app will all crash.
+ * <p>
+ * Note that the settings UI shows a permission group as granted as long as at least one
+ * permission in the group is granted. If you want the user to observe the revocation in the
+ * settings, you should revoke every permission in the target group. To learn the current list
+ * of permissions in a group, you may use
+ * {@link PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)} and
+ * {@link PackageManager#getPlatformPermissionsForGroup(String, Executor, Consumer)}. This list
+ * of permissions may evolve over time, so it is recommended to check whether it contains any
+ * permission you wish to retain before trying to revoke an entire group.
*
* @param permissions Collection of permissions to be revoked.
* @see PackageManager#getGroupOfPlatformPermission(String, Executor, Consumer)
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 3e527f8..28bef56 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -5576,6 +5576,7 @@
/**
* A String[] holding attribution tags when used with
* {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * and ACTION_MANAGE_PERMISSION_USAGE
*
* E.g. an attribution tag could be location_provider, com.google.android.gms.*, etc.
*/
@@ -5584,17 +5585,20 @@
/**
* A long representing the start timestamp (epoch time in millis) of the permission usage
* when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * and ACTION_MANAGE_PERMISSION_USAGE
*/
public static final String EXTRA_START_TIME = "android.intent.extra.START_TIME";
/**
* A long representing the end timestamp (epoch time in millis) of the permission usage when
* used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * and ACTION_MANAGE_PERMISSION_USAGE
*/
public static final String EXTRA_END_TIME = "android.intent.extra.END_TIME";
/**
- * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD},
+ * A boolean extra, when used with {@link #ACTION_VIEW_PERMISSION_USAGE_FOR_PERIOD}
+ * and {@link #ACTION_MANAGE_PERMISSION_USAGE},
* that specifies whether the permission usage system UI is showing attribution information
* for the chosen entry.
*
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/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 1c82b38..30aed8b 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -653,6 +653,7 @@
@UnsupportedAppUsage(maxTargetSdk = 30, trackingBug = 170729553)
String getPermissionControllerPackageName();
+ String getSupplementalProcessPackageName();
ParceledListSlice getInstantApps(int userId);
byte[] getInstantAppCookie(String packageName, int userId);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e5c31d7..e9466e9 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;
/**
@@ -3325,7 +3326,8 @@
* <p>This feature should only be defined if {@link #FEATURE_TELEPHONY} has been defined.
*/
@SdkConstant(SdkConstantType.FEATURE)
- public static final String FEATURE_TELEPHONY_RADIO_ACCESS = "android.hardware.telephony.radio";
+ public static final String FEATURE_TELEPHONY_RADIO_ACCESS =
+ "android.hardware.telephony.radio.access";
/**
* Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:
@@ -5691,6 +5693,20 @@
}
/**
+ * Returns the package name of the component implementing supplemental process service.
+ *
+ * @return the package name of the component implementing supplemental process service
+ *
+ * @hide
+ */
+ @NonNull
+ @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public String getSupplementalProcessPackageName() {
+ throw new RuntimeException("Not implemented. Must override in a subclass.");
+ }
+
+ /**
* Add a new dynamic permission to the system. For this to work, your
* package must have defined a permission tree through the
* {@link android.R.styleable#AndroidManifestPermissionTree
diff --git a/core/java/android/hardware/CameraStreamStats.java b/core/java/android/hardware/CameraStreamStats.java
index ed22de8..85890c1 100644
--- a/core/java/android/hardware/CameraStreamStats.java
+++ b/core/java/android/hardware/CameraStreamStats.java
@@ -16,6 +16,7 @@
package android.hardware;
import android.hardware.camera2.params.DynamicRangeProfiles;
+import android.hardware.camera2.CameraMetadata;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
@@ -47,6 +48,7 @@
private float[] mHistogramBins;
private long[] mHistogramCounts;
private int mDynamicRangeProfile;
+ private int mStreamUseCase;
private static final String TAG = "CameraStreamStats";
@@ -63,11 +65,13 @@
mMaxAppBuffers = 0;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
public CameraStreamStats(int width, int height, int format,
int dataSpace, long usage, long requestCount, long errorCount,
- int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile) {
+ int startLatencyMs, int maxHalBuffers, int maxAppBuffers, int dynamicRangeProfile,
+ int streamUseCase) {
mWidth = width;
mHeight = height;
mFormat = format;
@@ -80,6 +84,7 @@
mMaxAppBuffers = maxAppBuffers;
mHistogramType = HISTOGRAM_TYPE_UNKNOWN;
mDynamicRangeProfile = dynamicRangeProfile;
+ mStreamUseCase = streamUseCase;
}
public static final @android.annotation.NonNull Parcelable.Creator<CameraStreamStats> CREATOR =
@@ -126,6 +131,7 @@
dest.writeFloatArray(mHistogramBins);
dest.writeLongArray(mHistogramCounts);
dest.writeInt(mDynamicRangeProfile);
+ dest.writeInt(mStreamUseCase);
}
public void readFromParcel(Parcel in) {
@@ -143,6 +149,7 @@
mHistogramBins = in.createFloatArray();
mHistogramCounts = in.createLongArray();
mDynamicRangeProfile = in.readInt();
+ mStreamUseCase = in.readInt();
}
public int getWidth() {
@@ -200,4 +207,8 @@
public int getDynamicRangeProfile() {
return mDynamicRangeProfile;
}
+
+ public int getStreamUseCase() {
+ return mStreamUseCase;
+ }
}
diff --git a/core/java/android/hardware/OWNERS b/core/java/android/hardware/OWNERS
index 95f13b5..3b6a564 100644
--- a/core/java/android/hardware/OWNERS
+++ b/core/java/android/hardware/OWNERS
@@ -1,3 +1,9 @@
+# Generic
+etalvala@google.com
+jreck@google.com
+michaelwr@google.com
+sumir@google.com
+
# Camera
per-file *Camera*=cychen@google.com,epeev@google.com,etalvala@google.com,shuzhenwang@google.com,yinchiayeh@google.com,zhijunhe@google.com,jchowdhary@google.com
diff --git a/core/java/android/hardware/Sensor.java b/core/java/android/hardware/Sensor.java
index 37cfb49..0d3aaf5 100644
--- a/core/java/android/hardware/Sensor.java
+++ b/core/java/android/hardware/Sensor.java
@@ -726,6 +726,89 @@
public static final String STRING_TYPE_HEAD_TRACKER = "android.sensor.head_tracker";
/**
+ * A constant describing a limited axes accelerometer sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ */
+ public static final int TYPE_ACCELEROMETER_LIMITED_AXES = 38;
+
+ /**
+ * A constant string describing a limited axes accelerometer sensor.
+ *
+ * @see #TYPE_ACCELEROMETER_LIMITED_AXES
+ *
+ */
+ public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES =
+ "android.sensor.accelerometer_limited_axes";
+
+ /**
+ * A constant describing a limited axes gyroscope sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ */
+ public static final int TYPE_GYROSCOPE_LIMITED_AXES = 39;
+
+ /**
+ * A constant string describing a limited axes gyroscope sensor.
+ *
+ * @see #TYPE_GYROSCOPE_LIMITED_AXES
+ *
+ */
+ public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES =
+ "android.sensor.gyroscope_limited_axes";
+
+ /**
+ * A constant describing an uncalibrated limited axes accelerometer sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ */
+ public static final int TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED = 40;
+
+ /**
+ * A constant string describing an uncalibrated limited axes accelerometer sensor.
+ *
+ * @see #TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
+ *
+ */
+ public static final String STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED =
+ "android.sensor.accelerometer_limited_axes_uncalibrated";
+
+ /**
+ * A constant describing an uncalibrated limited axes gyroscope sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ *
+ */
+ public static final int TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED = 41;
+
+ /**
+ * A constant string describing an uncalibrated limited axes gyroscope sensor.
+ *
+ * @see #TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED
+ *
+ */
+ public static final String STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED =
+ "android.sensor.gyroscope_limited_axes_uncalibrated";
+
+ /**
+ * A constant string describing a heading sensor.
+ *
+ * See {@link android.hardware.SensorEvent#values SensorEvent.values} for more details.
+ */
+ public static final int TYPE_HEADING = 42;
+
+ /**
+ * A constant string describing a heading sensor.
+ *
+ * @see #TYPE_HEADING
+ *
+ */
+ public static final String STRING_TYPE_HEADING = "android.sensor.heading";
+
+ /**
* A constant describing all sensor types.
*/
@@ -846,6 +929,11 @@
6, // SENSOR_TYPE_ACCELEROMETER_UNCALIBRATED
1, // SENSOR_TYPE_HINGE_ANGLE
6, // SENSOR_TYPE_HEAD_TRACKER (discontinuity count is excluded)
+ 6, // SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES
+ 6, // SENSOR_TYPE_GYROSCOPE_LIMITED_AXES
+ 9, // SENSOR_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
+ 9, // SENSOR_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED
+ 2, // SENSOR_TYPE_HEADING
};
/**
@@ -1301,6 +1389,21 @@
case TYPE_HEAD_TRACKER:
mStringType = STRING_TYPE_HEAD_TRACKER;
return true;
+ case TYPE_ACCELEROMETER_LIMITED_AXES:
+ mStringType = STRING_TYPE_ACCELEROMETER_LIMITED_AXES;
+ return true;
+ case TYPE_GYROSCOPE_LIMITED_AXES:
+ mStringType = STRING_TYPE_GYROSCOPE_LIMITED_AXES;
+ return true;
+ case TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED:
+ mStringType = STRING_TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED;
+ return true;
+ case TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED:
+ mStringType = STRING_TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED;
+ return true;
+ case TYPE_HEADING:
+ mStringType = STRING_TYPE_HEADING;
+ return true;
default:
return false;
}
diff --git a/core/java/android/hardware/SensorEvent.java b/core/java/android/hardware/SensorEvent.java
index c77c8cc..45d4c09 100644
--- a/core/java/android/hardware/SensorEvent.java
+++ b/core/java/android/hardware/SensorEvent.java
@@ -676,6 +676,127 @@
* <li> values[5] : Z component of Euler vector representing angular velocity</li>
* </ul>
*
+ * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES
+ * Sensor.TYPE_ACCELEROMETER_LIMITED_AXES}:
+ * </h4> Equivalent to TYPE_ACCELEROMETER, but supporting cases where one
+ * or two axes are not supported.
+ *
+ * The last three values represent whether the acceleration value for a
+ * given axis is supported. A value of 1.0 indicates that the axis is
+ * supported, while a value of 0 means it isn't supported. The supported
+ * axes should be determined at build time and these values do not change
+ * during runtime.
+ *
+ * The acceleration values for axes that are not supported are set to 0.
+ *
+ * Similar to {@link android.hardware.Sensor#TYPE_ACCELEROMETER}.
+ *
+ * <ul>
+ * <li> values[0]: Acceleration minus Gx on the x-axis (if supported)</li>
+ * <li> values[1]: Acceleration minus Gy on the y-axis (if supported)</li>
+ * <li> values[2]: Acceleration minus Gz on the z-axis (if supported)</li>
+ * <li> values[3]: Acceleration supported for x-axis</li>
+ * <li> values[4]: Acceleration supported for y-axis</li>
+ * <li> values[5]: Acceleration supported for z-axis</li>
+ * </ul>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_GYROSCOPE_LIMITED_AXES
+ * Sensor.TYPE_GYROSCOPE_LIMITED_AXES}:
+ * </h4> Equivalent to TYPE_GYROSCOPE, but supporting cases where one or two
+ * axes are not supported.
+ *
+ * The last three values represent whether the angular speed value for a
+ * given axis is supported. A value of 1.0 indicates that the axis is
+ * supported, while a value of 0 means it isn't supported. The supported
+ * axes should be determined at build time and these values do not change
+ * during runtime.
+ *
+ * The angular speed values for axes that are not supported are set to 0.
+ *
+ * Similar to {@link android.hardware.Sensor#TYPE_GYROSCOPE}.
+ *
+ * <ul>
+ * <li> values[0]: Angular speed around the x-axis (if supported)</li>
+ * <li> values[1]: Angular speed around the y-axis (if supported)</li>
+ * <li> values[2]: Angular speed around the z-axis (if supported)</li>
+ * <li> values[3]: Angular speed supported for x-axis</li>
+ * <li> values[4]: Angular speed supported for y-axis</li>
+ * <li> values[5]: Angular speed supported for z-axis</li>
+ * </ul>
+ * <p>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED
+ * Sensor.TYPE_ACCELEROMETER_LIMITED_AXES_UNCALIBRATED}:
+ * </h4> Equivalent to TYPE_ACCELEROMETER_UNCALIBRATED, but supporting cases
+ * where one or two axes are not supported.
+ *
+ * The last three values represent whether the acceleration value for a
+ * given axis is supported. A value of 1.0 indicates that the axis is
+ * supported, while a value of 0 means it isn't supported. The supported
+ * axes should be determined at build time and these values do not change
+ * during runtime.
+ *
+ * The acceleration values and bias values for axes that are not supported
+ * are set to 0.
+ *
+ * <ul>
+ * <li> values[0]: x_uncalib without bias compensation (if supported)</li>
+ * <li> values[1]: y_uncalib without bias compensation (if supported)</li>
+ * <li> values[2]: z_uncalib without bias compensation (if supported)</li>
+ * <li> values[3]: estimated x_bias (if supported)</li>
+ * <li> values[4]: estimated y_bias (if supported)</li>
+ * <li> values[5]: estimated z_bias (if supported)</li>
+ * <li> values[6]: Acceleration supported for x-axis</li>
+ * <li> values[7]: Acceleration supported for y-axis</li>
+ * <li> values[8]: Acceleration supported for z-axis</li>
+ * </ul>
+ * </p>
+ *
+ * <h4> {@link android.hardware.Sensor#TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED
+ * Sensor.TYPE_GYROSCOPE_LIMITED_AXES_UNCALIBRATED}:
+ * </h4> Equivalent to TYPE_GYROSCOPE_UNCALIBRATED, but supporting cases
+ * where one or two axes are not supported.
+ *
+ * The last three values represent whether the angular speed value for a
+ * given axis is supported. A value of 1.0 indicates that the axis is
+ * supported, while a value of 0 means it isn't supported. The supported
+ * axes should be determined at build time and these values do not change
+ * during runtime.
+ *
+ * The angular speed values and drift values for axes that are not supported
+ * are set to 0.
+ *
+ * <ul>
+ * <li> values[0]: Angular speed (w/o drift compensation) around the X axis (if supported)</li>
+ * <li> values[1]: Angular speed (w/o drift compensation) around the Y axis (if supported)</li>
+ * <li> values[2]: Angular speed (w/o drift compensation) around the Z axis (if supported)</li>
+ * <li> values[3]: estimated drift around X axis (if supported)</li>
+ * <li> values[4]: estimated drift around Y axis (if supported)</li>
+ * <li> values[5]: estimated drift around Z axis (if supported)</li>
+ * <li> values[6]: Angular speed supported for x-axis</li>
+ * <li> values[7]: Angular speed supported for y-axis</li>
+ * <li> values[8]: Angular speed supported for z-axis</li>
+ * </ul>
+ * </p>
+ *
+ * <h4>{@link android.hardware.Sensor#TYPE_HEADING Sensor.TYPE_HEADING}:</h4>
+ *
+ * A sensor of this type measures the direction in which the device is
+ * pointing relative to true north in degrees. The value must be between
+ * 0.0 (inclusive) and 360.0 (exclusive), with 0 indicating north, 90 east,
+ * 180 south, and 270 west.
+ *
+ * Accuracy is defined at 68% confidence. In the case where the underlying
+ * distribution is assumed Gaussian normal, this would be considered one
+ * standard deviation. For example, if heading returns 60 degrees, and
+ * accuracy returns 10 degrees, then there is a 68 percent probability of
+ * the true heading being between 50 degrees and 70 degrees.
+ *
+ * <ul>
+ * <li> values[0]: Measured heading in degrees.</li>
+ * <li> values[1]: Heading accuracy in degrees.</li>
+ * </ul>
+ *
* @see GeomagneticField
*/
public final float[] values;
diff --git a/core/java/android/hardware/camera2/CameraCharacteristics.java b/core/java/android/hardware/camera2/CameraCharacteristics.java
index d2dc314..a06566c 100644
--- a/core/java/android/hardware/camera2/CameraCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraCharacteristics.java
@@ -657,7 +657,7 @@
*
* @throws IllegalArgumentException if metadataClass is not a subclass of CameraMetadata
*/
- private <TKey> List<TKey>
+ <TKey> List<TKey>
getAvailableKeyList(Class<?> metadataClass, Class<TKey> keyClass, int[] filterTags,
boolean includeSynthetic) {
@@ -1678,6 +1678,9 @@
* with PRIMARY_CAMERA.</p>
* <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
* represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
+ * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is AUTOMOTIVE, then this position is relative to the
+ * origin of the automotive sensor coordinate system, which is at the center of the rear
+ * axle.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
@@ -1824,6 +1827,7 @@
* <li>{@link #LENS_POSE_REFERENCE_PRIMARY_CAMERA PRIMARY_CAMERA}</li>
* <li>{@link #LENS_POSE_REFERENCE_GYROSCOPE GYROSCOPE}</li>
* <li>{@link #LENS_POSE_REFERENCE_UNDEFINED UNDEFINED}</li>
+ * <li>{@link #LENS_POSE_REFERENCE_AUTOMOTIVE AUTOMOTIVE}</li>
* </ul>
*
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
@@ -1834,6 +1838,7 @@
* @see #LENS_POSE_REFERENCE_PRIMARY_CAMERA
* @see #LENS_POSE_REFERENCE_GYROSCOPE
* @see #LENS_POSE_REFERENCE_UNDEFINED
+ * @see #LENS_POSE_REFERENCE_AUTOMOTIVE
*/
@PublicKey
@NonNull
@@ -2214,6 +2219,7 @@
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR ULTRA_HIGH_RESOLUTION_SENSOR}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING REMOSAIC_REPROCESSING}</li>
* <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT DYNAMIC_RANGE_TEN_BIT}</li>
+ * <li>{@link #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE STREAM_USE_CASE}</li>
* </ul>
*
* <p>This key is available on all devices.</p>
@@ -2238,6 +2244,7 @@
* @see #REQUEST_AVAILABLE_CAPABILITIES_ULTRA_HIGH_RESOLUTION_SENSOR
* @see #REQUEST_AVAILABLE_CAPABILITIES_REMOSAIC_REPROCESSING
* @see #REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT
+ * @see #REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE
*/
@PublicKey
@NonNull
@@ -3475,6 +3482,90 @@
new Key<Boolean>("android.scaler.multiResolutionStreamSupported", boolean.class);
/**
+ * <p>The stream use cases supported by this camera device.</p>
+ * <p>The stream use case indicates the purpose of a particular camera stream from
+ * the end-user perspective. Some examples of camera use cases are: preview stream for
+ * live viewfinder shown to the user, still capture for generating high quality photo
+ * capture, video record for encoding the camera output for the purpose of future playback,
+ * and video call for live realtime video conferencing.</p>
+ * <p>With this flag, the camera device can optimize the image processing pipeline
+ * parameters, such as tuning, sensor mode, and ISP settings, indepedent of
+ * the properties of the immediate camera output surface. For example, if the output
+ * surface is a SurfaceTexture, the stream use case flag can be used to indicate whether
+ * the camera frames eventually go to display, video encoder,
+ * still image capture, or all of them combined.</p>
+ * <p>The application sets the use case of a camera stream by calling
+ * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase }.</p>
+ * <p>A camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability must support the following stream use cases:</p>
+ * <ul>
+ * <li>DEFAULT</li>
+ * <li>PREVIEW</li>
+ * <li>STILL_CAPTURE</li>
+ * <li>VIDEO_RECORD</li>
+ * <li>PREVIEW_VIDEO_STILL</li>
+ * <li>VIDEO_CALL</li>
+ * </ul>
+ * <p>The guaranteed stream combinations related to stream use case for a camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability is documented in the camera device
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline}. The
+ * application is strongly recommended to use one of the guaranteed stream combintations.
+ * If the application creates a session with a stream combination not in the guaranteed
+ * list, or with mixed DEFAULT and non-DEFAULT use cases within the same session,
+ * the camera device may ignore some stream use cases due to hardware constraints
+ * and implementation details.</p>
+ * <p>For stream combinations not covered by the stream use case mandatory lists, such as
+ * reprocessable session, constrained high speed session, or RAW stream combinations, the
+ * application should leave stream use cases within the session as DEFAULT.</p>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW PREVIEW}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE STILL_CAPTURE}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD VIDEO_RECORD}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL PREVIEW_VIDEO_STILL}</li>
+ * <li>{@link #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL VIDEO_CALL}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL
+ * @see #SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<int[]> SCALER_AVAILABLE_STREAM_USE_CASES =
+ new Key<int[]>("android.scaler.availableStreamUseCases", int[].class);
+
+ /**
+ * <p>An array of mandatory stream combinations with stream use cases.
+ * This is an app-readable conversion of the mandatory stream combination
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession tables} with
+ * each stream's use case being set.</p>
+ * <p>The array of
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination combinations} is
+ * generated according to the documented
+ * {@link android.hardware.camera2.CameraDevice#createCaptureSession guideline} for a
+ * camera device with
+ * {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability.
+ * The mandatory stream combination array will be {@code null} in case the device doesn't
+ * have {@link android.hardware.camera2.CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE }
+ * capability.</p>
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ */
+ @PublicKey
+ @NonNull
+ @SyntheticKey
+ public static final Key<android.hardware.camera2.params.MandatoryStreamCombination[]> SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS =
+ new Key<android.hardware.camera2.params.MandatoryStreamCombination[]>("android.scaler.mandatoryUseCaseStreamCombinations", android.hardware.camera2.params.MandatoryStreamCombination[].class);
+
+ /**
* <p>The area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.</p>
* <p>This is the rectangle representing the size of the active region of the sensor (i.e.
@@ -5080,6 +5171,135 @@
public static final Key<android.hardware.camera2.params.StreamConfigurationDuration[]> HEIC_AVAILABLE_HEIC_STALL_DURATIONS_MAXIMUM_RESOLUTION =
new Key<android.hardware.camera2.params.StreamConfigurationDuration[]>("android.heic.availableHeicStallDurationsMaximumResolution", android.hardware.camera2.params.StreamConfigurationDuration[].class);
+ /**
+ * <p>The direction of the camera faces relative to the vehicle body frame and the
+ * passenger seats.</p>
+ * <p>This enum defines the lens facing characteristic of the cameras on the automotive
+ * devices with locations {@link CameraCharacteristics#AUTOMOTIVE_LOCATION android.automotive.location} defines. If the system has
+ * FEATURE_AUTOMOTIVE, the camera will have this entry in its static metadata.</p>
+ * <p>When {@link CameraCharacteristics#AUTOMOTIVE_LOCATION android.automotive.location} is INTERIOR, this has one or more INTERIOR_*
+ * values or a single EXTERIOR_* value. When this has more than one INTERIOR_*,
+ * the first value must be the one for the seat closest to the optical axis. If this
+ * contains INTERIOR_OTHER, all other values will be ineffective.</p>
+ * <p>When {@link CameraCharacteristics#AUTOMOTIVE_LOCATION android.automotive.location} is EXTERIOR_* or EXTRA, this has a single
+ * EXTERIOR_* value.</p>
+ * <p>If a camera has INTERIOR_OTHER or EXTERIOR_OTHER, or more than one camera is at the
+ * same location and facing the same direction, their static metadata will list the
+ * following entries, so that applications can determain their lenses' exact facing
+ * directions:</p>
+ * <ul>
+ * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+ * </ul>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_EXTERIOR_OTHER EXTERIOR_OTHER}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_EXTERIOR_FRONT EXTERIOR_FRONT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_EXTERIOR_REAR EXTERIOR_REAR}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_EXTERIOR_LEFT EXTERIOR_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_EXTERIOR_RIGHT EXTERIOR_RIGHT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER INTERIOR_OTHER}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_LEFT INTERIOR_SEAT_ROW_1_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_CENTER INTERIOR_SEAT_ROW_1_CENTER}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_RIGHT INTERIOR_SEAT_ROW_1_RIGHT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_LEFT INTERIOR_SEAT_ROW_2_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_CENTER INTERIOR_SEAT_ROW_2_CENTER}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_RIGHT INTERIOR_SEAT_ROW_2_RIGHT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_LEFT INTERIOR_SEAT_ROW_3_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_CENTER INTERIOR_SEAT_ROW_3_CENTER}</li>
+ * <li>{@link #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_RIGHT INTERIOR_SEAT_ROW_3_RIGHT}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see #AUTOMOTIVE_LENS_FACING_EXTERIOR_OTHER
+ * @see #AUTOMOTIVE_LENS_FACING_EXTERIOR_FRONT
+ * @see #AUTOMOTIVE_LENS_FACING_EXTERIOR_REAR
+ * @see #AUTOMOTIVE_LENS_FACING_EXTERIOR_LEFT
+ * @see #AUTOMOTIVE_LENS_FACING_EXTERIOR_RIGHT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_LEFT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_CENTER
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_RIGHT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_LEFT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_CENTER
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_RIGHT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_LEFT
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_CENTER
+ * @see #AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_RIGHT
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<int[]> AUTOMOTIVE_LENS_FACING =
+ new Key<int[]>("android.automotive.lens.facing", int[].class);
+
+ /**
+ * <p>Location of the cameras on the automotive devices.</p>
+ * <p>This enum defines the locations of the cameras relative to the vehicle body frame on
+ * <a href="https://source.android.com/devices/sensors/sensor-types#auto_axes">the automotive sensor coordinate system</a>.
+ * If the system has FEATURE_AUTOMOTIVE, the camera will have this entry in its static
+ * metadata.</p>
+ * <ul>
+ * <li>INTERIOR is the inside of the vehicle body frame (or the passenger cabin).</li>
+ * <li>EXTERIOR is the outside of the vehicle body frame.</li>
+ * <li>EXTRA is the extra vehicle such as a trailer.</li>
+ * </ul>
+ * <p>Each side of the vehicle body frame on this coordinate system is defined as below:</p>
+ * <ul>
+ * <li>FRONT is where the Y-axis increases toward.</li>
+ * <li>REAR is where the Y-axis decreases toward.</li>
+ * <li>LEFT is where the X-axis decreases toward.</li>
+ * <li>RIGHT is where the X-axis increases toward.</li>
+ * </ul>
+ * <p>If the camera has either EXTERIOR_OTHER or EXTRA_OTHER, its static metadata will list
+ * the following entries, so that applications can determine the camera's exact location:</p>
+ * <ul>
+ * <li>{@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation}</li>
+ * <li>{@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}</li>
+ * </ul>
+ * <p><b>Possible values:</b></p>
+ * <ul>
+ * <li>{@link #AUTOMOTIVE_LOCATION_INTERIOR INTERIOR}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTERIOR_OTHER EXTERIOR_OTHER}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTERIOR_FRONT EXTERIOR_FRONT}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTERIOR_REAR EXTERIOR_REAR}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTERIOR_LEFT EXTERIOR_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT EXTERIOR_RIGHT}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTRA_OTHER EXTRA_OTHER}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTRA_FRONT EXTRA_FRONT}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTRA_REAR EXTRA_REAR}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTRA_LEFT EXTRA_LEFT}</li>
+ * <li>{@link #AUTOMOTIVE_LOCATION_EXTRA_RIGHT EXTRA_RIGHT}</li>
+ * </ul>
+ *
+ * <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see #AUTOMOTIVE_LOCATION_INTERIOR
+ * @see #AUTOMOTIVE_LOCATION_EXTERIOR_OTHER
+ * @see #AUTOMOTIVE_LOCATION_EXTERIOR_FRONT
+ * @see #AUTOMOTIVE_LOCATION_EXTERIOR_REAR
+ * @see #AUTOMOTIVE_LOCATION_EXTERIOR_LEFT
+ * @see #AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT
+ * @see #AUTOMOTIVE_LOCATION_EXTRA_OTHER
+ * @see #AUTOMOTIVE_LOCATION_EXTRA_FRONT
+ * @see #AUTOMOTIVE_LOCATION_EXTRA_REAR
+ * @see #AUTOMOTIVE_LOCATION_EXTRA_LEFT
+ * @see #AUTOMOTIVE_LOCATION_EXTRA_RIGHT
+ */
+ @PublicKey
+ @NonNull
+ public static final Key<Integer> AUTOMOTIVE_LOCATION =
+ new Key<Integer>("android.automotive.location", int.class);
+
/*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
* End generated code
*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
diff --git a/core/java/android/hardware/camera2/CameraDevice.java b/core/java/android/hardware/camera2/CameraDevice.java
index 47eb79d..1a42eaf 100644
--- a/core/java/android/hardware/camera2/CameraDevice.java
+++ b/core/java/android/hardware/camera2/CameraDevice.java
@@ -404,7 +404,10 @@
* (output format)/(surface type), or if the extension is not
* supported, or if any of the output configurations select
* a dynamic range different from
- * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD}
+ * {@link android.hardware.camera2.params.DynamicRangeProfiles#STANDARD},
+ * or if any of the output configurations sets a stream use
+ * case different from {@link
+ * android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT}.
* @see CameraExtensionCharacteristics#getSupportedExtensions
* @see CameraExtensionCharacteristics#getExtensionSupportedSizes
*/
@@ -855,6 +858,31 @@
* will cause a capture session initialization failure.
* </p>
*
+ * <p>Devices with the STREAM_USE_CASE capability ({@link
+ * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES} includes {@link
+ * CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}) support below additional
+ * stream combinations:
+ *
+ * <table>
+ * <tr><th colspan="10">STREAM_USE_CASE capability additional guaranteed configurations</th></tr>
+ * <tr><th colspan="3" id="rb">Target 1</th><th colspan="3" id="rb">Target 2</th><th colspan="3" id="rb">Target 3</th> <th rowspan="2">Sample use case(s)</th> </tr>
+ * <tr><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th><th>Type</th><th id="rb">Max size</th><th>Usecase</th> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple preview or in-app image processing</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video recording or in-app video processing</td> </tr>
+ * <tr> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream for preview, video and still image capture</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td colspan="3" id="rb"></td> <td>Simple video call</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Preview with JPEG or YUV still image capture</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td colspan="3" id="rb"></td> <td>Preview with video recording or in-app video processing</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td colspan="3" id="rb"></td> <td>Preview with in-application image processing</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code VIDEO_CALL}</td> <td colspan="3" id="rb"></td> <td>Preview with video call</td> </tr>
+ * <tr> <td>{@code YUV / PRIV}</td><td id="rb">{@code s1440p}</td><td id="rb">{@code PREVIEW_VIDEO_STILL}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>Multi-purpose stream with JPEG or YUV still capture</td> </tr>
+ * <tr> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>{@code JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td colspan="3" id="rb"></td> <td>YUV and JPEG concurrent still image capture (for testing)</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / PRIV}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code VIDEO_RECORD}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code RECORD}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, video record and JPEG or YUV video snapshot</td> </tr>
+ * <tr> <td>{@code PRIV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV}</td><td id="rb">{@code PREVIEW}</td><td id="rb">{@code PREVIEW}</td> <td>{@code YUV / JPEG}</td><td id="rb">{@code MAXIMUM}</td><td id="rb">{@code STILL_CAPTURE}</td> <td>Preview, in-application image processing, and JPEG or YUV still image capture</td> </tr>
+ * </table><br>
+ * </p>
+ *
* <p>Since the capabilities of camera devices vary greatly, a given camera device may support
* target combinations with sizes outside of these guarantees, but this can only be tested for
* by calling {@link #isSessionConfigurationSupported} or attempting to create a session with
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 5c636c7..aa98f1f 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -30,6 +30,7 @@
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.LatencyRange;
import android.hardware.camera2.extension.SizeList;
+import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.os.ConditionVariable;
@@ -49,6 +50,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
+import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@@ -785,8 +787,8 @@
if (latencyRange != null) {
return new Range(latencyRange.min, latencyRange.max);
}
- }
- } catch (RemoteException e) {
+ }
+ } catch (RemoteException e) {
Log.e(TAG, "Failed to query the extension capture latency! Extension service does"
+ " not respond!");
} finally {
@@ -795,4 +797,142 @@
return null;
}
+
+ /**
+ * Returns the set of keys supported by a {@link CaptureRequest} submitted in a
+ * {@link CameraExtensionSession} with a given extension type.
+ *
+ * <p>The set returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * @param extension the extension type
+ *
+ * @return non-modifiable set of capture keys supported by camera extension session initialized
+ * with the given extension type.
+ * @throws IllegalArgumentException in case of unsupported extension.
+ */
+ @NonNull
+ public Set<CaptureRequest.Key> getAvailableCaptureRequestKeys(@Extension int extension) {
+ long clientId = registerClient(mContext);
+ if (clientId < 0) {
+ throw new IllegalArgumentException("Unsupported extensions");
+ }
+
+ HashSet<CaptureRequest.Key> ret = new HashSet<>();
+
+ try {
+ if (!isExtensionSupported(mCameraId, extension, mChars)) {
+ throw new IllegalArgumentException("Unsupported extension");
+ }
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ CameraMetadataNative captureRequestMeta =
+ extenders.second.getAvailableCaptureRequestKeys();
+
+ if (captureRequestMeta != null) {
+ int[] requestKeys = captureRequestMeta.get(
+ CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS);
+ if (requestKeys == null) {
+ throw new AssertionError("android.request.availableRequestKeys must be non-null"
+ + " in the characteristics");
+ }
+ CameraCharacteristics requestChars = new CameraCharacteristics(captureRequestMeta);
+
+ Object crKey = CaptureRequest.Key.class;
+ Class<CaptureRequest.Key<?>> crKeyTyped = (Class<CaptureRequest.Key<?>>)crKey;
+
+ ret.addAll(requestChars.getAvailableKeyList(CaptureRequest.class, crKeyTyped,
+ requestKeys, /*includeSynthetic*/ false));
+ }
+
+ // Jpeg quality and orientation must always be supported
+ if (!ret.contains(CaptureRequest.JPEG_QUALITY)) {
+ ret.add(CaptureRequest.JPEG_QUALITY);
+ }
+ if (!ret.contains(CaptureRequest.JPEG_ORIENTATION)) {
+ ret.add(CaptureRequest.JPEG_ORIENTATION);
+ }
+ extenders.second.onDeInit();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to query the available capture request keys!");
+ } finally {
+ unregisterClient(clientId);
+ }
+
+ return Collections.unmodifiableSet(ret);
+ }
+
+ /**
+ * Returns the set of keys supported by a {@link CaptureResult} passed as an argument to
+ * {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}.
+ *
+ * <p>The set returned is not modifiable, so any attempts to modify it will throw
+ * a {@code UnsupportedOperationException}.</p>
+ *
+ * <p>In case the set is empty, then the extension is not able to support any capture results
+ * and the {@link CameraExtensionSession.ExtensionCaptureCallback#onCaptureResultAvailable}
+ * callback will not be fired.</p>
+ *
+ * @param extension the extension type
+ *
+ * @return non-modifiable set of capture result keys supported by camera extension session
+ * initialized with the given extension type.
+ * @throws IllegalArgumentException in case of unsupported extension.
+ */
+ @NonNull
+ public Set<CaptureResult.Key> getAvailableCaptureResultKeys(@Extension int extension) {
+ long clientId = registerClient(mContext);
+ if (clientId < 0) {
+ throw new IllegalArgumentException("Unsupported extensions");
+ }
+
+ HashSet<CaptureResult.Key> ret = new HashSet<>();
+ try {
+ if (!isExtensionSupported(mCameraId, extension, mChars)) {
+ throw new IllegalArgumentException("Unsupported extension");
+ }
+
+ Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
+ initializeExtension(extension);
+ extenders.second.onInit(mCameraId, mChars.getNativeMetadata());
+ extenders.second.init(mCameraId, mChars.getNativeMetadata());
+ CameraMetadataNative captureResultMeta =
+ extenders.second.getAvailableCaptureResultKeys();
+
+ if (captureResultMeta != null) {
+ int[] resultKeys = captureResultMeta.get(
+ CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS);
+ if (resultKeys == null) {
+ throw new AssertionError("android.request.availableResultKeys must be non-null "
+ + "in the characteristics");
+ }
+ CameraCharacteristics resultChars = new CameraCharacteristics(captureResultMeta);
+ Object crKey = CaptureResult.Key.class;
+ Class<CaptureResult.Key<?>> crKeyTyped = (Class<CaptureResult.Key<?>>)crKey;
+
+ ret.addAll(resultChars.getAvailableKeyList(CaptureResult.class, crKeyTyped,
+ resultKeys, /*includeSynthetic*/ false));
+
+ // Jpeg quality, orientation and sensor timestamp must always be supported
+ if (!ret.contains(CaptureResult.JPEG_QUALITY)) {
+ ret.add(CaptureResult.JPEG_QUALITY);
+ }
+ if (!ret.contains(CaptureResult.JPEG_ORIENTATION)) {
+ ret.add(CaptureResult.JPEG_ORIENTATION);
+ }
+ if (!ret.contains(CaptureResult.SENSOR_TIMESTAMP)) {
+ ret.add(CaptureResult.SENSOR_TIMESTAMP);
+ }
+ }
+ extenders.second.onDeInit();
+ } catch (RemoteException e) {
+ throw new IllegalStateException("Failed to query the available capture result keys!");
+ } finally {
+ unregisterClient(clientId);
+ }
+
+ return Collections.unmodifiableSet(ret);
+ }
}
diff --git a/core/java/android/hardware/camera2/CameraExtensionSession.java b/core/java/android/hardware/camera2/CameraExtensionSession.java
index 5892f68..ee3441f 100644
--- a/core/java/android/hardware/camera2/CameraExtensionSession.java
+++ b/core/java/android/hardware/camera2/CameraExtensionSession.java
@@ -172,6 +172,32 @@
int sequenceId) {
// default empty implementation
}
+
+ /**
+ * This method is called when an image capture has fully completed and all the
+ * result metadata is available.
+ *
+ * <p>This callback will only be called in case
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys} returns a valid
+ * non-empty list.</p>
+ *
+ * <p>The default implementation of this method does nothing.</p>
+ *
+ * @param session The session received during
+ * {@link StateCallback#onConfigured(CameraExtensionSession)}
+ * @param request The request that was given to the CameraDevice
+ * @param result The total output metadata from the capture, which only includes the
+ * capture result keys advertised as supported in
+ * {@link CameraExtensionCharacteristics#getAvailableCaptureResultKeys}.
+ *
+ * @see #capture
+ * @see #setRepeatingRequest
+ * @see CameraExtensionCharacteristics#getAvailableCaptureResultKeys
+ */
+ public void onCaptureResultAvailable(@NonNull CameraExtensionSession session,
+ @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
+ // default empty implementation
+ }
}
/**
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/camera2/CameraMetadata.java b/core/java/android/hardware/camera2/CameraMetadata.java
index 803684d..40565b0 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -407,6 +407,15 @@
*/
public static final int LENS_POSE_REFERENCE_UNDEFINED = 2;
+ /**
+ * <p>The value of {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation} is relative to the origin of the
+ * automotive sensor coodinate system, which is at the center of the rear axle.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#LENS_POSE_REFERENCE
+ */
+ public static final int LENS_POSE_REFERENCE_AUTOMOTIVE = 3;
+
//
// Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
//
@@ -1210,6 +1219,36 @@
*/
public static final int REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_RANGE_TEN_BIT = 18;
+ /**
+ * <p>The camera device supports selecting a per-stream use case via
+ * {@link android.hardware.camera2.params.OutputConfiguration#setStreamUseCase }
+ * so that the device can optimize camera pipeline parameters such as tuning, sensor
+ * mode, or ISP settings for a specific user scenario.
+ * Some sample usages of this capability are:
+ * * Distinguish high quality YUV captures from a regular YUV stream where
+ * the image quality may not be as good as the JPEG stream, or
+ * * Use one stream to serve multiple purposes: viewfinder, video recording and
+ * still capture. This is common with applications that wish to apply edits equally
+ * to preview, saved images, and saved videos.</p>
+ * <p>This capability requires the camera device to support the following
+ * stream use cases:
+ * * DEFAULT for backward compatibility where the application doesn't set
+ * a stream use case
+ * * PREVIEW for live viewfinder and in-app image analysis
+ * * STILL_CAPTURE for still photo capture
+ * * VIDEO_RECORD for recording video clips
+ * * PREVIEW_VIDEO_STILL for one single stream used for viewfinder, video
+ * recording, and still capture.
+ * * VIDEO_CALL for long running video calls</p>
+ * <p>{@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES }
+ * lists all of the supported stream use cases.</p>
+ * <p>Refer to {@link android.hardware.camera2.CameraDevice#createCaptureSession } for the
+ * mandatory stream combinations involving stream use cases, which can also be queried
+ * via {@link android.hardware.camera2.params.MandatoryStreamCombination }.</p>
+ * @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
+ */
+ public static final int REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE = 19;
+
//
// Enumeration values for CameraCharacteristics#REQUEST_AVAILABLE_DYNAMIC_RANGE_PROFILES_MAP
//
@@ -1336,6 +1375,89 @@
public static final int SCALER_CROPPING_TYPE_FREEFORM = 1;
//
+ // Enumeration values for CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ //
+
+ /**
+ * <p>Default stream use case.</p>
+ * <p>This use case is the same as when the application doesn't set any use case for
+ * the stream. The camera device uses the properties of the output target, such as
+ * format, dataSpace, or surface class type, to optimize the image processing pipeline.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT = 0x0;
+
+ /**
+ * <p>Live stream shown to the user.</p>
+ * <p>Optimized for performance and usability as a viewfinder, but not necessarily for
+ * image quality. The output is not meant to be persisted as saved images or video.</p>
+ * <p>No stall if android.control.<em> are set to FAST; may have stall if android.control.</em>
+ * are set to HIGH_QUALITY. This use case has the same behavior as the default
+ * SurfaceView and SurfaceTexture targets. Additionally, this use case can be used for
+ * in-app image analysis.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW = 0x1;
+
+ /**
+ * <p>Still photo capture.</p>
+ * <p>Optimized for high-quality high-resolution capture, and not expected to maintain
+ * preview-like frame rates.</p>
+ * <p>The stream may have stalls regardless of whether android.control.* is HIGH_QUALITY.
+ * This use case has the same behavior as the default JPEG and RAW related formats.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE = 0x2;
+
+ /**
+ * <p>Recording video clips.</p>
+ * <p>Optimized for high-quality video capture, including high-quality image stabilization
+ * if supported by the device and enabled by the application. As a result, may produce
+ * output frames with a substantial lag from real time, to allow for highest-quality
+ * stabilization or other processing. As such, such an output is not suitable for drawing
+ * to screen directly, and is expected to be persisted to disk or similar for later
+ * playback or processing. Only streams that set the VIDEO_RECORD use case are guaranteed
+ * to have video stabilization applied when the video stabilization control is set
+ * to ON, as opposed to PREVIEW_STABILIZATION.</p>
+ * <p>This use case has the same behavior as the default MediaRecorder and MediaCodec
+ * targets.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD = 0x3;
+
+ /**
+ * <p>One single stream used for combined purposes of preview, video, and still capture.</p>
+ * <p>For such multi-purpose streams, the camera device aims to make the best tradeoff
+ * between the individual use cases. For example, the STILL_CAPTURE use case by itself
+ * may have stalls for achieving best image quality. But if combined with PREVIEW and
+ * VIDEO_RECORD, the camera device needs to trade off the additional image processing
+ * for speed so that preview and video recording aren't slowed down.</p>
+ * <p>Similarly, VIDEO_RECORD may produce frames with a substantial lag, but
+ * PREVIEW_VIDEO_STILL must have minimal output delay. This means that to enable video
+ * stabilization with this use case, the device must support and the app must select the
+ * PREVIEW_STABILIZATION mode for video stabilization.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL = 0x4;
+
+ /**
+ * <p>Long-running video call optimized for both power efficienty and video quality.</p>
+ * <p>The camera sensor may run in a lower-resolution mode to reduce power consumption
+ * at the cost of some image and digital zoom quality. Unlike VIDEO_RECORD, VIDEO_CALL
+ * outputs are expected to work in dark conditions, so are usually accompanied with
+ * variable frame rate settings to allow sufficient exposure time in low light.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL = 0x5;
+
+ /**
+ * <p>Vendor defined use cases. These depend on the vendor implementation.</p>
+ * @see CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES
+ * @hide
+ */
+ public static final int SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START = 0x10000;
+
+ //
// Enumeration values for CameraCharacteristics#SENSOR_INFO_COLOR_FILTER_ARRANGEMENT
//
@@ -1716,6 +1838,191 @@
public static final int LOGICAL_MULTI_CAMERA_SENSOR_SYNC_TYPE_CALIBRATED = 1;
//
+ // Enumeration values for CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ //
+
+ /**
+ * <p>The camera device faces the outside of the vehicle body frame but not exactly
+ * one of the exterior sides defined by this enum. Applications should determine
+ * the exact facing direction from {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and
+ * {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_OTHER = 0;
+
+ /**
+ * <p>The camera device faces the front of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_FRONT = 1;
+
+ /**
+ * <p>The camera device faces the rear of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_REAR = 2;
+
+ /**
+ * <p>The camera device faces the left side of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_LEFT = 3;
+
+ /**
+ * <p>The camera device faces the right side of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_EXTERIOR_RIGHT = 4;
+
+ /**
+ * <p>The camera device faces the inside of the vehicle body frame but not exactly
+ * one of seats described by this enum. Applications should determine the exact
+ * facing direction from {@link CameraCharacteristics#LENS_POSE_ROTATION android.lens.poseRotation} and {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_ROTATION
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_OTHER = 5;
+
+ /**
+ * <p>The camera device faces the left side seat of the first row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_LEFT = 6;
+
+ /**
+ * <p>The camera device faces the center seat of the first row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_CENTER = 7;
+
+ /**
+ * <p>The camera device faces the right seat of the first row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_1_RIGHT = 8;
+
+ /**
+ * <p>The camera device faces the left side seat of the second row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_LEFT = 9;
+
+ /**
+ * <p>The camera device faces the center seat of the second row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_CENTER = 10;
+
+ /**
+ * <p>The camera device faces the right side seat of the second row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_2_RIGHT = 11;
+
+ /**
+ * <p>The camera device faces the left side seat of the third row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_LEFT = 12;
+
+ /**
+ * <p>The camera device faces the center seat of the third row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_CENTER = 13;
+
+ /**
+ * <p>The camera device faces the right seat of the third row.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LENS_FACING
+ */
+ public static final int AUTOMOTIVE_LENS_FACING_INTERIOR_SEAT_ROW_3_RIGHT = 14;
+
+ //
+ // Enumeration values for CameraCharacteristics#AUTOMOTIVE_LOCATION
+ //
+
+ /**
+ * <p>The camera device exists inside of the vehicle cabin.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_INTERIOR = 0;
+
+ /**
+ * <p>The camera exists outside of the vehicle body frame but not exactly on one of the
+ * exterior locations this enum defines. The applications should determine the exact
+ * location from {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTERIOR_OTHER = 1;
+
+ /**
+ * <p>The camera device exists outside of the vehicle body frame and on its front side.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTERIOR_FRONT = 2;
+
+ /**
+ * <p>The camera device exists outside of the vehicle body frame and on its rear side.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTERIOR_REAR = 3;
+
+ /**
+ * <p>The camera device exists outside and on left side of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTERIOR_LEFT = 4;
+
+ /**
+ * <p>The camera device exists outside and on right side of the vehicle body frame.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTERIOR_RIGHT = 5;
+
+ /**
+ * <p>The camera device exists on an extra vehicle, such as the trailer, but not exactly
+ * on one of front, rear, left, or right side. Applications should determine the exact
+ * location from {@link CameraCharacteristics#LENS_POSE_TRANSLATION android.lens.poseTranslation}.</p>
+ *
+ * @see CameraCharacteristics#LENS_POSE_TRANSLATION
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTRA_OTHER = 6;
+
+ /**
+ * <p>The camera device exists outside of the extra vehicle's body frame and on its front
+ * side.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTRA_FRONT = 7;
+
+ /**
+ * <p>The camera device exists outside of the extra vehicle's body frame and on its rear
+ * side.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTRA_REAR = 8;
+
+ /**
+ * <p>The camera device exists outside and on left side of the extra vehicle body.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTRA_LEFT = 9;
+
+ /**
+ * <p>The camera device exists outside and on right side of the extra vehicle body.</p>
+ * @see CameraCharacteristics#AUTOMOTIVE_LOCATION
+ */
+ public static final int AUTOMOTIVE_LOCATION_EXTRA_RIGHT = 10;
+
+ //
// Enumeration values for CaptureRequest#COLOR_CORRECTION_MODE
//
diff --git a/core/java/android/hardware/camera2/CaptureResult.java b/core/java/android/hardware/camera2/CaptureResult.java
index a0fb179..60d5e9e 100644
--- a/core/java/android/hardware/camera2/CaptureResult.java
+++ b/core/java/android/hardware/camera2/CaptureResult.java
@@ -3239,6 +3239,9 @@
* with PRIMARY_CAMERA.</p>
* <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is UNDEFINED, this position cannot be accurately
* represented by the camera device, and will be represented as <code>(0, 0, 0)</code>.</p>
+ * <p>When {@link CameraCharacteristics#LENS_POSE_REFERENCE android.lens.poseReference} is AUTOMOTIVE, then this position is relative to the
+ * origin of the automotive sensor coordinate system, which is at the center of the rear
+ * axle.</p>
* <p><b>Units</b>: Meters</p>
* <p><b>Optional</b> - The value for this key may be {@code null} on some devices.</p>
* <p><b>Permission {@link android.Manifest.permission#CAMERA } is needed to access this property</b></p>
diff --git a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
index 022b084..3c5f5ff 100644
--- a/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/ICaptureProcessorImpl.aidl
@@ -17,6 +17,7 @@
import android.view.Surface;
import android.hardware.camera2.extension.CaptureBundle;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.Size;
/** @hide */
@@ -25,5 +26,5 @@
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
- void process(in List<CaptureBundle> capturelist);
+ void process(in List<CaptureBundle> capturelist, in IProcessResultImpl resultCallback);
}
diff --git a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
index 3ebf637..a8a7866e 100644
--- a/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IImageCaptureExtenderImpl.aidl
@@ -39,4 +39,6 @@
int getMaxCaptureStage();
@nullable List<SizeList> getSupportedResolutions();
LatencyRange getEstimatedCaptureLatencyRange(in Size outputSize);
+ CameraMetadataNative getAvailableCaptureRequestKeys();
+ CameraMetadataNative getAvailableCaptureResultKeys();
}
diff --git a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
index f7e4023..ecd098b 100644
--- a/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
+++ b/core/java/android/hardware/camera2/extension/IPreviewImageProcessorImpl.aidl
@@ -17,6 +17,7 @@
import android.hardware.camera2.impl.CameraMetadataNative;
import android.view.Surface;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.extension.Size;
@@ -26,5 +27,6 @@
void onOutputSurface(in Surface surface, int imageFormat);
void onResolutionUpdate(in Size size);
void onImageFormatUpdate(int imageFormat);
- void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId);
+ void process(in ParcelImage image, in CameraMetadataNative result, int sequenceId,
+ in IProcessResultImpl resultCallback);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
similarity index 65%
copy from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
copy to core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
index cb602d79..4114edb 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/core/java/android/hardware/camera2/extension/IProcessResultImpl.aidl
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2021, The Android Open Source Project
+ * 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.
@@ -13,7 +13,12 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package android.hardware.camera2.extension;
-package android.net;
+import android.hardware.camera2.impl.CameraMetadataNative;
-parcelable NetworkStateSnapshot;
+/** @hide */
+interface IProcessResultImpl
+{
+ void onCaptureCompleted(long shutterTimestamp, in CameraMetadataNative results);
+}
diff --git a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
index 9d2c901..cd392ce 100644
--- a/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraAdvancedExtensionSessionImpl.java
@@ -105,7 +105,7 @@
@RequiresPermission(android.Manifest.permission.CAMERA)
public static CameraAdvancedExtensionSessionImpl createCameraAdvancedExtensionSession(
@NonNull CameraDevice cameraDevice, @NonNull Context ctx,
- @NonNull ExtensionSessionConfiguration config)
+ @NonNull ExtensionSessionConfiguration config, int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {
@@ -135,6 +135,11 @@
throw new IllegalArgumentException("Unsupported dynamic range profile: " +
c.getDynamicRangeProfile());
}
+ if (c.getStreamUseCase() !=
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+ throw new IllegalArgumentException("Unsupported stream use case: " +
+ c.getStreamUseCase());
+ }
}
int suitableSurfaceCount = 0;
diff --git a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
index 2920e67..87553d8 100644
--- a/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraConstrainedHighSpeedCaptureSessionImpl.java
@@ -30,9 +30,11 @@
import android.os.Handler;
import android.os.ConditionVariable;
import android.util.Range;
+import android.util.Log;
import android.view.Surface;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -56,6 +58,7 @@
private final CameraCharacteristics mCharacteristics;
private final CameraCaptureSessionImpl mSessionImpl;
private final ConditionVariable mInitialized = new ConditionVariable();
+ private final String TAG = "CameraConstrainedHighSpeedCaptureSessionImpl";
/**
* Create a new CameraCaptureSession.
@@ -95,10 +98,33 @@
StreamConfigurationMap config = mCharacteristics.get(ck);
SurfaceUtils.checkConstrainedHighSpeedSurfaces(outputSurfaces, fpsRange, config);
- // Request list size: to limit the preview to 30fps, need use maxFps/30; to maximize
- // the preview frame rate, should use maxBatch size for that high speed stream
- // configuration. We choose the former for now.
- int requestListSize = fpsRange.getUpper() / 30;
+ // Check the high speed video fps ranges for video size and find the min value from the list
+ // and assign it to previewFps which will be used to calculate the requestList size.
+ Range<Integer>[] highSpeedFpsRanges = config.getHighSpeedVideoFpsRangesFor(
+ SurfaceUtils.getSurfaceSize(outputSurfaces.iterator().next()));
+ Log.v(TAG, "High speed fps ranges: " + Arrays.toString(highSpeedFpsRanges));
+ int previewFps = Integer.MAX_VALUE;
+ for (Range<Integer> range : highSpeedFpsRanges) {
+ int rangeMin = range.getLower();
+ if (previewFps > rangeMin) {
+ previewFps = rangeMin;
+ }
+ }
+ // Since we only want to support 60fps apart from 30fps, if the min value is not 60,
+ // then continue to calculate the requestList size using value 30.
+ if (previewFps != 60 && previewFps != 30) {
+ Log.w(TAG, "previewFps is neither 60 nor 30.");
+ previewFps = 30;
+ }
+ Log.v(TAG, "previewFps: " + previewFps);
+
+ int requestListSize = fpsRange.getUpper() / previewFps;
+ // If it's a preview, keep requestList size fixed = 1.
+ if (fpsRange.getUpper() > fpsRange.getLower()) {
+ requestListSize = 1;
+ }
+
+ Log.v(TAG, "Request list size is: " + requestListSize);
List<CaptureRequest> requestList = new ArrayList<CaptureRequest>();
// Prepare the Request builders: need carry over the request controls.
diff --git a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
index 9b19fc4..3cb0c93 100644
--- a/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraDeviceImpl.java
@@ -2495,10 +2495,10 @@
if (CameraExtensionCharacteristics.areAdvancedExtensionsSupported()) {
mCurrentAdvancedExtensionSession =
CameraAdvancedExtensionSessionImpl.createCameraAdvancedExtensionSession(
- this, mContext, extensionConfiguration);
+ this, mContext, extensionConfiguration, mNextSessionId++);
} else {
mCurrentExtensionSession = CameraExtensionSessionImpl.createCameraExtensionSession(
- this, mContext, extensionConfiguration);
+ this, mContext, extensionConfiguration, mNextSessionId++);
}
} catch (RemoteException e) {
throw new CameraAccessException(CameraAccessException.CAMERA_ERROR);
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
index bf45932..d148d87 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionForwardProcessor.java
@@ -19,6 +19,7 @@
import android.annotation.SuppressLint;
import android.hardware.camera2.CameraExtensionCharacteristics;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
import android.media.Image;
@@ -114,12 +115,12 @@
}
}
- public void process(ParcelImage image, TotalCaptureResult totalCaptureResult)
- throws RemoteException {
+ public void process(ParcelImage image, TotalCaptureResult totalCaptureResult,
+ IProcessResultImpl resultCallback) throws RemoteException {
if ((mIntermediateSurface != null) && (mIntermediateSurface.isValid()) &&
!mOutputAbandoned) {
mProcessor.process(image, totalCaptureResult.getNativeMetadata(),
- totalCaptureResult.getSequenceId());
+ totalCaptureResult.getSequenceId(), resultCallback);
}
}
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
index 425f22c..1514a2b 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionJpegProcessor.java
@@ -24,6 +24,7 @@
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.media.Image;
import android.media.Image.Plane;
import android.media.ImageReader;
@@ -183,11 +184,13 @@
int cropLeft, int cropTop, int cropRight, int cropBottom,
int rot90);
- public void process(List<CaptureBundle> captureBundle) throws RemoteException {
+ @Override
+ public void process(List<CaptureBundle> captureBundle, IProcessResultImpl captureCallback)
+ throws RemoteException {
JpegParameters jpegParams = getJpegParameters(captureBundle);
try {
mJpegParameters.add(jpegParams);
- mProcessor.process(captureBundle);
+ mProcessor.process(captureBundle, captureCallback);
} catch (Exception e) {
mJpegParameters.remove(jpegParams);
throw e;
diff --git a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
index c8ecfd0..a50db57 100644
--- a/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
+++ b/core/java/android/hardware/camera2/impl/CameraExtensionSessionImpl.java
@@ -36,6 +36,7 @@
import android.hardware.camera2.extension.IImageCaptureExtenderImpl;
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
@@ -67,6 +68,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.Executor;
public final class CameraExtensionSessionImpl extends CameraExtensionSession {
@@ -83,6 +85,10 @@
private final StateCallback mCallbacks;
private final List<Size> mSupportedPreviewSizes;
private final InitializeSessionHandler mInitializeHandler;
+ private final int mSessionId;
+ private final Set<CaptureRequest.Key> mSupportedRequestKeys;
+ private final Set<CaptureResult.Key> mSupportedResultKeys;
+ private boolean mCaptureResultsSupported;
private CameraCaptureSession mCaptureSession = null;
private Surface mCameraRepeatingSurface, mClientRepeatingRequestSurface;
@@ -121,7 +127,8 @@
public static CameraExtensionSessionImpl createCameraExtensionSession(
@NonNull CameraDevice cameraDevice,
@NonNull Context ctx,
- @NonNull ExtensionSessionConfiguration config)
+ @NonNull ExtensionSessionConfiguration config,
+ int sessionId)
throws CameraAccessException, RemoteException {
long clientId = CameraExtensionCharacteristics.registerClient(ctx);
if (clientId < 0) {
@@ -151,6 +158,11 @@
throw new IllegalArgumentException("Unsupported dynamic range profile: " +
c.getDynamicRangeProfile());
}
+ if (c.getStreamUseCase() !=
+ CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT) {
+ throw new IllegalArgumentException("Unsupported stream use case: " +
+ c.getStreamUseCase());
+ }
}
Pair<IPreviewExtenderImpl, IImageCaptureExtenderImpl> extenders =
@@ -197,7 +209,10 @@
repeatingRequestSurface,
burstCaptureSurface,
config.getStateCallback(),
- config.getExecutor());
+ config.getExecutor(),
+ sessionId,
+ extensionChars.getAvailableCaptureRequestKeys(config.getExtension()),
+ extensionChars.getAvailableCaptureResultKeys(config.getExtension()));
session.initialize();
@@ -212,7 +227,10 @@
@Nullable Surface repeatingRequestSurface,
@Nullable Surface burstCaptureSurface,
@NonNull StateCallback callback,
- @NonNull Executor executor) {
+ @NonNull Executor executor,
+ int sessionId,
+ @NonNull Set<CaptureRequest.Key> requestKeys,
+ @Nullable Set<CaptureResult.Key> resultKeys) {
mExtensionClientId = extensionClientId;
mImageExtender = imageExtender;
mPreviewExtender = previewExtender;
@@ -227,6 +245,10 @@
mHandler = new Handler(mHandlerThread.getLooper());
mInitialized = false;
mInitializeHandler = new InitializeSessionHandler();
+ mSessionId = sessionId;
+ mSupportedRequestKeys = requestKeys;
+ mSupportedResultKeys = resultKeys;
+ mCaptureResultsSupported = !resultKeys.isEmpty();
}
private void initializeRepeatingRequestPipeline() throws RemoteException {
@@ -483,7 +505,7 @@
return captureStageList;
}
- private static List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
+ private List<CaptureRequest> createBurstRequest(CameraDevice cameraDevice,
List<CaptureStageImpl> captureStageList, CaptureRequest clientRequest,
Surface target, int captureTemplate, Map<CaptureRequest, Integer> captureMap) {
CaptureRequest.Builder requestBuilder;
@@ -495,16 +517,13 @@
return null;
}
- // This will override the extension capture stage jpeg parameters with the user set
- // jpeg quality and rotation. This will guarantee that client configured jpeg
- // parameters always have highest priority.
- Integer jpegRotation = clientRequest.get(CaptureRequest.JPEG_ORIENTATION);
- if (jpegRotation != null) {
- captureStage.parameters.set(CaptureRequest.JPEG_ORIENTATION, jpegRotation);
- }
- Byte jpegQuality = clientRequest.get(CaptureRequest.JPEG_QUALITY);
- if (jpegQuality != null) {
- captureStage.parameters.set(CaptureRequest.JPEG_QUALITY, jpegQuality);
+ // This will guarantee that client configured
+ // parameters always have the highest priority.
+ for (CaptureRequest.Key requestKey : mSupportedRequestKeys){
+ Object value = clientRequest.get(requestKey);
+ if (value != null) {
+ captureStage.parameters.set(requestKey, value);
+ }
}
requestBuilder.addTarget(target);
@@ -517,10 +536,9 @@
return ret;
}
- private static CaptureRequest createRequest(CameraDevice cameraDevice,
- List<CaptureStageImpl> captureStageList,
- Surface target,
- int captureTemplate) throws CameraAccessException {
+ private CaptureRequest createRequest(CameraDevice cameraDevice,
+ List<CaptureStageImpl> captureStageList, Surface target, int captureTemplate,
+ CaptureRequest clientRequest) throws CameraAccessException {
CaptureRequest.Builder requestBuilder;
requestBuilder = cameraDevice.createCaptureRequest(captureTemplate);
if (target != null) {
@@ -528,14 +546,35 @@
}
CaptureRequest ret = requestBuilder.build();
+ CameraMetadataNative nativeMeta = ret.getNativeMetadata();
for (CaptureStageImpl captureStage : captureStageList) {
if (captureStage != null) {
- CameraMetadataNative.update(ret.getNativeMetadata(), captureStage.parameters);
+ CameraMetadataNative.update(nativeMeta, captureStage.parameters);
}
}
+
+ if (clientRequest != null) {
+ // This will guarantee that client configured
+ // parameters always have the highest priority.
+ for (CaptureRequest.Key requestKey : mSupportedRequestKeys) {
+ Object value = clientRequest.get(requestKey);
+ if (value != null) {
+ nativeMeta.set(requestKey, value);
+ }
+ }
+ }
+
return ret;
}
+ private CaptureRequest createRequest(CameraDevice cameraDevice,
+ List<CaptureStageImpl> captureStageList,
+ Surface target,
+ int captureTemplate) throws CameraAccessException {
+ return createRequest(cameraDevice, captureStageList, target, captureTemplate,
+ /*clientRequest*/ null);
+ }
+
@Override
public int capture(@NonNull CaptureRequest request,
@NonNull Executor executor,
@@ -629,12 +668,17 @@
}
private int setRepeatingRequest(CaptureStageImpl captureStage,
- CameraCaptureSession.CaptureCallback requestHandler)
+ CameraCaptureSession.CaptureCallback requestHandler) throws CameraAccessException {
+ return setRepeatingRequest(captureStage, requestHandler, /*clientRequest*/ null);
+ }
+
+ private int setRepeatingRequest(CaptureStageImpl captureStage,
+ CameraCaptureSession.CaptureCallback requestHandler, CaptureRequest clientRequest)
throws CameraAccessException {
ArrayList<CaptureStageImpl> captureStageList = new ArrayList<>();
captureStageList.add(captureStage);
- CaptureRequest repeatingRequest = createRequest(mCameraDevice,
- captureStageList, mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW);
+ CaptureRequest repeatingRequest = createRequest(mCameraDevice, captureStageList,
+ mCameraRepeatingSurface, CameraDevice.TEMPLATE_PREVIEW, clientRequest);
return mCaptureSession.setSingleRepeatingRequest(repeatingRequest,
new CameraExtensionUtils.HandlerExecutor(mHandler), requestHandler);
}
@@ -843,6 +887,7 @@
private ImageCallback mImageCallback = null;
private boolean mCaptureFailed = false;
+ private CaptureResultHandler mCaptureResultHandler = null;
public BurstRequestHandler(@NonNull CaptureRequest request, @NonNull Executor executor,
@NonNull ExtensionCaptureCallback callbacks,
@@ -963,20 +1008,18 @@
Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
+ if (mCaptureResultsSupported && (mCaptureResultHandler == null)) {
+ mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
+ mCallbacks, result.getSessionId());
+ }
if (mImageProcessor != null) {
if (mCapturePendingMap.indexOfKey(timestamp) >= 0) {
Image img = mCapturePendingMap.get(timestamp).first;
- mCaptureStageMap.put(stageId,
- new Pair<>(img,
- result));
+ mCaptureStageMap.put(stageId, new Pair<>(img, result));
checkAndFireBurstProcessing();
} else {
- mCapturePendingMap.put(timestamp,
- new Pair<>(null,
- stageId));
- mCaptureStageMap.put(stageId,
- new Pair<>(null,
- result));
+ mCapturePendingMap.put(timestamp, new Pair<>(null, stageId));
+ mCaptureStageMap.put(stageId, new Pair<>(null, result));
}
} else {
mCaptureRequestMap.clear();
@@ -986,6 +1029,18 @@
() -> mCallbacks
.onCaptureProcessStarted(CameraExtensionSessionImpl.this,
mClientRequest));
+
+ if (mCaptureResultHandler != null) {
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ for (CaptureResult.Key key : mSupportedResultKeys) {
+ Object value = result.get(key);
+ if (value != null) {
+ captureResults.set(key, value);
+ }
+ }
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ captureResults);
+ }
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -1013,7 +1068,7 @@
List<CaptureBundle> captureList = initializeParcelable(mCaptureStageMap,
jpegOrientation, jpegQuality);
try {
- mImageProcessor.process(captureList);
+ mImageProcessor.process(captureList, mCaptureResultHandler);
} catch (RemoteException e) {
Log.e(TAG, "Failed to process multi-frame request! Extension service "
+ "does not respond!");
@@ -1228,6 +1283,43 @@
}
}
+ private class CaptureResultHandler extends IProcessResultImpl.Stub {
+ private final Executor mExecutor;
+ private final ExtensionCaptureCallback mCallbacks;
+ private final CaptureRequest mClientRequest;
+ private final int mRequestId;
+
+ public CaptureResultHandler(@NonNull CaptureRequest clientRequest,
+ @NonNull Executor executor, @NonNull ExtensionCaptureCallback listener,
+ int requestId) {
+ mClientRequest = clientRequest;
+ mExecutor = executor;
+ mCallbacks = listener;
+ mRequestId = requestId;
+ }
+
+ @Override
+ public void onCaptureCompleted(long shutterTimestamp, CameraMetadataNative result) {
+ if (result == null) {
+ Log.e(TAG,"Invalid capture result!");
+ return;
+ }
+
+ result.set(CaptureResult.SENSOR_TIMESTAMP, shutterTimestamp);
+ TotalCaptureResult totalResult = new TotalCaptureResult(mCameraDevice.getId(), result,
+ mClientRequest, mRequestId, shutterTimestamp, new ArrayList<CaptureResult>(),
+ mSessionId, new PhysicalCaptureResultInfo[0]);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mExecutor.execute(
+ () -> mCallbacks.onCaptureResultAvailable(CameraExtensionSessionImpl.this,
+ mClientRequest, totalResult));
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+ }
+
// This handler can operate in two modes:
// 1) Using valid client callbacks, which means camera buffers will be propagated the
// registered output surfaces and clients will be notified accordingly.
@@ -1242,6 +1334,7 @@
private OnImageAvailableListener mImageCallback = null;
private LongSparseArray<Pair<Image, TotalCaptureResult>> mPendingResultMap =
new LongSparseArray<>();
+ private CaptureResultHandler mCaptureResultHandler = null;
private boolean mRequestUpdatedNeeded = false;
@@ -1375,6 +1468,11 @@
synchronized (mInterfaceLock) {
final Long timestamp = result.get(CaptureResult.SENSOR_TIMESTAMP);
if (timestamp != null) {
+ if (mCaptureResultsSupported && mClientNotificationsEnabled &&
+ (mCaptureResultHandler == null)) {
+ mCaptureResultHandler = new CaptureResultHandler(mClientRequest, mExecutor,
+ mCallbacks, result.getSessionId());
+ }
if (mPreviewProcessorType ==
IPreviewExtenderImpl.PROCESSOR_TYPE_REQUEST_UPDATE_ONLY) {
CaptureStageImpl captureStage = null;
@@ -1387,7 +1485,7 @@
}
if (captureStage != null) {
try {
- setRepeatingRequest(captureStage, this);
+ setRepeatingRequest(captureStage, this, request);
mRequestUpdatedNeeded = true;
} catch (IllegalStateException e) {
// This is possible in case the camera device closes and the
@@ -1406,7 +1504,8 @@
ParcelImage parcelImage = initializeParcelImage(
mPendingResultMap.get(timestamp).first);
try {
- mPreviewImageProcessor.process(parcelImage, result);
+ mPreviewImageProcessor.process(parcelImage, result,
+ mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +
@@ -1444,6 +1543,19 @@
.onCaptureProcessStarted(
CameraExtensionSessionImpl.this,
mClientRequest));
+ if ((mCaptureResultHandler != null) && (mPreviewProcessorType !=
+ IPreviewExtenderImpl.PROCESSOR_TYPE_IMAGE_PROCESSOR)) {
+ CameraMetadataNative captureResults =
+ new CameraMetadataNative();
+ for (CaptureResult.Key key : mSupportedResultKeys) {
+ Object value = result.get(key);
+ if (value != null) {
+ captureResults.set(key, value);
+ }
+ }
+ mCaptureResultHandler.onCaptureCompleted(timestamp,
+ captureResults);
+ }
} else {
mExecutor.execute(
() -> mCallbacks
@@ -1587,7 +1699,7 @@
ParcelImage parcelImage = initializeParcelImage(img);
try {
mPreviewImageProcessor.process(parcelImage,
- mPendingResultMap.get(timestamp).second);
+ mPendingResultMap.get(timestamp).second, mCaptureResultHandler);
} catch (RemoteException e) {
processStatus = false;
Log.e(TAG, "Extension service does not respond during " +
diff --git a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
index 0f8bdf6..9b67633 100644
--- a/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
+++ b/core/java/android/hardware/camera2/impl/CameraMetadataNative.java
@@ -333,6 +333,7 @@
private static final int MANDATORY_STREAM_CONFIGURATIONS_MAX_RESOLUTION = 1;
private static final int MANDATORY_STREAM_CONFIGURATIONS_CONCURRENT = 2;
private static final int MANDATORY_STREAM_CONFIGURATIONS_10BIT = 3;
+ private static final int MANDATORY_STREAM_CONFIGURATIONS_USE_CASE = 4;
private static String translateLocationProviderToProcess(final String provider) {
if (provider == null) {
@@ -700,6 +701,16 @@
});
sGetCommandMap.put(
+ CameraCharacteristics.SCALER_MANDATORY_USE_CASE_STREAM_COMBINATIONS.getNativeKey(),
+ new GetCommand() {
+ @Override
+ @SuppressWarnings("unchecked")
+ public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
+ return (T) metadata.getMandatoryUseCaseStreamCombinations();
+ }
+ });
+
+ sGetCommandMap.put(
CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
@Override
@SuppressWarnings("unchecked")
@@ -1413,6 +1424,9 @@
case MANDATORY_STREAM_CONFIGURATIONS_10BIT:
combs = build.getAvailableMandatory10BitStreamCombinations();
break;
+ case MANDATORY_STREAM_CONFIGURATIONS_USE_CASE:
+ combs = build.getAvailableMandatoryStreamUseCaseCombinations();
+ break;
default:
combs = build.getAvailableMandatoryStreamCombinations();
}
@@ -1446,6 +1460,10 @@
return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_DEFAULT);
}
+ private MandatoryStreamCombination[] getMandatoryUseCaseStreamCombinations() {
+ return getMandatoryStreamCombinationsHelper(MANDATORY_STREAM_CONFIGURATIONS_USE_CASE);
+ }
+
private StreamConfigurationMap getStreamConfigurationMap() {
StreamConfiguration[] configurations = getBase(
CameraCharacteristics.SCALER_AVAILABLE_STREAM_CONFIGURATIONS);
diff --git a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
index 32c15da..0d93c98 100644
--- a/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
+++ b/core/java/android/hardware/camera2/params/MandatoryStreamCombination.java
@@ -26,6 +26,9 @@
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.params.OutputConfiguration;
+import android.hardware.camera2.params.OutputConfiguration.StreamUseCase;
+import android.hardware.camera2.params.StreamConfigurationMap;
import android.hardware.camera2.utils.HashCodeHelpers;
import android.media.CamcorderProfile;
import android.util.Log;
@@ -63,6 +66,7 @@
private final boolean mIsUltraHighResolution;
private final boolean mIsMaximumSize;
private final boolean mIs10BitCapable;
+ private final int mStreamUseCase;
/**
* Create a new {@link MandatoryStreamInformation}.
@@ -141,6 +145,30 @@
public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
boolean is10BitCapable) {
+ this(availableSizes, format, isMaximumSize, isInput, isUltraHighResolution,
+ is10BitCapable, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
+ }
+
+ /**
+ * Create a new {@link MandatoryStreamInformation}.
+ *
+ * @param availableSizes List of possible stream sizes.
+ * @param format Image format.
+ * @param isMaximumSize Whether this is a maximum size stream.
+ * @param isInput Flag indicating whether this stream is input.
+ * @param isUltraHighResolution Flag indicating whether this is a ultra-high resolution
+ * stream.
+ * @param is10BitCapable Flag indicating whether this stream is able to support 10-bit
+ * @param streamUseCase The stream use case.
+ *
+ * @throws IllegalArgumentException
+ * if sizes is empty or if the format was not user-defined in
+ * ImageFormat/PixelFormat.
+ * @hide
+ */
+ public MandatoryStreamInformation(@NonNull List<Size> availableSizes, @Format int format,
+ boolean isMaximumSize, boolean isInput, boolean isUltraHighResolution,
+ boolean is10BitCapable, @StreamUseCase int streamUseCase) {
if (availableSizes.isEmpty()) {
throw new IllegalArgumentException("No available sizes");
}
@@ -150,6 +178,7 @@
mIsInput = isInput;
mIsUltraHighResolution = isUltraHighResolution;
mIs10BitCapable = is10BitCapable;
+ mStreamUseCase = streamUseCase;
}
/**
@@ -272,6 +301,20 @@
}
/**
+ * Retrieve the mandatory stream use case.
+ *
+ * <p>If this {@link MandatoryStreamInformation} is part of a mandatory stream
+ * combination for stream use cases, the return value will be a non-DEFAULT value.
+ * For {@link MandatoryStreamInformation} belonging to other mandatory stream
+ * combinations, the return value will be DEFAULT. </p>
+ *
+ * @return the integer stream use case.
+ */
+ public @StreamUseCase int getStreamUseCase() {
+ return mStreamUseCase;
+ }
+
+ /**
* Check if this {@link MandatoryStreamInformation} is equal to another
* {@link MandatoryStreamInformation}.
*
@@ -292,6 +335,7 @@
final MandatoryStreamInformation other = (MandatoryStreamInformation) obj;
if ((mFormat != other.mFormat) || (mIsInput != other.mIsInput) ||
(mIsUltraHighResolution != other.mIsUltraHighResolution) ||
+ (mStreamUseCase != other.mStreamUseCase) ||
(mAvailableSizes.size() != other.mAvailableSizes.size())) {
return false;
}
@@ -308,7 +352,8 @@
@Override
public int hashCode() {
return HashCodeHelpers.hashCode(mFormat, Boolean.hashCode(mIsInput),
- Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode());
+ Boolean.hashCode(mIsUltraHighResolution), mAvailableSizes.hashCode(),
+ mStreamUseCase);
}
}
@@ -316,6 +361,21 @@
private final boolean mIsReprocessable;
private final ArrayList<MandatoryStreamInformation> mStreamsInformation =
new ArrayList<MandatoryStreamInformation>();
+
+ /**
+ * Short hand for stream use cases
+ */
+ private static final int STREAM_USE_CASE_PREVIEW =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW;
+ private static final int STREAM_USE_CASE_STILL_CAPTURE =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE;
+ private static final int STREAM_USE_CASE_RECORD =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD;
+ private static final int STREAM_USE_CASE_PREVIEW_VIDEO_STILL =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL;
+ private static final int STREAM_USE_CASE_VIDEO_CALL =
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL;
+
/**
* Create a new {@link MandatoryStreamCombination}.
*
@@ -411,15 +471,15 @@
private static final class StreamTemplate {
public int mFormat;
public SizeThreshold mSizeThreshold;
- public boolean mIsInput;
+ public int mStreamUseCase;
public StreamTemplate(int format, SizeThreshold sizeThreshold) {
- this(format, sizeThreshold, /*isInput*/false);
+ this(format, sizeThreshold, CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT);
}
public StreamTemplate(@Format int format, @NonNull SizeThreshold sizeThreshold,
- boolean isInput) {
+ @StreamUseCase int streamUseCase) {
mFormat = format;
mSizeThreshold = sizeThreshold;
- mIsInput = isInput;
+ mStreamUseCase = streamUseCase;
}
}
@@ -1034,6 +1094,174 @@
"High-resolution recording with video snapshot"),
};
+ private static StreamCombinationTemplate sStreamUseCaseCombinations[] = {
+ // Single stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW) },
+ "Simple preview"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW) },
+ "Simple in-application image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD) },
+ "Simple video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD) },
+ "Simple in-application video processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE) },
+ "Simple JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE) },
+ "Simple YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
+ "Multi-purpose stream for preview, video and still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL) },
+ "Multi-purpose YUV stream for preview, video and still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL) },
+ "Simple video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL) },
+ "Simple YUV video call"),
+
+ // 2-stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview with JPEG still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview with YUV still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD)},
+ "Preview with video recording"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD)},
+ "Preview with in-application video processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW)},
+ "Preview with in-application image processing"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL)},
+ "Preview with video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_VIDEO_CALL)},
+ "Preview with YUV video call"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose stream with JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose stream with YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose YUV stream with JPEG still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.s1440p,
+ STREAM_USE_CASE_PREVIEW_VIDEO_STILL),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Multi-purpose YUV stream with YUV still capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_STILL_CAPTURE),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "YUV and JPEG concurrent still image capture (for testing)"),
+
+ // 3-stream combinations
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, video record and JPEG video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, video record and YUV video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application video processing and JPEG video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_RECORD),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.RECORD,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application video processing and YUV video snapshot"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.JPEG, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application image processing, and JPEG still image capture"),
+ new StreamCombinationTemplate(new StreamTemplate [] {
+ new StreamTemplate(ImageFormat.PRIVATE, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.PREVIEW,
+ STREAM_USE_CASE_PREVIEW),
+ new StreamTemplate(ImageFormat.YUV_420_888, SizeThreshold.MAXIMUM,
+ STREAM_USE_CASE_STILL_CAPTURE)},
+ "Preview, in-application image processing, and YUV still image capture"),
+ };
+
/**
* Helper builder class to generate a list of available mandatory stream combinations.
* @hide
@@ -1153,6 +1381,76 @@
}
/**
+ * Retrieve a list of all available mandatory stream combinations with stream use cases.
+ * when the camera device has {@link
+ * CameraMetdata.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE} capability.
+ *
+ * @return a non-modifiable list of supported mandatory stream combinations with stream
+ * use cases. Null in case the device doesn't have {@link
+ * CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE}
+ * capability.
+ */
+ public @NonNull List<MandatoryStreamCombination>
+ getAvailableMandatoryStreamUseCaseCombinations() {
+ if (!isCapabilitySupported(
+ CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_STREAM_USE_CASE)) {
+ return null;
+ }
+
+ HashMap<Pair<SizeThreshold, Integer>, List<Size>> availableSizes =
+ enumerateAvailableSizes();
+ if (availableSizes == null) {
+ Log.e(TAG, "Available size enumeration failed!");
+ return null;
+ }
+
+ ArrayList<MandatoryStreamCombination> availableStreamCombinations = new ArrayList<>();
+ availableStreamCombinations.ensureCapacity(sStreamUseCaseCombinations.length);
+ for (StreamCombinationTemplate combTemplate : sStreamUseCaseCombinations) {
+ ArrayList<MandatoryStreamInformation> streamsInfo =
+ new ArrayList<MandatoryStreamInformation>();
+ streamsInfo.ensureCapacity(combTemplate.mStreamTemplates.length);
+
+ for (StreamTemplate template : combTemplate.mStreamTemplates) {
+ List<Size> sizes = null;
+ Pair<SizeThreshold, Integer> pair;
+ pair = new Pair<SizeThreshold, Integer>(template.mSizeThreshold,
+ new Integer(template.mFormat));
+ sizes = availableSizes.get(pair);
+
+ MandatoryStreamInformation streamInfo;
+ boolean isMaximumSize =
+ (template.mSizeThreshold == SizeThreshold.MAXIMUM);
+ try {
+ streamInfo = new MandatoryStreamInformation(sizes, template.mFormat,
+ isMaximumSize, /*isInput*/false, /*isUltraHighResolution*/false,
+ /*is10BitCapable*/ false, template.mStreamUseCase);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No available sizes found for format: " + template.mFormat +
+ " size threshold: " + template.mSizeThreshold + " combination: " +
+ combTemplate.mDescription);
+ return null;
+ }
+ streamsInfo.add(streamInfo);
+ }
+
+ MandatoryStreamCombination streamCombination;
+ try {
+ streamCombination = new MandatoryStreamCombination(streamsInfo,
+ combTemplate.mDescription, /*isReprocessable*/ false);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "No stream information for mandatory combination: "
+ + combTemplate.mDescription);
+ return null;
+ }
+
+ availableStreamCombinations.add(streamCombination);
+ }
+
+ return Collections.unmodifiableList(availableStreamCombinations);
+ }
+
+ /**
* Retrieve a list of all available mandatory concurrent stream combinations.
* This method should only be called for devices which are listed in combinations returned
* by CameraManager.getConcurrentCameraIds.
@@ -1632,6 +1930,8 @@
Size recordingMaxSize = new Size(0, 0);
Size previewMaxSize = new Size(0, 0);
Size vgaSize = new Size(640, 480);
+ Size s720pSize = new Size(1280, 720);
+ Size s1440pSize = new Size(1920, 1440);
// For external camera, or hidden physical camera, CamcorderProfile may not be
// available, so get maximum recording size using stream configuration map.
if (isExternalCamera() || mIsHiddenPhysicalCamera) {
@@ -1682,6 +1982,12 @@
pair = new Pair<SizeThreshold, Integer>(SizeThreshold.MAXIMUM, intFormat);
availableSizes.put(pair, Arrays.asList(sizes));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s720p, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, s720pSize));
+
+ pair = new Pair<SizeThreshold, Integer>(SizeThreshold.s1440p, intFormat);
+ availableSizes.put(pair, getSizesWithinBound(sizes, s1440pSize));
}
return availableSizes;
diff --git a/core/java/android/hardware/camera2/params/OutputConfiguration.java b/core/java/android/hardware/camera2/params/OutputConfiguration.java
index f2b881b..d8295c9 100644
--- a/core/java/android/hardware/camera2/params/OutputConfiguration.java
+++ b/core/java/android/hardware/camera2/params/OutputConfiguration.java
@@ -159,6 +159,17 @@
CameraMetadata.SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION})
public @interface SensorPixelMode {};
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(prefix = {"STREAM_USE_CASE_"}, value =
+ {CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_STILL_CAPTURE,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_RECORD,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL,
+ CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL})
+ public @interface StreamUseCase {};
+
/**
* Create a new {@link OutputConfiguration} instance with a {@link Surface}.
*
@@ -355,6 +366,7 @@
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
/**
@@ -453,6 +465,7 @@
mIsMultiResolution = false;
mSensorPixelModesUsed = new ArrayList<Integer>();
mDynamicRangeProfile = DynamicRangeProfiles.STANDARD;
+ mStreamUseCase = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT;
}
/**
@@ -730,6 +743,73 @@
}
/**
+ * Set stream use case for this OutputConfiguration
+ *
+ * <p>Stream use case is used to describe the purpose of the stream, whether it's for live
+ * preview, still image capture, video recording, or their combinations. This flag is useful
+ * for scenarios where the immediate consumer target isn't sufficient to indicate the stream's
+ * usage.</p>
+ *
+ * <p>The main difference beteween stream use case and capture intent is that the former
+ * enables the camera device to optimize camera hardware and software pipelines based on user
+ * scenarios for each stream, whereas the latter is mainly a hint to camera to decide
+ * optimal 3A strategy that's applicable to the whole session. The camera device carries out
+ * configurations such as selecting tuning parameters, choosing camera sensor mode, and
+ * constructing image processing pipeline based on the streams's use cases. Capture intents are
+ * then used to fine tune 3A behaviors such as adjusting AE/AF convergence speed, and capture
+ * intents may change during the lifetime of a session. For example, for a session with a
+ * PREVIEW_VIDEO_STILL use case stream and a STILL_CAPTURE use case stream, the capture intents
+ * may be PREVIEW with fast 3A convergence speed and flash metering with automatic control for
+ * live preview, STILL_CAPTURE with best 3A parameters for still photo capture, or VIDEO_RECORD
+ * with slower 3A convergence speed for better video playback experience.</p>
+ *
+ * <p>The supported stream use cases supported by a camera device can be queried by
+ * {@link android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES}.</p>
+ *
+ * <p>The mandatory stream combinations involving stream use cases can be found at {@link
+ * android.hardware.camera2.CameraDevice#createCaptureSession}, as well as queried via
+ * {@link android.hardware.camera2.params.MandatoryStreamCombination}. The application is
+ * strongly recommended to select one of the guaranteed stream combinations where all streams'
+ * use cases are set to non-DEFAULT values. If the application chooses a stream combination
+ * not in the mandatory list, the camera device may ignore some use case flags due to
+ * hardware constraints or implementation details.</p>
+ *
+ * <p>This function must be called before {@link CameraDevice#createCaptureSession} or {@link
+ * CameraDevice#createCaptureSessionByOutputConfigurations}. Calling this function after
+ * {@link CameraDevice#createCaptureSession} or
+ * {@link CameraDevice#createCaptureSessionByOutputConfigurations} has no effect to the camera
+ * session.</p>
+ *
+ * @param streamUseCase The stream use case to be set.
+ *
+ * @throws IllegalArgumentException If the streamUseCase isn't within the range of valid
+ * values.
+ */
+ public void setStreamUseCase(@StreamUseCase int streamUseCase) {
+ // Verify that the value is in range
+ int maxUseCaseValue = CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL;
+ if (streamUseCase > maxUseCaseValue &&
+ streamUseCase < CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VENDOR_START) {
+ throw new IllegalArgumentException("Not a valid stream use case value " +
+ streamUseCase);
+ }
+
+ mStreamUseCase = streamUseCase;
+ }
+
+ /**
+ * Get the current stream use case
+ *
+ * <p>If no {@link #setStreamUseCase} is called first, this function returns
+ * {@link CameraCharacteristics#SCALER_AVAILABLE_STREAM_USE_CASES_DEFAULT DEFAULT}.</p>
+ *
+ * @return the currently set stream use case
+ */
+ public int getStreamUseCase() {
+ return mStreamUseCase;
+ }
+
+ /**
* Create a new {@link OutputConfiguration} instance with another {@link OutputConfiguration}
* instance.
*
@@ -756,6 +836,7 @@
this.mIsMultiResolution = other.mIsMultiResolution;
this.mSensorPixelModesUsed = other.mSensorPixelModesUsed;
this.mDynamicRangeProfile = other.mDynamicRangeProfile;
+ this.mStreamUseCase = other.mStreamUseCase;
}
/**
@@ -774,6 +855,8 @@
String physicalCameraId = source.readString();
boolean isMultiResolutionOutput = source.readInt() == 1;
int[] sensorPixelModesUsed = source.createIntArray();
+ int streamUseCase = source.readInt();
+
checkArgumentInRange(rotation, ROTATION_0, ROTATION_270, "Rotation constant");
int dynamicRangeProfile = source.readInt();
DynamicRangeProfiles.checkProfileValue(dynamicRangeProfile);
@@ -801,6 +884,7 @@
mIsMultiResolution = isMultiResolutionOutput;
mSensorPixelModesUsed = convertIntArrayToIntegerList(sensorPixelModesUsed);
mDynamicRangeProfile = dynamicRangeProfile;
+ mStreamUseCase = streamUseCase;
}
/**
@@ -917,6 +1001,7 @@
// writeList doesn't seem to work well with Integer list.
dest.writeIntArray(convertIntegerToIntList(mSensorPixelModesUsed));
dest.writeInt(mDynamicRangeProfile);
+ dest.writeInt(mStreamUseCase);
}
/**
@@ -947,7 +1032,8 @@
mConfiguredDataspace != other.mConfiguredDataspace ||
mConfiguredGenerationId != other.mConfiguredGenerationId ||
!Objects.equals(mPhysicalCameraId, other.mPhysicalCameraId) ||
- mIsMultiResolution != other.mIsMultiResolution)
+ mIsMultiResolution != other.mIsMultiResolution ||
+ mStreamUseCase != other.mStreamUseCase)
return false;
if (mSensorPixelModesUsed.size() != other.mSensorPixelModesUsed.size()) {
return false;
@@ -985,7 +1071,7 @@
mSurfaceGroupId, mSurfaceType, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
- mDynamicRangeProfile);
+ mDynamicRangeProfile, mStreamUseCase);
}
return HashCodeHelpers.hashCode(
@@ -994,7 +1080,7 @@
mConfiguredDataspace, mSurfaceGroupId, mIsShared ? 1 : 0,
mPhysicalCameraId == null ? 0 : mPhysicalCameraId.hashCode(),
mIsMultiResolution ? 1 : 0, mSensorPixelModesUsed.hashCode(),
- mDynamicRangeProfile);
+ mDynamicRangeProfile, mStreamUseCase);
}
private static final String TAG = "OutputConfiguration";
@@ -1028,4 +1114,6 @@
private ArrayList<Integer> mSensorPixelModesUsed;
// Dynamic range profile
private int mDynamicRangeProfile;
+ // Stream use case
+ private int mStreamUseCase;
}
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/hardware/hdmi/HdmiClient.java b/core/java/android/hardware/hdmi/HdmiClient.java
index cee6a5b..b96e4f8 100644
--- a/core/java/android/hardware/hdmi/HdmiClient.java
+++ b/core/java/android/hardware/hdmi/HdmiClient.java
@@ -21,6 +21,8 @@
public abstract class HdmiClient {
private static final String TAG = "HdmiClient";
+ private static final int UNKNOWN_VENDOR_ID = 0xFFFFFF;
+
/* package */ final IHdmiControlService mService;
private IHdmiVendorCommandListener mIHdmiVendorCommandListener;
@@ -156,11 +158,25 @@
}
/**
- * Sets a listener used to receive incoming vendor-specific command.
+ * Sets a listener used to receive incoming vendor-specific command. This listener will only
+ * receive {@code <Vendor Command>} but will not receive any {@code <Vendor Command with ID>}
+ * messages.
*
* @param listener listener object
*/
public void setVendorCommandListener(@NonNull VendorCommandListener listener) {
+ // Set the vendor ID to INVALID_VENDOR_ID.
+ setVendorCommandListener(listener, UNKNOWN_VENDOR_ID);
+ }
+
+ /**
+ * Sets a listener used to receive incoming vendor-specific command.
+ *
+ * @param listener listener object
+ * @param vendorId The listener is interested in {@code <Vendor Command with ID>} received with
+ * this vendorId and all {@code <Vendor Command>} messages.
+ */
+ public void setVendorCommandListener(@NonNull VendorCommandListener listener, int vendorId) {
if (listener == null) {
throw new IllegalArgumentException("listener cannot be null");
}
@@ -169,7 +185,7 @@
}
try {
IHdmiVendorCommandListener wrappedListener = getListenerWrapper(listener);
- mService.addVendorCommandListener(wrappedListener, getDeviceType());
+ mService.addVendorCommandListener(wrappedListener, vendorId);
mIHdmiVendorCommandListener = wrappedListener;
} catch (RemoteException e) {
Log.e(TAG, "failed to set vendor command listener: ", e);
diff --git a/core/java/android/hardware/hdmi/HdmiControlManager.java b/core/java/android/hardware/hdmi/HdmiControlManager.java
index ec55e12..9235ba1 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -564,6 +564,32 @@
@Retention(RetentionPolicy.SOURCE)
public @interface TvSendStandbyOnSleep {}
+ // -- Whether a playback device should act on an incoming {@code <Set Menu Language>} message.
+ /**
+ * Confirmation dialog should be shown upon receiving the CEC message.
+ *
+ * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+ * @hide
+ */
+ public static final int SET_MENU_LANGUAGE_ENABLED = 1;
+ /**
+ * The message should be ignored.
+ *
+ * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+ * @hide
+ */
+ public static final int SET_MENU_LANGUAGE_DISABLED = 0;
+ /**
+ * @see HdmiControlManager#CEC_SETTING_NAME_SET_MENU_LANGUAGE
+ * @hide
+ */
+ @IntDef(prefix = { "SET_MENU_LANGUAGE_" }, value = {
+ SET_MENU_LANGUAGE_ENABLED,
+ SET_MENU_LANGUAGE_DISABLED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface SetMenuLanguage {}
+
// -- The RC profile of a TV panel.
/**
* RC profile none.
@@ -818,6 +844,13 @@
public static final String CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP =
"tv_send_standby_on_sleep";
/**
+ * Name of a setting deciding whether {@code <Set Menu Language>} message should be
+ * handled by the framework or ignored.
+ *
+ * @hide
+ */
+ public static final String CEC_SETTING_NAME_SET_MENU_LANGUAGE = "set_menu_language";
+ /**
* Name of a setting representing the RC profile of a TV panel.
*
* @hide
@@ -983,6 +1016,7 @@
CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ CEC_SETTING_NAME_SET_MENU_LANGUAGE,
CEC_SETTING_NAME_RC_PROFILE_TV,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
diff --git a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
index 16adee9..818554d 100644
--- a/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
+++ b/core/java/android/hardware/hdmi/HdmiControlServiceWrapper.java
@@ -221,8 +221,8 @@
}
@Override
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- HdmiControlServiceWrapper.this.addVendorCommandListener(listener, deviceType);
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ HdmiControlServiceWrapper.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -481,7 +481,7 @@
boolean hasVendorId) {}
/** @hide */
- public void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {}
+ public void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {}
/** @hide */
public void sendStandby(int deviceType, int deviceId) {}
diff --git a/core/java/android/hardware/hdmi/IHdmiControlService.aidl b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
index 6613397..35dd9ed 100644
--- a/core/java/android/hardware/hdmi/IHdmiControlService.aidl
+++ b/core/java/android/hardware/hdmi/IHdmiControlService.aidl
@@ -76,7 +76,7 @@
void askRemoteDeviceToBecomeActiveSource(int physicalAddress);
void sendVendorCommand(int deviceType, int targetAddress, in byte[] params,
boolean hasVendorId);
- void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType);
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId);
void sendStandby(int deviceType, int deviceId);
void setHdmiRecordListener(IHdmiRecordListener callback);
void startOneTouchRecord(int recorderAddress, in byte[] recordSource);
diff --git a/core/java/android/hardware/input/IInputManager.aidl b/core/java/android/hardware/input/IInputManager.aidl
index 27403ec..e1ffd4a 100644
--- a/core/java/android/hardware/input/IInputManager.aidl
+++ b/core/java/android/hardware/input/IInputManager.aidl
@@ -112,7 +112,7 @@
oneway void requestPointerCapture(IBinder inputChannelToken, boolean enabled);
/** Create an input monitor for gestures. */
- InputMonitor monitorGestureInput(String name, int displayId);
+ InputMonitor monitorGestureInput(IBinder token, String name, int displayId);
// Add a runtime association between the input port and the display port. This overrides any
// static associations.
diff --git a/core/java/android/hardware/input/InputManager.java b/core/java/android/hardware/input/InputManager.java
index 979e9dd..2fd79cf 100644
--- a/core/java/android/hardware/input/InputManager.java
+++ b/core/java/android/hardware/input/InputManager.java
@@ -35,6 +35,7 @@
import android.hardware.lights.LightState;
import android.hardware.lights.LightsManager;
import android.hardware.lights.LightsRequest;
+import android.os.Binder;
import android.os.BlockUntrustedTouchesMode;
import android.os.Build;
import android.os.CombinedVibration;
@@ -1211,7 +1212,7 @@
*/
public InputMonitor monitorGestureInput(String name, int displayId) {
try {
- return mIm.monitorGestureInput(name, displayId);
+ return mIm.monitorGestureInput(new Binder(), name, displayId);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
diff --git a/core/java/android/hardware/input/InputManagerInternal.java b/core/java/android/hardware/input/InputManagerInternal.java
index cc9aeab..3f20002 100644
--- a/core/java/android/hardware/input/InputManagerInternal.java
+++ b/core/java/android/hardware/input/InputManagerInternal.java
@@ -87,6 +87,12 @@
*/
public abstract void setVirtualMousePointerDisplayId(int pointerDisplayId);
+ /**
+ * Gets the display id that the MouseCursorController is being forced to target. Returns
+ * {@link android.view.Display#INVALID_DISPLAY} if there is no override
+ */
+ public abstract int getVirtualMousePointerDisplayId();
+
/** Gets the current position of the mouse cursor. */
public abstract PointF getCursorPosition();
@@ -94,7 +100,7 @@
* Sets the pointer acceleration.
* See {@code frameworks/native/include/input/VelocityControl.h#VelocityControlParameters}.
*/
- public abstract void setPointerAcceleration(float acceleration);
+ public abstract void setPointerAcceleration(float acceleration, int displayId);
/**
* Sets the eligibility of windows on a given display for pointer capture. If a display is
@@ -103,6 +109,9 @@
*/
public abstract void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible);
+ /** Sets the visibility of the cursor. */
+ public abstract void setPointerIconVisible(boolean visible, int displayId);
+
/** Registers the {@link LidSwitchCallback} to begin receiving notifications. */
public abstract void registerLidSwitchCallback(@NonNull LidSwitchCallback callbacks);
diff --git a/core/java/android/hardware/lights/Light.java b/core/java/android/hardware/lights/Light.java
index dbe7a41..c311379 100644
--- a/core/java/android/hardware/lights/Light.java
+++ b/core/java/android/hardware/lights/Light.java
@@ -38,6 +38,12 @@
/** Type for lights that indicate microphone usage */
public static final int LIGHT_TYPE_MICROPHONE = 8;
+ /** Type for lights that indicate camera usage
+ *
+ * @hide
+ */
+ public static final int LIGHT_TYPE_CAMERA = 9;
+
// These enum values start from 10001 to avoid collision with expanding of HAL light types.
/**
* Type for lights that indicate a monochrome color LED light.
diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java
index fc2fbc3..b6e1b1f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -839,10 +839,9 @@
final boolean wasVisible = isInputViewShown();
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.hideSoftInput");
- applyVisibilityInInsetsConsumerIfNecessary(false /* setVisible */);
mShowInputFlags = 0;
mShowInputRequested = false;
- doHideWindow();
+ hideWindow();
final boolean isVisible = isInputViewShown();
final boolean visibilityChanged = isVisible != wasVisible;
if (resultReceiver != null) {
@@ -891,7 +890,6 @@
final boolean wasVisible = isInputViewShown();
if (dispatchOnShowInputRequested(flags, false)) {
showWindow(true);
- applyVisibilityInInsetsConsumerIfNecessary(true /* setVisible */);
}
setImeWindowStatus(mapToImeWindowStatus(), mBackDisposition);
@@ -1666,7 +1664,7 @@
onDisplayCompletions(completions);
}
} else {
- doHideWindow();
+ hideWindow();
}
} else if (mCandidatesVisibility == View.VISIBLE) {
// If the candidates are currently visible, make sure the
@@ -1674,7 +1672,7 @@
showWindow(false);
} else {
// Otherwise hide the window.
- doHideWindow();
+ hideWindow();
}
// If user uses hard keyboard, IME button should always be shown.
boolean showing = onEvaluateInputViewShown();
@@ -2119,7 +2117,7 @@
if (shown) {
showWindow(false);
} else {
- doHideWindow();
+ hideWindow();
}
}
}
@@ -2540,12 +2538,11 @@
mWindowVisible = true;
// request draw for the IME surface.
- // When IME is not pre-rendered, this will actually show the IME.
- if ((previousImeWindowStatus & IME_ACTIVE) == 0) {
- if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
- mWindow.show();
- }
+ if (DEBUG) Log.v(TAG, "showWindow: draw decorView!");
+ mWindow.show();
mDecorViewWasVisible = true;
+ applyVisibilityInInsetsConsumerIfNecessary(true);
+ cancelImeSurfaceRemoval();
mInShowWindow = false;
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
@@ -2602,9 +2599,6 @@
ImeTracing.getInstance().triggerServiceDump(
"InputMethodService#applyVisibilityInInsetsConsumerIfNecessary", mDumper,
null /* icProto */);
- if (setVisible) {
- cancelImeSurfaceRemoval();
- }
mPrivOps.applyImeVisibilityAsync(setVisible
? mCurShowInputToken : mCurHideInputToken, setVisible);
}
@@ -2622,15 +2616,12 @@
mCandidatesViewStarted = false;
}
- private void doHideWindow() {
- setImeWindowStatus(0, mBackDisposition);
- hideWindow();
- }
-
public void hideWindow() {
if (DEBUG) Log.v(TAG, "CALL: hideWindow");
ImeTracing.getInstance().triggerServiceDump("InputMethodService#hideWindow", mDumper,
null /* icProto */);
+ setImeWindowStatus(0, mBackDisposition);
+ applyVisibilityInInsetsConsumerIfNecessary(false);
mWindowVisible = false;
finishViews(false /* finishingInput */);
if (mDecorViewVisible) {
@@ -2916,7 +2907,7 @@
// If we have the window visible for some other reason --
// most likely to show candidates -- then just get rid
// of it. This really shouldn't happen, but just in case...
- if (doIt) doHideWindow();
+ if (doIt) hideWindow();
}
return true;
}
diff --git a/core/java/android/inputmethodservice/NavigationBarController.java b/core/java/android/inputmethodservice/NavigationBarController.java
index e5c22e4..6f4fd76 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -219,7 +219,7 @@
// TODO(b/213337792): Set NAVIGATION_HINT_IME_SHOWN only when necessary.
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (mShouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN
: 0);
navigationBarView.setNavigationIconHints(hints);
}
@@ -470,7 +470,7 @@
}
final int hints = StatusBarManager.NAVIGATION_HINT_BACK_ALT
| (shouldShowImeSwitcherWhenImeIsShown
- ? StatusBarManager.NAVIGATION_HINT_IME_SHOWN : 0);
+ ? StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN : 0);
navigationBarView.setNavigationIconHints(hints);
}
@@ -531,12 +531,14 @@
if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) {
mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground;
- if (mDrawLegacyNavigationBarBackground) {
- mNavigationBarFrame.setBackgroundColor(Color.BLACK);
- } else {
- mNavigationBarFrame.setBackground(null);
+ if (mNavigationBarFrame != null) {
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
+ scheduleRelayout();
}
- scheduleRelayout();
onSystemBarAppearanceChanged(mAppearance);
}
return drawLegacyNavigationBarBackground;
@@ -546,7 +548,8 @@
public String toDebugString() {
return "{mRenderGesturalNavButtons=" + mRenderGesturalNavButtons
+ " mNavigationBarFrame=" + mNavigationBarFrame
- + " mShouldShowImeSwitcherWhenImeIsShown" + mShouldShowImeSwitcherWhenImeIsShown
+ + " mShouldShowImeSwitcherWhenImeIsShown="
+ + mShouldShowImeSwitcherWhenImeIsShown
+ " mAppearance=0x" + Integer.toHexString(mAppearance)
+ " mDarkIntensity=" + mDarkIntensity
+ " mDrawLegacyNavigationBarBackground=" + mDrawLegacyNavigationBarBackground
diff --git a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
index 4284778..a2d7105 100644
--- a/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
+++ b/core/java/android/inputmethodservice/navigationbar/NavigationBarView.java
@@ -270,7 +270,7 @@
// Update IME button visibility, a11y and rotate button always overrides the appearance
final boolean imeSwitcherVisible =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0;
getImeSwitchButton().setVisibility(imeSwitcherVisible ? View.VISIBLE : View.INVISIBLE);
getBackButton().setVisibility(View.VISIBLE);
diff --git a/core/java/android/net/DhcpResults.aidl b/core/java/android/net/DhcpResults.aidl
deleted file mode 100644
index f4db3c3..0000000
--- a/core/java/android/net/DhcpResults.aidl
+++ /dev/null
@@ -1,19 +0,0 @@
-/**
- * Copyright (c) 2012, 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.net;
-
-parcelable DhcpResults;
diff --git a/core/java/android/net/DhcpResults.java b/core/java/android/net/DhcpResults.java
deleted file mode 100644
index 82ba156..0000000
--- a/core/java/android/net/DhcpResults.java
+++ /dev/null
@@ -1,330 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import android.annotation.Nullable;
-import android.compat.annotation.UnsupportedAppUsage;
-import android.os.Build;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.text.TextUtils;
-import android.util.Log;
-
-import com.android.net.module.util.InetAddressUtils;
-
-import java.net.Inet4Address;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Objects;
-
-/**
- * A simple object for retrieving the results of a DHCP request.
- * Optimized (attempted) for that jni interface
- * TODO: remove this class and replace with other existing constructs
- * @hide
- */
-public final class DhcpResults implements Parcelable {
- private static final String TAG = "DhcpResults";
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public LinkAddress ipAddress;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public InetAddress gateway;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public final ArrayList<InetAddress> dnsServers = new ArrayList<>();
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public String domains;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public Inet4Address serverAddress;
-
- /** Vendor specific information (from RFC 2132). */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public String vendorInfo;
-
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int leaseDuration;
-
- /** Link MTU option. 0 means unset. */
- @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public int mtu;
-
- public String serverHostName;
-
- @Nullable
- public String captivePortalApiUrl;
-
- public DhcpResults() {
- super();
- }
-
- /**
- * Create a {@link StaticIpConfiguration} based on the DhcpResults.
- */
- public StaticIpConfiguration toStaticIpConfiguration() {
- return new StaticIpConfiguration.Builder()
- .setIpAddress(ipAddress)
- .setGateway(gateway)
- .setDnsServers(dnsServers)
- .setDomains(domains)
- .build();
- }
-
- public DhcpResults(StaticIpConfiguration source) {
- if (source != null) {
- ipAddress = source.getIpAddress();
- gateway = source.getGateway();
- dnsServers.addAll(source.getDnsServers());
- domains = source.getDomains();
- }
- }
-
- /** copy constructor */
- public DhcpResults(DhcpResults source) {
- this(source == null ? null : source.toStaticIpConfiguration());
- if (source != null) {
- serverAddress = source.serverAddress;
- vendorInfo = source.vendorInfo;
- leaseDuration = source.leaseDuration;
- mtu = source.mtu;
- serverHostName = source.serverHostName;
- captivePortalApiUrl = source.captivePortalApiUrl;
- }
- }
-
- /**
- * @see StaticIpConfiguration#getRoutes(String)
- * @hide
- */
- public List<RouteInfo> getRoutes(String iface) {
- return toStaticIpConfiguration().getRoutes(iface);
- }
-
- /**
- * Test if this DHCP lease includes vendor hint that network link is
- * metered, and sensitive to heavy data transfers.
- */
- public boolean hasMeteredHint() {
- if (vendorInfo != null) {
- return vendorInfo.contains("ANDROID_METERED");
- } else {
- return false;
- }
- }
-
- public void clear() {
- ipAddress = null;
- gateway = null;
- dnsServers.clear();
- domains = null;
- serverAddress = null;
- vendorInfo = null;
- leaseDuration = 0;
- mtu = 0;
- serverHostName = null;
- captivePortalApiUrl = null;
- }
-
- @Override
- public String toString() {
- StringBuffer str = new StringBuffer(super.toString());
-
- str.append(" DHCP server ").append(serverAddress);
- str.append(" Vendor info ").append(vendorInfo);
- str.append(" lease ").append(leaseDuration).append(" seconds");
- if (mtu != 0) str.append(" MTU ").append(mtu);
- str.append(" Servername ").append(serverHostName);
- if (captivePortalApiUrl != null) {
- str.append(" CaptivePortalApiUrl ").append(captivePortalApiUrl);
- }
-
- return str.toString();
- }
-
- @Override
- public boolean equals(@Nullable Object obj) {
- if (this == obj) return true;
-
- if (!(obj instanceof DhcpResults)) return false;
-
- DhcpResults target = (DhcpResults)obj;
-
- return toStaticIpConfiguration().equals(target.toStaticIpConfiguration())
- && Objects.equals(serverAddress, target.serverAddress)
- && Objects.equals(vendorInfo, target.vendorInfo)
- && Objects.equals(serverHostName, target.serverHostName)
- && leaseDuration == target.leaseDuration
- && mtu == target.mtu
- && Objects.equals(captivePortalApiUrl, target.captivePortalApiUrl);
- }
-
- /**
- * Implement the Parcelable interface
- */
- public static final @android.annotation.NonNull Creator<DhcpResults> CREATOR =
- new Creator<DhcpResults>() {
- public DhcpResults createFromParcel(Parcel in) {
- return readFromParcel(in);
- }
-
- public DhcpResults[] newArray(int size) {
- return new DhcpResults[size];
- }
- };
-
- /** Implement the Parcelable interface */
- public void writeToParcel(Parcel dest, int flags) {
- toStaticIpConfiguration().writeToParcel(dest, flags);
- dest.writeInt(leaseDuration);
- dest.writeInt(mtu);
- InetAddressUtils.parcelInetAddress(dest, serverAddress, flags);
- dest.writeString(vendorInfo);
- dest.writeString(serverHostName);
- dest.writeString(captivePortalApiUrl);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- private static DhcpResults readFromParcel(Parcel in) {
- final StaticIpConfiguration s = StaticIpConfiguration.CREATOR.createFromParcel(in);
- final DhcpResults dhcpResults = new DhcpResults(s);
- dhcpResults.leaseDuration = in.readInt();
- dhcpResults.mtu = in.readInt();
- dhcpResults.serverAddress = (Inet4Address) InetAddressUtils.unparcelInetAddress(in);
- dhcpResults.vendorInfo = in.readString();
- dhcpResults.serverHostName = in.readString();
- dhcpResults.captivePortalApiUrl = in.readString();
- return dhcpResults;
- }
-
- // Utils for jni population - false on success
- // Not part of the superclass because they're only used by the JNI iterface to the DHCP daemon.
- public boolean setIpAddress(String addrString, int prefixLength) {
- try {
- Inet4Address addr = (Inet4Address) InetAddresses.parseNumericAddress(addrString);
- ipAddress = new LinkAddress(addr, prefixLength);
- } catch (IllegalArgumentException|ClassCastException e) {
- Log.e(TAG, "setIpAddress failed with addrString " + addrString + "/" + prefixLength);
- return true;
- }
- return false;
- }
-
- public boolean setGateway(String addrString) {
- try {
- gateway = InetAddresses.parseNumericAddress(addrString);
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "setGateway failed with addrString " + addrString);
- return true;
- }
- return false;
- }
-
- public boolean addDns(String addrString) {
- if (TextUtils.isEmpty(addrString) == false) {
- try {
- dnsServers.add(InetAddresses.parseNumericAddress(addrString));
- } catch (IllegalArgumentException e) {
- Log.e(TAG, "addDns failed with addrString " + addrString);
- return true;
- }
- }
- return false;
- }
-
- public LinkAddress getIpAddress() {
- return ipAddress;
- }
-
- public void setIpAddress(LinkAddress ipAddress) {
- this.ipAddress = ipAddress;
- }
-
- public InetAddress getGateway() {
- return gateway;
- }
-
- public void setGateway(InetAddress gateway) {
- this.gateway = gateway;
- }
-
- public List<InetAddress> getDnsServers() {
- return dnsServers;
- }
-
- /**
- * Add a DNS server to this configuration.
- */
- public void addDnsServer(InetAddress server) {
- dnsServers.add(server);
- }
-
- public String getDomains() {
- return domains;
- }
-
- public void setDomains(String domains) {
- this.domains = domains;
- }
-
- public Inet4Address getServerAddress() {
- return serverAddress;
- }
-
- public void setServerAddress(Inet4Address addr) {
- serverAddress = addr;
- }
-
- public int getLeaseDuration() {
- return leaseDuration;
- }
-
- public void setLeaseDuration(int duration) {
- leaseDuration = duration;
- }
-
- public String getVendorInfo() {
- return vendorInfo;
- }
-
- public void setVendorInfo(String info) {
- vendorInfo = info;
- }
-
- public int getMtu() {
- return mtu;
- }
-
- public void setMtu(int mtu) {
- this.mtu = mtu;
- }
-
- public String getCaptivePortalApiUrl() {
- return captivePortalApiUrl;
- }
-
- public void setCaptivePortalApiUrl(String url) {
- captivePortalApiUrl = url;
- }
-}
diff --git a/core/java/android/net/INetworkPolicyManager.aidl b/core/java/android/net/INetworkPolicyManager.aidl
index 6284f56..dc24106 100644
--- a/core/java/android/net/INetworkPolicyManager.aidl
+++ b/core/java/android/net/INetworkPolicyManager.aidl
@@ -72,9 +72,9 @@
SubscriptionPlan getSubscriptionPlan(in NetworkTemplate template);
void notifyStatsProviderWarningOrLimitReached();
SubscriptionPlan[] getSubscriptionPlans(int subId, String callingPackage);
- void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, String callingPackage);
+ void setSubscriptionPlans(int subId, in SubscriptionPlan[] plans, long expirationDurationMillis, String callingPackage);
String getSubscriptionPlansOwner(int subId);
- void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long timeoutMillis, String callingPackage);
+ void setSubscriptionOverride(int subId, int overrideMask, int overrideValue, in int[] networkTypes, long expirationDurationMillis, String callingPackage);
void factoryReset(String subscriber);
diff --git a/core/java/android/net/NetworkPolicy.java b/core/java/android/net/NetworkPolicy.java
index 596f431..714227d 100644
--- a/core/java/android/net/NetworkPolicy.java
+++ b/core/java/android/net/NetworkPolicy.java
@@ -338,7 +338,9 @@
out.writeInt(TEMPLATE_BACKUP_VERSION_LATEST);
out.writeInt(template.getMatchRule());
- BackupUtils.writeString(out, template.getSubscriberIds().iterator().next());
+ final Set<String> subscriberIds = template.getSubscriberIds();
+ BackupUtils.writeString(out, subscriberIds.isEmpty()
+ ? null : subscriberIds.iterator().next());
BackupUtils.writeString(out, template.getWifiNetworkKeys().isEmpty()
? null : template.getWifiNetworkKeys().iterator().next());
out.writeInt(template.getMeteredness());
diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java
index 9122adf..d8f098e 100644
--- a/core/java/android/net/NetworkPolicyManager.java
+++ b/core/java/android/net/NetworkPolicyManager.java
@@ -482,8 +482,8 @@
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first
* @param callingPackage the name of the package making the call.
@@ -491,11 +491,11 @@
*/
public void setSubscriptionOverride(int subId, @SubscriptionOverrideMask int overrideMask,
@SubscriptionOverrideMask int overrideValue,
- @NonNull @Annotation.NetworkType int[] networkTypes, long timeoutMillis,
+ @NonNull @Annotation.NetworkType int[] networkTypes, long expirationDurationMillis,
@NonNull String callingPackage) {
try {
mService.setSubscriptionOverride(subId, overrideMask, overrideValue, networkTypes,
- timeoutMillis, callingPackage);
+ expirationDurationMillis, callingPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -506,13 +506,16 @@
*
* @param subId the subscriber this relationship applies to.
* @param plans the list of plans.
+ * @param expirationDurationMillis the duration after which the subscription plans
+ * will be automatically cleared, or {@code 0} to leave the plans until
+ * explicitly cleared, or the next reboot, whichever happens first
* @param callingPackage the name of the package making the call
* @hide
*/
public void setSubscriptionPlans(int subId, @NonNull SubscriptionPlan[] plans,
- @NonNull String callingPackage) {
+ long expirationDurationMillis, @NonNull String callingPackage) {
try {
- mService.setSubscriptionPlans(subId, plans, callingPackage);
+ mService.setSubscriptionPlans(subId, plans, expirationDurationMillis, callingPackage);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
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/GraphicsEnvironment.java b/core/java/android/os/GraphicsEnvironment.java
index aa4b83a..0c3514f 100644
--- a/core/java/android/os/GraphicsEnvironment.java
+++ b/core/java/android/os/GraphicsEnvironment.java
@@ -165,7 +165,7 @@
private boolean isAngleEnabledByGameMode(Context context, String packageName) {
try {
final boolean gameModeEnabledAngle =
- (mGameManager != null) && mGameManager.getAngleEnabled(packageName);
+ (mGameManager != null) && mGameManager.isAngleEnabled(packageName);
Log.v(TAG, "ANGLE GameManagerService for " + packageName + ": " + gameModeEnabledAngle);
return gameModeEnabledAngle;
} catch (SecurityException e) {
diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl
index d9c9a2b..bc7fb78 100644
--- a/core/java/android/os/IUserManager.aidl
+++ b/core/java/android/os/IUserManager.aidl
@@ -63,7 +63,7 @@
boolean isUserTypeEnabled(in String userType);
boolean canAddMoreUsersOfType(in String userType);
int getRemainingCreatableUserCount(in String userType);
- int getRemainingCreatableProfileCount(in String userType, int userId, boolean allowedToRemoveOne);
+ int getRemainingCreatableProfileCount(in String userType, int userId);
boolean canAddMoreProfilesToUser(in String userType, int userId, boolean allowedToRemoveOne);
boolean canAddMoreManagedProfiles(int userId, boolean allowedToRemoveOne);
UserInfo getProfileParent(int userId);
diff --git a/core/java/android/os/Parcel.java b/core/java/android/os/Parcel.java
index 3bc3ec8..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.
*/
@@ -2088,6 +2144,102 @@
}
/**
+ * Flatten a homogeneous multi-dimensional array with fixed-size. This delegates to other
+ * APIs to write a one-dimensional array. Use {@link #readFixedArray(Object)} or
+ * {@link #createFixedArray(Class, int[])} with the same dimensions to unmarshal.
+ *
+ * @param val The array to be written.
+ * @param parcelableFlags Contextual flags as per
+ * {@link Parcelable#writeToParcel(Parcel, int) Parcelable.writeToParcel()}.
+ * Used only if val is an array of Parcelable objects.
+ * @param dimensions an array of int representing length of each dimension. The array should be
+ * sized with the exact size of dimensions.
+ *
+ * @see #readFixedArray
+ * @see #createFixedArray
+ * @see #writeBooleanArray
+ * @see #writeByteArray
+ * @see #writeCharArray
+ * @see #writeIntArray
+ * @see #writeLongArray
+ * @see #writeFloatArray
+ * @see #writeDoubleArray
+ * @see #writeBinderArray
+ * @see #writeInterfaceArray
+ * @see #writeTypedArray
+ * @throws BadParcelableException If the array's component type is not supported or if its
+ * size doesn't match with the given dimensions.
+ */
+ public <T> void writeFixedArray(@Nullable T val, int parcelableFlags,
+ @NonNull int... dimensions) {
+ if (val == null) {
+ writeInt(-1);
+ return;
+ }
+ writeFixedArrayInternal(val, parcelableFlags, /*index=*/0, dimensions);
+ }
+
+ private <T> void writeFixedArrayInternal(T val, int parcelableFlags, int index,
+ int[] dimensions) {
+ if (index >= dimensions.length) {
+ throw new BadParcelableException("Array has more dimensions than expected: "
+ + dimensions.length);
+ }
+
+ int length = dimensions[index];
+
+ // val should be an array of length N
+ if (val == null) {
+ throw new BadParcelableException("Non-null array shouldn't have a null array.");
+ }
+ if (!val.getClass().isArray()) {
+ throw new BadParcelableException("Not an array: " + val);
+ }
+ if (Array.getLength(val) != length) {
+ throw new BadParcelableException("bad length: expected " + length + ", but got "
+ + Array.getLength(val));
+ }
+
+ // Delegates to other writers if this is a one-dimensional array.
+ // Otherwise, write component arrays with recursive calls.
+
+ final Class<?> componentType = val.getClass().getComponentType();
+ if (!componentType.isArray() && index + 1 != dimensions.length) {
+ throw new BadParcelableException("Array has fewer dimensions than expected: "
+ + dimensions.length);
+ }
+ if (componentType == boolean.class) {
+ writeBooleanArray((boolean[]) val);
+ } else if (componentType == byte.class) {
+ writeByteArray((byte[]) val);
+ } else if (componentType == char.class) {
+ writeCharArray((char[]) val);
+ } else if (componentType == int.class) {
+ writeIntArray((int[]) val);
+ } else if (componentType == long.class) {
+ writeLongArray((long[]) val);
+ } else if (componentType == float.class) {
+ writeFloatArray((float[]) val);
+ } else if (componentType == double.class) {
+ writeDoubleArray((double[]) val);
+ } else if (componentType == IBinder.class) {
+ writeBinderArray((IBinder[]) val);
+ } else if (IInterface.class.isAssignableFrom(componentType)) {
+ writeInterfaceArray((IInterface[]) val);
+ } else if (Parcelable.class.isAssignableFrom(componentType)) {
+ writeTypedArray((Parcelable[]) val, parcelableFlags);
+ } else if (componentType.isArray()) {
+ writeInt(length);
+ for (int i = 0; i < length; i++) {
+ writeFixedArrayInternal(Array.get(val, i), parcelableFlags, index + 1,
+ dimensions);
+ }
+ } else {
+ throw new BadParcelableException("unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
* Flatten a generic object in to a parcel. The given Object value may
* currently be one of the following types:
*
@@ -2949,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;
}
/**
@@ -3788,6 +3948,317 @@
}
/**
+ * Read a new multi-dimensional array from a parcel. If you want to read Parcelable or
+ * IInterface values, use {@link #readFixedArray(Object, Parcelable.Creator)} or
+ * {@link #readFixedArray(Object, Function)}.
+ * @param val the destination array to hold the read values.
+ *
+ * @see #writeTypedArray
+ * @see #readBooleanArray
+ * @see #readByteArray
+ * @see #readCharArray
+ * @see #readIntArray
+ * @see #readLongArray
+ * @see #readFloatArray
+ * @see #readDoubleArray
+ * @see #readBinderArray
+ * @see #readInterfaceArray
+ * @see #readTypedArray
+ */
+ public <T> void readFixedArray(@NonNull T val) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (componentType == boolean.class) {
+ readBooleanArray((boolean[]) val);
+ } else if (componentType == byte.class) {
+ readByteArray((byte[]) val);
+ } else if (componentType == char.class) {
+ readCharArray((char[]) val);
+ } else if (componentType == int.class) {
+ readIntArray((int[]) val);
+ } else if (componentType == long.class) {
+ readLongArray((long[]) val);
+ } else if (componentType == float.class) {
+ readFloatArray((float[]) val);
+ } else if (componentType == double.class) {
+ readDoubleArray((double[]) val);
+ } else if (componentType == IBinder.class) {
+ readBinderArray((IBinder[]) val);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i));
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
+ * Read a new multi-dimensional array of typed interfaces from a parcel.
+ * If you want to read Parcelable values, use
+ * {@link #readFixedArray(Object, Parcelable.Creator)}. For values of other types, use
+ * {@link #readFixedArray(Object)}.
+ * @param val the destination array to hold the read values.
+ */
+ public <T, S extends IInterface> void readFixedArray(@NonNull T val,
+ @NonNull Function<IBinder, S> asInterface) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (IInterface.class.isAssignableFrom(componentType)) {
+ readInterfaceArray((S[]) val, asInterface);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), asInterface);
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ /**
+ * Read a new multi-dimensional array of typed parcelables from a parcel.
+ * If you want to read IInterface values, use
+ * {@link #readFixedArray(Object, Function)}. For values of other types, use
+ * {@link #readFixedArray(Object)}.
+ * @param val the destination array to hold the read values.
+ */
+ public <T, S extends Parcelable> void readFixedArray(@NonNull T val,
+ @NonNull Parcelable.Creator<S> c) {
+ Class<?> componentType = val.getClass().getComponentType();
+ if (Parcelable.class.isAssignableFrom(componentType)) {
+ readTypedArray((S[]) val, c);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length != Array.getLength(val)) {
+ throw new BadParcelableException("Bad length: expected " + Array.getLength(val)
+ + ", but got " + length);
+ }
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), c);
+ }
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+ }
+
+ private void ensureClassHasExpectedDimensions(@NonNull Class<?> cls, int numDimension) {
+ if (numDimension <= 0) {
+ throw new BadParcelableException("Fixed-size array should have dimensions.");
+ }
+
+ for (int i = 0; i < numDimension; i++) {
+ if (!cls.isArray()) {
+ throw new BadParcelableException("Array has fewer dimensions than expected: "
+ + numDimension);
+ }
+ cls = cls.getComponentType();
+ }
+ if (cls.isArray()) {
+ throw new BadParcelableException("Array has more dimensions than expected: "
+ + numDimension);
+ }
+ }
+
+ /**
+ * Read and return a new multi-dimensional array from a parcel. Returns null if the
+ * previously written array object is null. If you want to read Parcelable or
+ * IInterface values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])} or
+ * {@link #createFixedArray(Class, Function, int[])}.
+ * @param cls the Class object for the target array type. (e.g. int[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ *
+ * @see #writeTypedArray
+ * @see #createBooleanArray
+ * @see #createByteArray
+ * @see #createCharArray
+ * @see #createIntArray
+ * @see #createLongArray
+ * @see #createFloatArray
+ * @see #createDoubleArray
+ * @see #createBinderArray
+ * @see #createInterfaceArray
+ * @see #createTypedArray
+ */
+ @Nullable
+ public <T> T createFixedArray(@NonNull Class<T> cls, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (componentType == boolean.class) {
+ val = (T) createBooleanArray();
+ } else if (componentType == byte.class) {
+ val = (T) createByteArray();
+ } else if (componentType == char.class) {
+ val = (T) createCharArray();
+ } else if (componentType == int.class) {
+ val = (T) createIntArray();
+ } else if (componentType == long.class) {
+ val = (T) createLongArray();
+ } else if (componentType == float.class) {
+ val = (T) createFloatArray();
+ } else if (componentType == double.class) {
+ val = (T) createDoubleArray();
+ } else if (componentType == IBinder.class) {
+ val = (T) createBinderArray();
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i));
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
+ * Read and return a new multi-dimensional array of typed interfaces from a parcel.
+ * Returns null if the previously written array object is null. If you want to read
+ * Parcelable values, use {@link #createFixedArray(Class, Parcelable.Creator, int[])}.
+ * For values of other types use {@link #createFixedArray(Class, int[])}.
+ * @param cls the Class object for the target array type. (e.g. IFoo[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ */
+ @Nullable
+ public <T, S extends IInterface> T createFixedArray(@NonNull Class<T> cls,
+ @NonNull Function<IBinder, S> asInterface, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (IInterface.class.isAssignableFrom(componentType)) {
+ val = (T) createInterfaceArray(n -> (S[]) Array.newInstance(componentType, n),
+ asInterface);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), asInterface);
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
+ * Read and return a new multi-dimensional array of typed parcelables from a parcel.
+ * Returns null if the previously written array object is null. If you want to read
+ * IInterface values, use {@link #createFixedArray(Class, Function, int[])}.
+ * For values of other types use {@link #createFixedArray(Class, int[])}.
+ * @param cls the Class object for the target array type. (e.g. Foo[][].class)
+ * @param dimensions an array of int representing length of each dimension.
+ */
+ @Nullable
+ public <T, S extends Parcelable> T createFixedArray(@NonNull Class<T> cls,
+ @NonNull Parcelable.Creator<S> c, @NonNull int... dimensions) {
+ // Check if type matches with dimensions
+ // If type is one-dimensional array, delegate to other creators
+ // Otherwise, create an multi-dimensional array at once and then fill it with readFixedArray
+
+ ensureClassHasExpectedDimensions(cls, dimensions.length);
+
+ T val = null;
+ final Class<?> componentType = cls.getComponentType();
+ if (Parcelable.class.isAssignableFrom(componentType)) {
+ val = (T) createTypedArray(c);
+ } else if (componentType.isArray()) {
+ int length = readInt();
+ if (length < 0) {
+ return null;
+ }
+ if (length != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0]
+ + ", but got " + length);
+ }
+
+ // Create a multi-dimensional array with an innermost component type and dimensions
+ Class<?> innermost = componentType.getComponentType();
+ while (innermost.isArray()) {
+ innermost = innermost.getComponentType();
+ }
+ val = (T) Array.newInstance(innermost, dimensions);
+ for (int i = 0; i < length; i++) {
+ readFixedArray(Array.get(val, i), c);
+ }
+ return val;
+ } else {
+ throw new BadParcelableException("Unknown type for fixed-size array: " + componentType);
+ }
+
+ // Check if val is null (which is OK) or has the expected size.
+ // This check doesn't have to be multi-dimensional because multi-dimensional arrays
+ // are created with expected dimensions.
+ if (val != null && Array.getLength(val) != dimensions[0]) {
+ throw new BadParcelableException("Bad length: expected " + dimensions[0] + ", but got "
+ + Array.getLength(val));
+ }
+ return val;
+ }
+
+ /**
* Write a heterogeneous array of Parcelable objects into the Parcel.
* Each object in the array is written along with its class name, so
* that the correct class can later be instantiated. As a result, this
@@ -4588,6 +5059,7 @@
}
private void freeBuffer() {
+ mFlags = 0;
resetSqaushingState();
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java
index 7b8d34b..2bd1dbb 100644
--- a/core/java/android/os/UserManager.java
+++ b/core/java/android/os/UserManager.java
@@ -4055,9 +4055,6 @@
* <p>Note that is applicable to any profile type (currently not including Restricted profiles).
*
* @param userType the type of profile, such as {@link UserManager#USER_TYPE_PROFILE_MANAGED}.
- * @param allowedToRemoveOne whether removing an existing profile of given type -if there is-
- * from the context user to make up space should be taken into account
- * for the calculation.
* @return how many additional profiles can be created.
* @hide
*/
@@ -4068,13 +4065,11 @@
android.Manifest.permission.QUERY_USERS
})
@UserHandleAware
- public int getRemainingCreatableProfileCount(@NonNull String userType,
- boolean allowedToRemoveOne) {
+ public int getRemainingCreatableProfileCount(@NonNull String userType) {
Objects.requireNonNull(userType, "userType must not be null");
try {
// TODO(b/142482943): Perhaps let the following code apply to restricted users too.
- return mService.getRemainingCreatableProfileCount(userType, mUserId,
- allowedToRemoveOne);
+ return mService.getRemainingCreatableProfileCount(userType, mUserId);
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
diff --git a/core/java/android/os/Vibrator.java b/core/java/android/os/Vibrator.java
index 8f50860..78f1cb1 100644
--- a/core/java/android/os/Vibrator.java
+++ b/core/java/android/os/Vibrator.java
@@ -546,18 +546,22 @@
@VibrationEffectSupport
public final int areAllEffectsSupported(
@NonNull @VibrationEffect.EffectType int... effectIds) {
- int support = VIBRATION_EFFECT_SUPPORT_YES;
- for (int supported : areEffectsSupported(effectIds)) {
- if (supported == VIBRATION_EFFECT_SUPPORT_NO) {
- return VIBRATION_EFFECT_SUPPORT_NO;
- } else if (supported == VIBRATION_EFFECT_SUPPORT_UNKNOWN) {
- support = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+ VibratorInfo info = getInfo();
+ int allSupported = VIBRATION_EFFECT_SUPPORT_YES;
+ for (int effectId : effectIds) {
+ switch (info.isEffectSupported(effectId)) {
+ case VIBRATION_EFFECT_SUPPORT_NO:
+ return VIBRATION_EFFECT_SUPPORT_NO;
+ case VIBRATION_EFFECT_SUPPORT_YES:
+ continue;
+ default: // VIBRATION_EFFECT_SUPPORT_UNKNOWN
+ allSupported = VIBRATION_EFFECT_SUPPORT_UNKNOWN;
+ break;
}
}
- return support;
+ return allSupported;
}
-
/**
* Query whether the vibrator supports the given primitives.
*
@@ -598,8 +602,9 @@
*/
public final boolean areAllPrimitivesSupported(
@NonNull @VibrationEffect.Composition.PrimitiveType int... primitiveIds) {
- for (boolean supported : arePrimitivesSupported(primitiveIds)) {
- if (!supported) {
+ VibratorInfo info = getInfo();
+ for (int primitiveId : primitiveIds) {
+ if (!info.isPrimitiveSupported(primitiveId)) {
return false;
}
}
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/permission/IPermissionManager.aidl b/core/java/android/permission/IPermissionManager.aidl
index 1c0320e..619c870 100644
--- a/core/java/android/permission/IPermissionManager.aidl
+++ b/core/java/android/permission/IPermissionManager.aidl
@@ -79,7 +79,8 @@
void revokeOwnPermissionsOnKill(String packageName, in List<String> permissions);
void startOneTimePermissionSession(String packageName, int userId, long timeout,
- int importanceToResetTimer, int importanceToKeepSessionAlive);
+ long revokeAfterKilledDelay, int importanceToResetTimer,
+ int importanceToKeepSessionAlive);
void stopOneTimePermissionSession(String packageName, int userId);
diff --git a/core/java/android/permission/PermissionControllerManager.java b/core/java/android/permission/PermissionControllerManager.java
index 0cf06aa..a005ab4e 100644
--- a/core/java/android/permission/PermissionControllerManager.java
+++ b/core/java/android/permission/PermissionControllerManager.java
@@ -907,21 +907,23 @@
* <li>Each permission in {@code permissions} must be a runtime permission.
* </ul>
* <p>
- * For every permission in {@code permissions}, the entire permission group it belongs to will
- * be revoked. This revocation happens asynchronously and kills all processes running in the
- * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * This revocation happens asynchronously and kills all processes running in the same UID as
+ * {@code packageName}. It will be triggered once it is safe to do so.
*
* @param packageName The name of the package for which the permissions will be revoked.
* @param permissions List of permissions to be revoked.
- * @param callback Callback called when the revocation request has been completed.
*
- * @see Context#revokeOwnPermissionsOnKill(Collection)
+ * @see Context#revokeOwnPermissionsOnKill(java.util.Collection)
*
* @hide
*/
public void revokeOwnPermissionsOnKill(@NonNull String packageName,
- @NonNull List<String> permissions, AndroidFuture<Void> callback) {
+ @NonNull List<String> permissions) {
mRemoteService.postAsync(service -> {
+ AndroidFuture<Void> callback = new AndroidFuture<>();
service.revokeOwnPermissionsOnKill(packageName, permissions, callback);
return callback;
}).whenComplete((result, err) -> {
diff --git a/core/java/android/permission/PermissionControllerService.java b/core/java/android/permission/PermissionControllerService.java
index 8d9f82b..3292e71 100644
--- a/core/java/android/permission/PermissionControllerService.java
+++ b/core/java/android/permission/PermissionControllerService.java
@@ -291,7 +291,7 @@
/**
* Called when a package is considered inactive based on the criteria given by
- * {@link PermissionManager#startOneTimePermissionSession(String, long, int, int)}.
+ * {@link PermissionManager#startOneTimePermissionSession(String, long, long, int, int)}.
* This method is called at the end of a one-time permission session
*
* @param packageName The package that has been inactive
@@ -329,9 +329,11 @@
* Triggers the revocation of one or more permissions for a package. This should only be called
* at the request of {@code packageName}.
* <p>
- * For every permission in {@code permissions}, the entire permission group it belongs to will
- * be revoked. This revocation happens asynchronously and kills all processes running in the
- * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * This revocation happens asynchronously and kills all processes running in the same UID as
+ * {@code packageName}. It will be triggered once it is safe to do so.
*
* @param packageName The name of the package for which the permissions will be revoked.
* @param permissions List of permissions to be revoked.
diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java
index 15f13eb..12fa0dd 100644
--- a/core/java/android/permission/PermissionManager.java
+++ b/core/java/android/permission/PermissionManager.java
@@ -20,6 +20,7 @@
import android.Manifest;
import android.annotation.CheckResult;
+import android.annotation.DurationMillisLong;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -1282,6 +1283,22 @@
}
/**
+ * Starts a one-time permission session for a given package.
+ * @see #startOneTimePermissionSession(String, long, long, int, int)
+ * @hide
+ * @deprecated Use {@link #startOneTimePermissionSession(String, long, long, int, int)} instead
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
+ public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
+ @ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
+ @ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
+ startOneTimePermissionSession(packageName, timeoutMillis, -1,
+ importanceToResetTimer, importanceToKeepSessionAlive);
+ }
+
+ /**
* Starts a one-time permission session for a given package. A one-time permission session is
* ended if app becomes inactive. Inactivity is defined as the package's uid importance level
* staying > importanceToResetTimer for timeoutMillis milliseconds. If the package's uid
@@ -1301,25 +1318,33 @@
* {@link PermissionControllerService#onOneTimePermissionSessionTimeout(String)} is invoked.
* </p>
* <p>
- * Note that if there is currently an active session for a package a new one isn't created and
- * the existing one isn't changed.
+ * Note that if there is currently an active session for a package a new one isn't created but
+ * each parameter of the existing one will be updated to the more aggressive of both sessions.
+ * This means that durations will be set to the shortest parameter and importances will be set
+ * to the lowest one.
* </p>
* @param packageName The package to start a one-time permission session for
* @param timeoutMillis Number of milliseconds for an app to be in an inactive state
+ * @param revokeAfterKilledDelayMillis Number of milliseconds to wait before revoking on the
+ * event an app is terminated. Set to -1 to use default
+ * value for the device.
* @param importanceToResetTimer The least important level to uid must be to reset the timer
* @param importanceToKeepSessionAlive The least important level the uid must be to keep the
- * session alive
+ * session alive
*
* @hide
*/
@SystemApi
@RequiresPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS)
- public void startOneTimePermissionSession(@NonNull String packageName, long timeoutMillis,
+ public void startOneTimePermissionSession(@NonNull String packageName,
+ @DurationMillisLong long timeoutMillis,
+ @DurationMillisLong long revokeAfterKilledDelayMillis,
@ActivityManager.RunningAppProcessInfo.Importance int importanceToResetTimer,
@ActivityManager.RunningAppProcessInfo.Importance int importanceToKeepSessionAlive) {
try {
mPermissionManager.startOneTimePermissionSession(packageName, mContext.getUserId(),
- timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
+ importanceToKeepSessionAlive);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c4fe1a4..3f41458 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -16816,6 +16816,16 @@
"low_power_standby_active_during_maintenance";
/**
+ * Timeout for the system server watchdog.
+ *
+ * @see {@link com.android.server.Watchdog}.
+ *
+ * @hide
+ */
+ public static final String WATCHDOG_TIMEOUT_MILLIS =
+ "system_server_watchdog_timeout_ms";
+
+ /**
* Settings migrated from Wear OS settings provider.
* @hide
*/
@@ -17322,6 +17332,12 @@
"clockwork_long_press_to_assistant_enabled";
/*
+ * Whether the device has Cooldown Mode enabled.
+ * @hide
+ */
+ public static final String COOLDOWN_MODE_ON = "cooldown_mode_on";
+
+ /*
* Whether the device has Wet Mode/ Touch Lock Mode enabled.
* @hide
*/
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java
index 34e35d4..3ff0161 100644
--- a/core/java/android/provider/Telephony.java
+++ b/core/java/android/provider/Telephony.java
@@ -1425,25 +1425,6 @@
public static final String KEY_TYPE = "key_type";
/**
- * MVNO type:
- * {@code SPN (Service Provider Name), IMSI, GID (Group Identifier Level 1)}.
- * <P> Type: TEXT </P>
- */
- public static final String MVNO_TYPE = "mvno_type";
-
- /**
- * MVNO data.
- * Use the following examples.
- * <ul>
- * <li>SPN: A MOBILE, BEN NL, ...</li>
- * <li>IMSI: 302720x94, 2060188, ...</li>
- * <li>GID: 4E, 33, ...</li>
- * </ul>
- * <P> Type: TEXT </P>
- */
- public static final String MVNO_MATCH_DATA = "mvno_match_data";
-
- /**
* The carrier public key that is used for the IMSI encryption.
* <P> Type: TEXT </P>
*/
@@ -1470,6 +1451,11 @@
public static final String LAST_MODIFIED = "last_modified";
/**
+ * Carrier ID of the operetor.
+ * <P> Type: TEXT </P>
+ */
+ public static final String CARRIER_ID = "carrier_id";
+ /**
* The {@code content://} style URL for this table.
*/
@NonNull
diff --git a/core/java/android/service/attention/AttentionService.java b/core/java/android/service/attention/AttentionService.java
index 49ab5db..f5c59b5 100644
--- a/core/java/android/service/attention/AttentionService.java
+++ b/core/java/android/service/attention/AttentionService.java
@@ -24,11 +24,14 @@
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
+import android.util.Slog;
import com.android.internal.util.Preconditions;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
/**
@@ -51,6 +54,7 @@
*/
@SystemApi
public abstract class AttentionService extends Service {
+ private static final String LOG_TAG = "AttentionService";
/**
* The {@link Intent} that must be declared as handled by the service. To be supported, the
* service must also require the {@link android.Manifest.permission#BIND_ATTENTION_SERVICE}
@@ -80,6 +84,9 @@
/** Camera permission is not granted. */
public static final int ATTENTION_FAILURE_CAMERA_PERMISSION_ABSENT = 6;
+ /** Users’ proximity is unknown (proximity sensing was inconclusive and is unsupported). */
+ public static final double PROXIMITY_UNKNOWN = -1;
+
/**
* Result codes for when attention check was successful.
*
@@ -118,6 +125,20 @@
Preconditions.checkNotNull(callback);
AttentionService.this.onCancelAttentionCheck(new AttentionCallback(callback));
}
+
+ /** {@inheritDoc} */
+ @Override
+ public void onStartProximityUpdates(IProximityCallback callback) {
+ Objects.requireNonNull(callback);
+ AttentionService.this.onStartProximityUpdates(new ProximityCallback(callback));
+
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void onStopProximityUpdates() {
+ AttentionService.this.onStopProximityUpdates();
+ }
};
@Nullable
@@ -143,6 +164,23 @@
*/
public abstract void onCancelAttentionCheck(@NonNull AttentionCallback callback);
+ /**
+ * Requests the continuous updates of proximity signal via the provided callback,
+ * until the given callback is unregistered.
+ *
+ * @param callback the callback to return the result to
+ */
+ public void onStartProximityUpdates(@NonNull ProximityCallback callback) {
+ Slog.w(LOG_TAG, "Override this method.");
+ }
+
+ /**
+ * Requests to stop providing continuous updates until the callback is registered.
+ */
+ public void onStopProximityUpdates() {
+ Slog.w(LOG_TAG, "Override this method.");
+ }
+
/** Callbacks for AttentionService results. */
public static final class AttentionCallback {
@NonNull private final IAttentionCallback mCallback;
@@ -174,4 +212,26 @@
}
}
}
+
+ /** Callbacks for ProximityCallback results. */
+ public static final class ProximityCallback {
+ @NonNull private final WeakReference<IProximityCallback> mCallback;
+
+ private ProximityCallback(@NonNull IProximityCallback callback) {
+ mCallback = new WeakReference<>(callback);
+ }
+
+ /**
+ * @param distance the estimated distance of the user (in meter)
+ * The distance will be PROXIMITY_UNKNOWN if the proximity sensing was inconclusive.
+ *
+ */
+ public void onProximityUpdate(double distance) {
+ try {
+ mCallback.get().onProximityUpdate(distance);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+ }
}
diff --git a/core/java/android/service/attention/IAttentionService.aidl b/core/java/android/service/attention/IAttentionService.aidl
index 99e7997..8bb881b 100644
--- a/core/java/android/service/attention/IAttentionService.aidl
+++ b/core/java/android/service/attention/IAttentionService.aidl
@@ -17,6 +17,7 @@
package android.service.attention;
import android.service.attention.IAttentionCallback;
+import android.service.attention.IProximityCallback;
/**
* Interface for a concrete implementation to provide to the AttentionManagerService.
@@ -26,4 +27,6 @@
oneway interface IAttentionService {
void checkAttention(IAttentionCallback callback);
void cancelAttentionCheck(IAttentionCallback callback);
+ void onStartProximityUpdates(IProximityCallback callback);
+ void onStopProximityUpdates();
}
\ No newline at end of file
diff --git a/core/java/android/service/attention/IProximityCallback.aidl b/core/java/android/service/attention/IProximityCallback.aidl
new file mode 100644
index 0000000..9ecf9bc
--- /dev/null
+++ b/core/java/android/service/attention/IProximityCallback.aidl
@@ -0,0 +1,10 @@
+package android.service.attention;
+
+/**
+ * Callback for onStartProximityUpdates request.
+ *
+ * @hide
+ */
+oneway interface IProximityCallback {
+ void onProximityUpdate(double distance);
+}
diff --git a/core/java/android/service/games/GameSession.java b/core/java/android/service/games/GameSession.java
index 9590933..468e087c 100644
--- a/core/java/android/service/games/GameSession.java
+++ b/core/java/android/service/games/GameSession.java
@@ -20,15 +20,23 @@
import android.annotation.IntDef;
import android.annotation.MainThread;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
+import android.app.ActivityTaskManager;
+import android.app.Instrumentation;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
+import android.os.UserHandle;
import android.util.Slog;
import android.view.SurfaceControlViewHost;
import android.view.View;
@@ -41,6 +49,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
import java.util.concurrent.Executor;
/**
@@ -84,6 +93,15 @@
}
@Override
+ public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+ boolean visibleDueToGesture) {
+ Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
+ GameSession::dispatchTransientSystemBarVisibilityFromRevealGestureChanged,
+ GameSession.this,
+ visibleDueToGesture));
+ }
+
+ @Override
public void onTaskFocusChanged(boolean focused) {
Handler.getMain().executeOrSendMessage(PooledLambda.obtainMessage(
GameSession::moveToState, GameSession.this,
@@ -109,7 +127,9 @@
}
private LifecycleState mLifecycleState = LifecycleState.INITIALIZED;
+ private boolean mAreTransientInsetsVisibleDueToGesture = false;
private IGameSessionController mGameSessionController;
+ private Context mContext;
private int mTaskId;
private GameSessionRootView mGameSessionRootView;
private SurfaceControlViewHost mSurfaceControlViewHost;
@@ -127,6 +147,7 @@
int heightPx) {
mGameSessionController = gameSessionController;
mTaskId = taskId;
+ mContext = context;
mSurfaceControlViewHost = surfaceControlViewHost;
mGameSessionRootView = new GameSessionRootView(context, mSurfaceControlViewHost);
surfaceControlViewHost.setView(mGameSessionRootView, widthPx, heightPx);
@@ -138,11 +159,23 @@
}
@Hide
- void doDestroy() {
+ private void doDestroy() {
mSurfaceControlViewHost.release();
moveToState(LifecycleState.DESTROYED);
}
+ /** @hide */
+ @VisibleForTesting
+ @MainThread
+ public void dispatchTransientSystemBarVisibilityFromRevealGestureChanged(
+ boolean visibleDueToGesture) {
+ boolean didValueChange = mAreTransientInsetsVisibleDueToGesture != visibleDueToGesture;
+ mAreTransientInsetsVisibleDueToGesture = visibleDueToGesture;
+ if (didValueChange) {
+ onTransientSystemBarVisibilityFromRevealGestureChanged(visibleDueToGesture);
+ }
+ }
+
/**
* @hide
*/
@@ -252,7 +285,23 @@
*
* @param focused True if the game task is focused, false if the game task is unfocused.
*/
- public void onGameTaskFocusChanged(boolean focused) {}
+ public void onGameTaskFocusChanged(boolean focused) {
+ }
+
+ /**
+ * Called when the visibility of the transient system bars changed due to the user performing
+ * the reveal gesture. The reveal gesture is defined as a swipe to reveal the transient system
+ * bars that originates from the system bars.
+ *
+ * @param visibleDueToGesture if the transient bars triggered by the reveal gesture are visible.
+ * This is {@code true} when the transient system bars become visible
+ * due to user performing the reveal gesture. This is {@code false}
+ * when the transient system bars are hidden or become permanently
+ * visible.
+ */
+ public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+ boolean visibleDueToGesture) {
+ }
/**
* Sets the task overlay content to an explicit view. This view is placed directly into the game
@@ -261,6 +310,8 @@
* {@code View} may not be cleared once set, but may be replaced by invoking
* {@link #setTaskOverlayView(View, ViewGroup.LayoutParams)} again.
*
+ * <p><b>WARNING</b>: Callers <b>must</b> ensure that only trusted views are provided.
+ *
* @param view The desired content to display.
* @param layoutParams Layout parameters for the view.
*/
@@ -344,12 +395,14 @@
/**
* Called when taking the screenshot failed.
+ *
* @param statusCode Indicates the reason for failure.
*/
void onFailure(@ScreenshotFailureStatus int statusCode);
/**
* Called when taking the screenshot succeeded.
+ *
* @param bitmap The screenshot.
*/
void onSuccess(@NonNull Bitmap bitmap);
@@ -416,4 +469,67 @@
break;
}
}
+
+ /**
+ * Launches an activity within the same activity stack as the {@link GameSession}. When the
+ * target activity exits, {@link GameSessionActivityCallback#onActivityResult(int, Intent)} will
+ * be invoked with the result code and result data directly from the target activity (in other
+ * words, the result code and data set via the target activity's
+ * {@link android.app.Activity#startActivityForResult} call). The caller is expected to handle
+ * the results that the target activity returns.
+ *
+ * <p>Any activity that an app would normally be able to start via {@link
+ * android.app.Activity#startActivityForResult} will be startable via this method.
+ *
+ * <p>Started activities may see a different calling package than the game session's package
+ * when calling {@link android.app.Activity#getCallingPackage()}.
+ *
+ * <p> If an exception is thrown while handling {@code intent},
+ * {@link GameSessionActivityCallback#onActivityStartFailed(Throwable)} will be called instead
+ * of {@link GameSessionActivityCallback#onActivityResult(int, Intent)}.
+ *
+ * @param intent The intent to start.
+ * @param options Additional options for how the Activity should be started. See
+ * {@link android.app.Activity#startActivityForResult(Intent, int, Bundle)} for
+ * more details. This value may be null.
+ * @param executor Executor on which {@code callback} should be invoked.
+ * @param callback Callback to be invoked once the started activity has finished.
+ */
+ @RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)
+ public final void startActivityFromGameSessionForResult(
+ @NonNull Intent intent, @Nullable Bundle options, @NonNull Executor executor,
+ @NonNull GameSessionActivityCallback callback) {
+ Objects.requireNonNull(intent);
+ Objects.requireNonNull(executor);
+ Objects.requireNonNull(callback);
+
+ AndroidFuture<GameSessionActivityResult> future =
+ new AndroidFuture<GameSessionActivityResult>()
+ .whenCompleteAsync((result, ex) -> {
+ if (ex != null) {
+ callback.onActivityStartFailed(ex);
+ return;
+ }
+ callback.onActivityResult(result.getResultCode(), result.getData());
+ }, executor);
+
+ final Intent trampolineIntent = new Intent();
+ trampolineIntent.setComponent(
+ new ComponentName(
+ "android", "android.service.games.GameSessionTrampolineActivity"));
+ trampolineIntent.putExtra(GameSessionTrampolineActivity.INTENT_KEY, intent);
+ trampolineIntent.putExtra(GameSessionTrampolineActivity.OPTIONS_KEY, options);
+ trampolineIntent.putExtra(
+ GameSessionTrampolineActivity.FUTURE_KEY, future);
+
+ try {
+ int result = ActivityTaskManager.getService().startActivityFromGameSession(
+ mContext.getIApplicationThread(), mContext.getPackageName(), "GameSession",
+ Binder.getCallingPid(), Binder.getCallingUid(), trampolineIntent, mTaskId,
+ UserHandle.myUserId());
+ Instrumentation.checkStartActivityResult(result, trampolineIntent);
+ } catch (Throwable t) {
+ executor.execute(() -> callback.onActivityStartFailed(t));
+ }
+ }
}
diff --git a/core/java/android/service/games/GameSessionActivityCallback.java b/core/java/android/service/games/GameSessionActivityCallback.java
new file mode 100644
index 0000000..3b11df1
--- /dev/null
+++ b/core/java/android/service/games/GameSessionActivityCallback.java
@@ -0,0 +1,54 @@
+/*
+ * 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.service.games;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Callback invoked when an activity launched via
+ * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
+ * GameSessionActivityCallback)}} has returned a result or failed to start.
+ *
+ * @hide
+ */
+@SystemApi
+public interface GameSessionActivityCallback {
+ /**
+ * Callback invoked when an activity launched via
+ * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
+ * GameSessionActivityCallback)}} has returned a result.
+ *
+ * @param resultCode The result code of the launched activity. See {@link
+ * android.app.Activity#setResult(int)}.
+ * @param data Any data returned by the launched activity. See {@link
+ * android.app.Activity#setResult(int, Intent)}.
+ */
+ void onActivityResult(int resultCode, @Nullable Intent data);
+
+ /**
+ * Callback invoked when a throwable was thrown when launching the {@link Intent} in
+ * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
+ * GameSessionActivityCallback)}}.
+ */
+ default void onActivityStartFailed(@NonNull Throwable t) {}
+}
diff --git a/core/java/android/service/games/GameSessionActivityResult.java b/core/java/android/service/games/GameSessionActivityResult.java
new file mode 100644
index 0000000..a2ec6ad
--- /dev/null
+++ b/core/java/android/service/games/GameSessionActivityResult.java
@@ -0,0 +1,71 @@
+/*
+ * 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.service.games;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.content.Intent;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+
+final class GameSessionActivityResult implements Parcelable {
+
+ public static final Creator<GameSessionActivityResult> CREATOR =
+ new Creator<GameSessionActivityResult>() {
+ @Override
+ public GameSessionActivityResult createFromParcel(Parcel in) {
+ int resultCode = in.readInt();
+ Intent data = in.readParcelable(Intent.class.getClassLoader(), Intent.class);
+ return new GameSessionActivityResult(resultCode, data);
+ }
+
+ @Override
+ public GameSessionActivityResult[] newArray(int size) {
+ return new GameSessionActivityResult[size];
+ }
+ };
+
+ private final int mResultCode;
+ @Nullable
+ private final Intent mData;
+
+ GameSessionActivityResult(int resultCode, @Nullable Intent data) {
+ mResultCode = resultCode;
+ mData = data;
+ }
+
+ int getResultCode() {
+ return mResultCode;
+ }
+
+ @Nullable
+ Intent getData() {
+ return mData;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mResultCode);
+ dest.writeParcelable(mData, flags);
+ }
+}
diff --git a/core/java/android/service/games/GameSessionTrampolineActivity.java b/core/java/android/service/games/GameSessionTrampolineActivity.java
new file mode 100644
index 0000000..ddea098
--- /dev/null
+++ b/core/java/android/service/games/GameSessionTrampolineActivity.java
@@ -0,0 +1,79 @@
+/*
+ * 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.service.games;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Slog;
+
+import com.android.internal.infra.AndroidFuture;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Trampoline activity that enables the
+ * {@link GameSession#startActivityFromGameSessionForResult(Intent, Bundle, Executor,
+ * GameSessionActivityCallback)} API by reusing existing activity result infrastructure in the
+ * {@link Activity} class. This activity forwards activity results back to the calling
+ * {@link GameSession} via {@link AndroidFuture}.
+ *
+ * @hide
+ */
+public final class GameSessionTrampolineActivity extends Activity {
+ private static final String TAG = "GameSessionTrampoline";
+ private static final int REQUEST_CODE = 1;
+
+ static final String FUTURE_KEY = "GameSessionTrampolineActivity.future";
+ static final String INTENT_KEY = "GameSessionTrampolineActivity.intent";
+ static final String OPTIONS_KEY = "GameSessionTrampolineActivity.options";
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ try {
+ startActivityAsCaller(
+ getIntent().getParcelableExtra(INTENT_KEY),
+ getIntent().getBundleExtra(OPTIONS_KEY),
+ null,
+ false,
+ getUserId(),
+ REQUEST_CODE);
+ } catch (Exception e) {
+ Slog.w(TAG, "Unable to launch activity from game session");
+ AndroidFuture<GameSessionActivityResult> future = getIntent().getParcelableExtra(
+ FUTURE_KEY);
+ future.completeExceptionally(e);
+ finish();
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (requestCode != REQUEST_CODE) {
+ // Something went very wrong if we hit this code path, and we should bail.
+ throw new IllegalStateException("Unexpected request code: " + requestCode);
+ }
+
+ AndroidFuture<GameSessionActivityResult> future = getIntent().getParcelableExtra(
+ FUTURE_KEY);
+ future.complete(new GameSessionActivityResult(resultCode, data));
+ finish();
+ }
+}
diff --git a/core/java/android/service/games/IGameSession.aidl b/core/java/android/service/games/IGameSession.aidl
index 71da630..49c36c6 100644
--- a/core/java/android/service/games/IGameSession.aidl
+++ b/core/java/android/service/games/IGameSession.aidl
@@ -21,5 +21,6 @@
*/
oneway interface IGameSession {
void onDestroyed();
+ void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean visibleDueToGesture);
void onTaskFocusChanged(boolean focused);
}
diff --git a/core/java/android/service/games/IGameSessionController.aidl b/core/java/android/service/games/IGameSessionController.aidl
index 84311dc..fd99404 100644
--- a/core/java/android/service/games/IGameSessionController.aidl
+++ b/core/java/android/service/games/IGameSessionController.aidl
@@ -24,6 +24,6 @@
*/
oneway interface IGameSessionController {
void takeScreenshot(int taskId, in AndroidFuture gameScreenshotResultFuture);
- @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.FORCE_STOP_PACKAGES)")
+ @JavaPassthrough(annotation="@android.annotation.RequiresPermission(android.Manifest.permission.MANAGE_GAME_ACTIVITY)")
void restartGame(in int taskId);
}
diff --git a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
index 220e498..6b11e74 100644
--- a/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
+++ b/core/java/android/service/trust/ITrustAgentServiceCallback.aidl
@@ -26,6 +26,7 @@
oneway interface ITrustAgentServiceCallback {
void grantTrust(CharSequence message, long durationMs, int flags);
void revokeTrust();
+ void lockUser();
void setManagingTrust(boolean managingTrust);
void onConfigureCompleted(boolean result, IBinder token);
void addEscrowToken(in byte[] token, int userId);
diff --git a/core/java/android/service/trust/TrustAgentService.java b/core/java/android/service/trust/TrustAgentService.java
index fba61cf..8f6e1e0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -119,16 +119,15 @@
* automatically remove trust after some conditions are met (detailed below) with the option for
* the agent to renew the trust again later.
*
- * <p>After this is called, the agent will grant trust until the platform thinks an active user
- * is no longer using that trust. For example, if the user dismisses keyguard, the platform will
- * remove trust (this does not automatically lock the device).
+ * <p>After this is called, the agent will grant trust until the platform thinks an active
+ * user is no longer using that trust. This can happen for any reason as determined by the
+ * platform. For example, if the user dismisses keyguard, the platform will remove trust;
+ * since this does not automatically lock the device, this results in the device locking the
+ * next time the screen turns off.
*
* <p>When the platform internally removes the agent's trust in this manner, an agent can
* re-grant it (via a call to grantTrust) without the user having to unlock the device through
* another method (e.g. PIN). This renewable state only persists for a limited time.
- *
- * TODO(b/213631675): Remove @hide
- * @hide
*/
public static final int FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE = 1 << 2;
@@ -139,9 +138,6 @@
* Without this flag, the message passed to {@code grantTrust} is only used for debugging
* purposes. With the flag, it may be displayed to the user as the reason why the device is
* unlocked.
- *
- * TODO(b/213911325): Remove @hide
- * @hide
*/
public static final int FLAG_GRANT_TRUST_DISPLAY_MESSAGE = 1 << 3;
@@ -309,9 +305,6 @@
* {@link #grantTrust(CharSequence, long, int)}.
*
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
- *
- * TODO(b/213631672): Add CTS tests
- * @hide
*/
public void onUserRequestedUnlock() {
}
@@ -624,11 +617,15 @@
*
* If the user has no auth method specified, then keyguard will still be shown but can be
* dismissed normally.
- *
- * TODO(b/213631675): Implement & make public
- * @hide
*/
public final void lockUser() {
+ if (mCallback != null) {
+ try {
+ mCallback.lockUser();
+ } catch (RemoteException e) {
+ onError("calling lockUser");
+ }
+ }
}
/**
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
similarity index 64%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
copy to core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
index 861a4ed..ca75d2e 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/core/java/android/service/wallpapereffectsgeneration/IWallpaperEffectsGenerationService.aidl
@@ -1,3 +1,4 @@
+
/*
* Copyright (C) 2022 The Android Open Source Project
*
@@ -14,6 +15,14 @@
* limitations under the License.
*/
-package com.android.systemui.shared.mediattt;
+package android.service.wallpapereffectsgeneration;
-parcelable DeviceInfo;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+/**
+ * Interface from the system to WallpaperEffectsGeneration service.
+ *
+ * @hide
+ */
+oneway interface IWallpaperEffectsGenerationService {
+ void onGenerateCinematicEffect(in CinematicEffectRequest request);
+}
\ No newline at end of file
diff --git a/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java
new file mode 100644
index 0000000..18b654e
--- /dev/null
+++ b/core/java/android/service/wallpapereffectsgeneration/WallpaperEffectsGenerationService.java
@@ -0,0 +1,142 @@
+/*
+ * 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.service.wallpapereffectsgeneration;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
+
+import android.annotation.CallSuper;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.app.Service;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.Slog;
+
+/**
+ * A service for handling wallpaper effects generation tasks. It must implement
+ * (onGenerateCinematicEffect} method to generate response and call returnCinematicEffectResponse
+ * to send the response.
+ *
+ * <p>To extend this service, you must declare the service in your manifest file with the
+ * {@link android.Manifest.permission#BIND_WALLPAPER_EFFECTS_GENERATION} permission and includes
+ * an intent filter with the {@link #SERVICE_INTERFACE} action. For example: </p>
+ * <pre>
+ * <application>
+ * <service android:name=".CtsWallpaperEffectsGenerationService"
+ * android:exported="true"
+ * android:label="CtsWallpaperEffectsGenerationService"
+ * android:permission="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE">
+ * <intent-filter>
+ * <action android:name="android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService"
+ />
+ * </intent-filter>
+ * </service>
+ * <uses-library android:name="android.test.runner"/>
+ * </application>
+ * </pre>
+ *
+ * @hide
+ */
+@SystemApi
+public abstract class WallpaperEffectsGenerationService extends Service {
+ /**
+ * The {@link Intent} that must be declared as handled by the service.
+ *
+ * <p>The service must also require the
+ * {@link android.permission#MANAGE_WALLPAPER_EFFECTS_GENERATION}
+ * permission.
+ *
+ * @hide
+ */
+ public static final String SERVICE_INTERFACE =
+ "android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService";
+ private static final boolean DEBUG = false;
+ private static final String TAG = "WallpaperEffectsGenerationService";
+ private Handler mHandler;
+ private IWallpaperEffectsGenerationManager mService;
+
+ private final IWallpaperEffectsGenerationService mInterface =
+ new IWallpaperEffectsGenerationService.Stub() {
+ @Override
+ public void onGenerateCinematicEffect(CinematicEffectRequest request) {
+ mHandler.sendMessage(
+ obtainMessage(
+ WallpaperEffectsGenerationService::onGenerateCinematicEffect,
+ WallpaperEffectsGenerationService.this, request));
+ }
+ };
+
+ /**
+ * Called when the OS receives a request for generating cinematic effect. On receiving the
+ * request, it extract cinematic information from the input and call
+ * {@link #returnCinematicEffectResponse} with the textured mesh
+ * and metadata wrapped in CinematicEffectResponse.
+ *
+ * @param request the cinematic effect request passed from the client.
+ */
+ public abstract void onGenerateCinematicEffect(@NonNull CinematicEffectRequest request);
+
+ /**
+ * Returns the cinematic effect response. Must be called when cinematic effect
+ * response is generated and ready to be sent back. Otherwise the response won't be
+ * returned.
+ *
+ * @param response the cinematic effect response returned from service provider.
+ */
+ public final void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
+ try {
+ mService.returnCinematicEffectResponse(response);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ @CallSuper
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ if (DEBUG) {
+ Log.d(TAG, "onCreate WallpaperEffectsGenerationService");
+ }
+ mHandler = new Handler(Looper.getMainLooper(), null, true);
+ IBinder b = ServiceManager.getService(Context.WALLPAPER_EFFECTS_GENERATION_SERVICE);
+ mService = IWallpaperEffectsGenerationManager.Stub.asInterface(b);
+ }
+
+ @NonNull
+ @Override
+ public final IBinder onBind(@NonNull Intent intent) {
+ if (DEBUG) {
+ Log.d(TAG, "onBind WallpaperEffectsGenerationService");
+ }
+ if (SERVICE_INTERFACE.equals(intent.getAction())) {
+ return mInterface.asBinder();
+ }
+ Slog.w(TAG,
+ "Tried to bind to wrong intent (should be " + SERVICE_INTERFACE + ": " + intent);
+ return null;
+ }
+}
diff --git a/core/java/android/speech/IRecognitionListener.aidl b/core/java/android/speech/IRecognitionListener.aidl
index 7c79b1a..986a41c 100644
--- a/core/java/android/speech/IRecognitionListener.aidl
+++ b/core/java/android/speech/IRecognitionListener.aidl
@@ -78,6 +78,24 @@
void onPartialResults(in Bundle results);
/**
+ * Called for each ready segment of a recognition request. To request segmented speech results
+ * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called
+ * any number of times between {@link #onBeginningOfSpeech()} and
+ * {@link #onEndOfSegmentedSession()}.
+ *
+ * @param segmentResults the returned results. To retrieve the results in
+ * ArrayList<String> format use {@link Bundle#getStringArrayList(String)} with
+ * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
+ */
+ void onSegmentResults(in Bundle results);
+
+ /**
+ * Called at the end of a segmented recognition request. To request segmented speech results
+ * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+ */
+ void onEndOfSegmentedSession();
+
+ /**
* Reserved for adding future events.
*
* @param eventType the type of the occurred event
diff --git a/core/java/android/speech/RecognitionListener.java b/core/java/android/speech/RecognitionListener.java
index c94b60f..64fd09f 100644
--- a/core/java/android/speech/RecognitionListener.java
+++ b/core/java/android/speech/RecognitionListener.java
@@ -15,6 +15,7 @@
*/
package android.speech;
+import android.annotation.NonNull;
import android.content.Intent;
import android.os.Bundle;
@@ -69,7 +70,13 @@
/**
* Called when recognition results are ready.
- *
+ *
+ * <p>
+ * Called with the results for the full speech since {@link #onReadyForSpeech(Bundle)}.
+ * To get recognition results in segments rather than for the full session see
+ * {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+ * </p>
+ *
* @param results the recognition results. To retrieve the results in {@code
* ArrayList<String>} format use {@link Bundle#getStringArrayList(String)} with
* {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter. A float array of
@@ -92,6 +99,24 @@
void onPartialResults(Bundle partialResults);
/**
+ * Called for each ready segment of a recognition request. To request segmented speech results
+ * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}. The callback might be called
+ * any number of times between {@link #onReadyForSpeech(Bundle)} and
+ * {@link #onEndOfSegmentedSession()}.
+ *
+ * @param segmentResults the returned results. To retrieve the results in
+ * ArrayList<String> format use {@link Bundle#getStringArrayList(String)} with
+ * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
+ */
+ default void onSegmentResults(@NonNull Bundle segmentResults) {}
+
+ /**
+ * Called at the end of a segmented recognition request. To request segmented speech results
+ * use {@link RecognizerIntent#EXTRA_SEGMENT_SESSION}.
+ */
+ default void onEndOfSegmentedSession() {}
+
+ /**
* Reserved for adding future events.
*
* @param eventType the type of the occurred event
diff --git a/core/java/android/speech/RecognitionService.java b/core/java/android/speech/RecognitionService.java
index 5dbbc04..08136761 100644
--- a/core/java/android/speech/RecognitionService.java
+++ b/core/java/android/speech/RecognitionService.java
@@ -427,6 +427,26 @@
}
/**
+ * The service should call this method for each ready segment of a long recognition session.
+ *
+ * @param results the recognition results. To retrieve the results in {@code
+ * ArrayList<String>} format use {@link Bundle#getStringArrayList(String)} with
+ * {@link SpeechRecognizer#RESULTS_RECOGNITION} as a parameter
+ */
+ @SuppressLint({"CallbackMethodName", "RethrowRemoteException"})
+ public void segmentResults(@NonNull Bundle results) throws RemoteException {
+ mListener.onSegmentResults(results);
+ }
+
+ /**
+ * The service should call this method to end a segmented session.
+ */
+ @SuppressLint({"CallbackMethodName", "RethrowRemoteException"})
+ public void endOfSegmentedSession() throws RemoteException {
+ mListener.onEndOfSegmentedSession();
+ }
+
+ /**
* Return the Linux uid assigned to the process that sent you the current transaction that
* is being processed. This is obtained from {@link Binder#getCallingUid()}.
*/
diff --git a/core/java/android/speech/RecognizerIntent.java b/core/java/android/speech/RecognizerIntent.java
index 3183f15..271e307 100644
--- a/core/java/android/speech/RecognizerIntent.java
+++ b/core/java/android/speech/RecognizerIntent.java
@@ -426,4 +426,16 @@
*
*/
public static final String EXTRA_PREFER_OFFLINE = "android.speech.extra.PREFER_OFFLINE";
+
+ /**
+ * Optional boolean, when true and supported by the recognizer implementation it will split
+ * the recognition results in segments, returned via
+ * {@link RecognitionListener#onSegmentResults(Bundle)} and terminate the session with
+ * {@link RecognitionListener#onEndOfSegmentedSession()}. There will be no call to
+ * {@link RecognitionListener#onResults(Bundle)}. Callers can use
+ * {@link #EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS} and
+ * {@link #EXTRA_SPEECH_INPUT_COMPLETE_SILENCE_LENGTH_MILLIS} to tune how long the segments
+ * will be. Defaults to false.
+ */
+ public static final String EXTRA_SEGMENT_SESSION = "android.speech.extra.SEGMENT_SESSION";
}
diff --git a/core/java/android/speech/SpeechRecognizer.java b/core/java/android/speech/SpeechRecognizer.java
index 71c1e88..502558e 100644
--- a/core/java/android/speech/SpeechRecognizer.java
+++ b/core/java/android/speech/SpeechRecognizer.java
@@ -768,6 +768,8 @@
private static final int MSG_PARTIAL_RESULTS = 7;
private static final int MSG_RMS_CHANGED = 8;
private static final int MSG_ON_EVENT = 9;
+ private static final int MSG_SEGMENT_RESULTS = 10;
+ private static final int MSG_SEGMENT_END_SESSION = 11;
private final Handler mInternalHandler = new Handler(Looper.getMainLooper()) {
@Override
@@ -803,6 +805,12 @@
case MSG_ON_EVENT:
mInternalListener.onEvent(msg.arg1, (Bundle) msg.obj);
break;
+ case MSG_SEGMENT_RESULTS:
+ mInternalListener.onSegmentResults((Bundle) msg.obj);
+ break;
+ case MSG_SEGMENT_END_SESSION:
+ mInternalListener.onEndOfSegmentedSession();
+ break;
}
}
};
@@ -839,6 +847,14 @@
Message.obtain(mInternalHandler, MSG_RMS_CHANGED, rmsdB).sendToTarget();
}
+ public void onSegmentResults(final Bundle bundle) {
+ Message.obtain(mInternalHandler, MSG_SEGMENT_RESULTS, bundle).sendToTarget();
+ }
+
+ public void onEndOfSegmentedSession() {
+ Message.obtain(mInternalHandler, MSG_SEGMENT_END_SESSION).sendToTarget();
+ }
+
public void onEvent(final int eventType, final Bundle params) {
Message.obtain(mInternalHandler, MSG_ON_EVENT, eventType, eventType, params)
.sendToTarget();
diff --git a/core/java/android/util/Dumpable.java b/core/java/android/util/Dumpable.java
index 79c576d..955113d 100644
--- a/core/java/android/util/Dumpable.java
+++ b/core/java/android/util/Dumpable.java
@@ -35,8 +35,6 @@
return getClass().getName();
}
- //TODO(b/149254050): decide whether it should take a ParcelFileDescription as well.
-
/**
* Dumps the internal state into the given {@code writer}.
*
diff --git a/core/java/android/util/OWNERS b/core/java/android/util/OWNERS
index 5425c21..c199b96 100644
--- a/core/java/android/util/OWNERS
+++ b/core/java/android/util/OWNERS
@@ -1,6 +1,5 @@
per-file FeatureFlagUtils.java = sbasi@google.com
per-file FeatureFlagUtils.java = tmfang@google.com
-per-file FeatureFlagUtils.java = asapperstein@google.com
per-file AttributeSet.java = file:/core/java/android/content/res/OWNERS
per-file TypedValue.java = file:/core/java/android/content/res/OWNERS
diff --git a/core/java/android/view/ActionProvider.java b/core/java/android/view/ActionProvider.java
index e1be0fe..1680fd2 100644
--- a/core/java/android/view/ActionProvider.java
+++ b/core/java/android/view/ActionProvider.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.util.Log;
@@ -67,7 +69,7 @@
*
* @param context Context for accessing resources.
*/
- public ActionProvider(Context context) {
+ public ActionProvider(@NonNull Context context) {
}
/**
@@ -82,7 +84,7 @@
* @deprecated use {@link #onCreateActionView(MenuItem)}
*/
@Deprecated
- public abstract View onCreateActionView();
+ public abstract @NonNull View onCreateActionView();
/**
* Factory method called by the Android framework to create new action views.
@@ -96,7 +98,7 @@
* @param forItem MenuItem to create the action view for
* @return the new action view
*/
- public View onCreateActionView(MenuItem forItem) {
+ public @NonNull View onCreateActionView(@NonNull MenuItem forItem) {
return onCreateActionView();
}
@@ -200,7 +202,7 @@
*
* @param subMenu Submenu that will be displayed
*/
- public void onPrepareSubMenu(SubMenu subMenu) {
+ public void onPrepareSubMenu(@NonNull SubMenu subMenu) {
}
/**
@@ -220,7 +222,7 @@
* @hide Internal use only
*/
@UnsupportedAppUsage
- public void setSubUiVisibilityListener(SubUiVisibilityListener listener) {
+ public void setSubUiVisibilityListener(@Nullable SubUiVisibilityListener listener) {
mSubUiVisibilityListener = listener;
}
@@ -230,7 +232,7 @@
*
* @param listener listener to set
*/
- public void setVisibilityListener(VisibilityListener listener) {
+ public void setVisibilityListener(@Nullable VisibilityListener listener) {
if (mVisibilityListener != null) {
Log.w(TAG, "setVisibilityListener: Setting a new ActionProvider.VisibilityListener " +
"when one is already set. Are you reusing this " + getClass().getSimpleName() +
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/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 9b36b9b..b3b7f10 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -260,7 +260,7 @@
private final float mScale;
public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
- String cutoutSpec, @Rotation int rotation, float scale) {
+ @Nullable String cutoutSpec, @Rotation int rotation, float scale) {
mDisplayWidth = displayWidth;
mDisplayHeight = displayHeight;
mDensity = density;
@@ -269,7 +269,7 @@
mScale = scale;
}
- public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) {
+ public CutoutPathParserInfo(@NonNull CutoutPathParserInfo cutoutPathParserInfo) {
mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
mDensity = cutoutPathParserInfo.mDensity;
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
index 63a8300..57ba7e9 100644
--- a/core/java/android/view/GestureDetector.java
+++ b/core/java/android/view/GestureDetector.java
@@ -23,6 +23,8 @@
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__SINGLE_TAP;
import static com.android.internal.util.FrameworkStatsLog.TOUCH_GESTURE_CLASSIFIED__CLASSIFICATION__UNKNOWN_CLASSIFICATION;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.UiContext;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
@@ -68,7 +70,7 @@
*
* @param e The down motion event.
*/
- boolean onDown(MotionEvent e);
+ boolean onDown(@NonNull MotionEvent e);
/**
* The user has performed a down {@link MotionEvent} and not performed
@@ -78,7 +80,7 @@
*
* @param e The down motion event
*/
- void onShowPress(MotionEvent e);
+ void onShowPress(@NonNull MotionEvent e);
/**
* Notified when a tap occurs with the up {@link MotionEvent}
@@ -87,7 +89,7 @@
* @param e The up motion event that completed the first tap
* @return true if the event is consumed, else false
*/
- boolean onSingleTapUp(MotionEvent e);
+ boolean onSingleTapUp(@NonNull MotionEvent e);
/**
* Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
@@ -104,7 +106,8 @@
* and {@code e2}.
* @return true if the event is consumed, else false
*/
- boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
+ boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float distanceX,
+ float distanceY);
/**
* Notified when a long press occurs with the initial on down {@link MotionEvent}
@@ -112,7 +115,7 @@
*
* @param e The initial on down motion event that started the longpress.
*/
- void onLongPress(MotionEvent e);
+ void onLongPress(@NonNull MotionEvent e);
/**
* Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
@@ -127,7 +130,8 @@
* along the y axis.
* @return true if the event is consumed, else false
*/
- boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
+ boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
+ float velocityY);
}
/**
@@ -146,7 +150,7 @@
* @param e The down motion event of the single-tap.
* @return true if the event is consumed, else false
*/
- boolean onSingleTapConfirmed(MotionEvent e);
+ boolean onSingleTapConfirmed(@NonNull MotionEvent e);
/**
* Notified when a double-tap occurs. Triggered on the down event of second tap.
@@ -154,7 +158,7 @@
* @param e The down motion event of the first tap of the double-tap.
* @return true if the event is consumed, else false
*/
- boolean onDoubleTap(MotionEvent e);
+ boolean onDoubleTap(@NonNull MotionEvent e);
/**
* Notified when an event within a double-tap gesture occurs, including
@@ -163,7 +167,7 @@
* @param e The motion event that occurred during the double-tap gesture.
* @return true if the event is consumed, else false
*/
- boolean onDoubleTapEvent(MotionEvent e);
+ boolean onDoubleTapEvent(@NonNull MotionEvent e);
}
/**
@@ -178,7 +182,7 @@
* @param e The motion event that occurred during the context click.
* @return true if the event is consumed, else false
*/
- boolean onContextClick(MotionEvent e);
+ boolean onContextClick(@NonNull MotionEvent e);
}
/**
@@ -190,43 +194,43 @@
public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener,
OnContextClickListener {
- public boolean onSingleTapUp(MotionEvent e) {
+ public boolean onSingleTapUp(@NonNull MotionEvent e) {
return false;
}
- public void onLongPress(MotionEvent e) {
+ public void onLongPress(@NonNull MotionEvent e) {
}
- public boolean onScroll(MotionEvent e1, MotionEvent e2,
+ public boolean onScroll(@NonNull MotionEvent e1, @NonNull MotionEvent e2,
float distanceX, float distanceY) {
return false;
}
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+ public boolean onFling(@NonNull MotionEvent e1, @NonNull MotionEvent e2, float velocityX,
float velocityY) {
return false;
}
- public void onShowPress(MotionEvent e) {
+ public void onShowPress(@NonNull MotionEvent e) {
}
- public boolean onDown(MotionEvent e) {
+ public boolean onDown(@NonNull MotionEvent e) {
return false;
}
- public boolean onDoubleTap(MotionEvent e) {
+ public boolean onDoubleTap(@NonNull MotionEvent e) {
return false;
}
- public boolean onDoubleTapEvent(MotionEvent e) {
+ public boolean onDoubleTapEvent(@NonNull MotionEvent e) {
return false;
}
- public boolean onSingleTapConfirmed(MotionEvent e) {
+ public boolean onSingleTapConfirmed(@NonNull MotionEvent e) {
return false;
}
- public boolean onContextClick(MotionEvent e) {
+ public boolean onContextClick(@NonNull MotionEvent e) {
return false;
}
}
@@ -348,14 +352,13 @@
* not be null.
* @param handler the handler to use
*
- * @throws NullPointerException if either {@code listener} or
- * {@code handler} is null.
+ * @throws NullPointerException if {@code listener} is null.
*
* @deprecated Use {@link #GestureDetector(android.content.Context,
* android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
*/
@Deprecated
- public GestureDetector(OnGestureListener listener, Handler handler) {
+ public GestureDetector(@NonNull OnGestureListener listener, @Nullable Handler handler) {
this(null, listener, handler);
}
@@ -373,7 +376,7 @@
* android.view.GestureDetector.OnGestureListener)} instead.
*/
@Deprecated
- public GestureDetector(OnGestureListener listener) {
+ public GestureDetector(@NonNull OnGestureListener listener) {
this(null, listener, null);
}
@@ -392,7 +395,8 @@
* @throws NullPointerException if {@code listener} is null.
*/
// TODO(b/182007470): Use @ConfigurationContext instead
- public GestureDetector(@UiContext Context context, OnGestureListener listener) {
+ public GestureDetector(@Nullable @UiContext Context context,
+ @NonNull OnGestureListener listener) {
this(context, listener, null);
}
@@ -411,8 +415,8 @@
*
* @throws NullPointerException if {@code listener} is null.
*/
- public GestureDetector(@UiContext Context context, OnGestureListener listener,
- Handler handler) {
+ public GestureDetector(@Nullable @UiContext Context context,
+ @NonNull OnGestureListener listener, @Nullable Handler handler) {
if (handler != null) {
mHandler = new GestureHandler(handler);
} else {
@@ -442,8 +446,8 @@
*
* @throws NullPointerException if {@code listener} is null.
*/
- public GestureDetector(@UiContext Context context, OnGestureListener listener, Handler handler,
- boolean unused) {
+ public GestureDetector(@Nullable @UiContext Context context,
+ @NonNull OnGestureListener listener, @Nullable Handler handler, boolean unused) {
this(context, listener, handler);
}
@@ -486,7 +490,7 @@
* @param onDoubleTapListener the listener invoked for all the callbacks, or
* null to stop listening for double-tap gestures.
*/
- public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
+ public void setOnDoubleTapListener(@Nullable OnDoubleTapListener onDoubleTapListener) {
mDoubleTapListener = onDoubleTapListener;
}
@@ -496,7 +500,7 @@
* @param onContextClickListener the listener invoked for all the callbacks, or null to stop
* listening for context clicks.
*/
- public void setContextClickListener(OnContextClickListener onContextClickListener) {
+ public void setContextClickListener(@Nullable OnContextClickListener onContextClickListener) {
mContextClickListener = onContextClickListener;
}
@@ -528,7 +532,7 @@
* @return true if the {@link OnGestureListener} consumed the event,
* else false.
*/
- public boolean onTouchEvent(MotionEvent ev) {
+ public boolean onTouchEvent(@NonNull MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(ev, 0);
}
@@ -800,7 +804,7 @@
* @return true if the {@link OnGestureListener} consumed the event,
* else false.
*/
- public boolean onGenericMotionEvent(MotionEvent ev) {
+ public boolean onGenericMotionEvent(@NonNull MotionEvent ev) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onGenericMotionEvent(ev, 0);
}
@@ -860,8 +864,8 @@
mIgnoreNextUpEvent = false;
}
- private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
- MotionEvent secondDown) {
+ private boolean isConsideredDoubleTap(@NonNull MotionEvent firstDown,
+ @NonNull MotionEvent firstUp, @NonNull MotionEvent secondDown) {
if (!mAlwaysInBiggerTapRegion) {
return false;
}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
index c8bfd36..b4cdc6e 100644
--- a/core/java/android/view/Gravity.java
+++ b/core/java/android/view/Gravity.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.graphics.Rect;
import java.lang.annotation.Retention;
@@ -187,8 +188,8 @@
* @see View#LAYOUT_DIRECTION_LTR
* @see View#LAYOUT_DIRECTION_RTL
*/
- public static void apply(int gravity, int w, int h, Rect container,
- Rect outRect, int layoutDirection) {
+ public static void apply(int gravity, int w, int h, @NonNull Rect container,
+ @NonNull Rect outRect, int layoutDirection) {
int absGravity = getAbsoluteGravity(gravity, layoutDirection);
apply(absGravity, w, h, container, 0, 0, outRect);
}
@@ -214,8 +215,8 @@
* @param outRect Receives the computed frame of the object in its
* container.
*/
- public static void apply(int gravity, int w, int h, Rect container,
- int xAdj, int yAdj, Rect outRect) {
+ public static void apply(int gravity, int w, int h, @NonNull Rect container,
+ int xAdj, int yAdj, @NonNull Rect outRect) {
switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
case 0:
outRect.left = container.left
@@ -324,8 +325,8 @@
* @see View#LAYOUT_DIRECTION_LTR
* @see View#LAYOUT_DIRECTION_RTL
*/
- public static void apply(int gravity, int w, int h, Rect container,
- int xAdj, int yAdj, Rect outRect, int layoutDirection) {
+ public static void apply(int gravity, int w, int h, @NonNull Rect container,
+ int xAdj, int yAdj, @NonNull Rect outRect, int layoutDirection) {
int absGravity = getAbsoluteGravity(gravity, layoutDirection);
apply(absGravity, w, h, container, xAdj, yAdj, outRect);
}
@@ -346,7 +347,7 @@
* @param inoutObj Supplies the current object position; returns with it
* modified if needed to fit in the display.
*/
- public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
+ public static void applyDisplay(int gravity, @NonNull Rect display, @NonNull Rect inoutObj) {
if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
if (inoutObj.top < display.top) inoutObj.top = display.top;
if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
@@ -404,7 +405,8 @@
* @see View#LAYOUT_DIRECTION_LTR
* @see View#LAYOUT_DIRECTION_RTL
*/
- public static void applyDisplay(int gravity, Rect display, Rect inoutObj, int layoutDirection) {
+ public static void applyDisplay(int gravity, @NonNull Rect display, @NonNull Rect inoutObj,
+ int layoutDirection) {
int absGravity = getAbsoluteGravity(gravity, layoutDirection);
applyDisplay(absGravity, display, inoutObj);
}
diff --git a/core/java/android/view/InputMonitor.java b/core/java/android/view/InputMonitor.java
index ad1f201..8801fe0 100644
--- a/core/java/android/view/InputMonitor.java
+++ b/core/java/android/view/InputMonitor.java
@@ -79,13 +79,17 @@
- // Code below generated by codegen v1.0.7.
+ // 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/view/InputMonitor.java
+ //
+ // To exclude the generated code from IntelliJ auto-formatting enable (one-time):
+ // Settings > Editor > Code Style > Formatter Control
+ //@formatter:off
@DataClass.Generated.Member
@@ -126,7 +130,7 @@
@Override
@DataClass.Generated.Member
- public void writeToParcel(Parcel dest, int flags) {
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
// You can override field parcelling by defining methods like:
// void parcelFieldName(Parcel dest, int flags) { ... }
@@ -141,7 +145,7 @@
/** @hide */
@SuppressWarnings({"unchecked", "RedundantCast"})
@DataClass.Generated.Member
- /* package-private */ InputMonitor(Parcel in) {
+ /* package-private */ InputMonitor(@NonNull Parcel in) {
// You can override field unparcelling by defining methods like:
// static FieldType unparcelFieldName(Parcel in) { ... }
@@ -167,17 +171,21 @@
}
@Override
- public InputMonitor createFromParcel(Parcel in) {
+ public InputMonitor createFromParcel(@NonNull Parcel in) {
return new InputMonitor(in);
}
};
@DataClass.Generated(
- time = 1571177265149L,
- codegenVersion = "1.0.7",
+ time = 1637697281750L,
+ codegenVersion = "1.0.23",
sourceFile = "frameworks/base/core/java/android/view/InputMonitor.java",
inputSignatures = "private static final java.lang.String TAG\nprivate static final boolean DEBUG\nprivate final @android.annotation.NonNull android.view.InputChannel mInputChannel\nprivate final @android.annotation.NonNull android.view.IInputMonitorHost mHost\npublic void pilferPointers()\npublic void dispose()\nclass InputMonitor extends java.lang.Object implements [android.os.Parcelable]\n@com.android.internal.util.DataClass(genToString=true)")
@Deprecated
private void __metadata() {}
+
+ //@formatter:on
+ // End of generated code
+
}
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
index a2fb596..bc46d55 100644
--- a/core/java/android/view/MenuItem.java
+++ b/core/java/android/view/MenuItem.java
@@ -91,7 +91,7 @@
* @return Return true to consume this click and prevent others from
* executing.
*/
- public boolean onMenuItemClick(MenuItem item);
+ public boolean onMenuItemClick(@NonNull MenuItem item);
}
/**
@@ -110,7 +110,7 @@
* @param item Item that was expanded
* @return true if the item should expand, false if expansion should be suppressed.
*/
- public boolean onMenuItemActionExpand(MenuItem item);
+ public boolean onMenuItemActionExpand(@NonNull MenuItem item);
/**
* Called when a menu item with {@link MenuItem#SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW}
@@ -118,7 +118,7 @@
* @param item Item that was collapsed
* @return true if the item should collapse, false if collapsing should be suppressed.
*/
- public boolean onMenuItemActionCollapse(MenuItem item);
+ public boolean onMenuItemActionCollapse(@NonNull MenuItem item);
}
/**
@@ -159,7 +159,7 @@
* @param title The new text to be displayed.
* @return This Item so additional setters can be called.
*/
- public MenuItem setTitle(CharSequence title);
+ public @NonNull MenuItem setTitle(@Nullable CharSequence title);
/**
* Change the title associated with this item.
@@ -173,14 +173,14 @@
* @see #setTitleCondensed(CharSequence)
*/
- public MenuItem setTitle(@StringRes int title);
+ public @NonNull MenuItem setTitle(@StringRes int title);
/**
* Retrieve the current title of the item.
*
* @return The title.
*/
- public CharSequence getTitle();
+ public @Nullable CharSequence getTitle();
/**
* Change the condensed title associated with this item. The condensed
@@ -190,7 +190,7 @@
* @param title The new text to be displayed as the condensed title.
* @return This Item so additional setters can be called.
*/
- public MenuItem setTitleCondensed(CharSequence title);
+ public @NonNull MenuItem setTitleCondensed(@Nullable CharSequence title);
/**
* Retrieve the current condensed title of the item. If a condensed
@@ -199,7 +199,7 @@
* @return The condensed title, if it exists.
* Otherwise the normal title.
*/
- public CharSequence getTitleCondensed();
+ public @Nullable CharSequence getTitleCondensed();
/**
* Change the icon associated with this item. This icon will not always be
@@ -209,7 +209,7 @@
* @param icon The new icon (as a Drawable) to be displayed.
* @return This Item so additional setters can be called.
*/
- public MenuItem setIcon(Drawable icon);
+ public @NonNull MenuItem setIcon(@Nullable Drawable icon);
/**
* Change the icon associated with this item. This icon will not always be
@@ -222,7 +222,7 @@
* @param iconRes The new icon (as a resource ID) to be displayed.
* @return This Item so additional setters can be called.
*/
- public MenuItem setIcon(@DrawableRes int iconRes);
+ public @NonNull MenuItem setIcon(@DrawableRes int iconRes);
/**
* Returns the icon for this item as a Drawable (getting it from resources if it hasn't been
@@ -233,7 +233,7 @@
*
* @return The icon as a Drawable.
*/
- public Drawable getIcon();
+ public @Nullable Drawable getIcon();
/**
* Applies a tint to this item's icon. Does not modify the
@@ -250,15 +250,14 @@
* @see #getIconTintList()
* @see Drawable#setTintList(ColorStateList)
*/
- public default MenuItem setIconTintList(@Nullable ColorStateList tint) { return this; }
+ public default @NonNull MenuItem setIconTintList(@Nullable ColorStateList tint) { return this; }
/**
* @return the tint applied to this item's icon
* @attr ref android.R.styleable#MenuItem_iconTint
* @see #setIconTintList(ColorStateList)
*/
- @Nullable
- public default ColorStateList getIconTintList() { return null; }
+ public default @Nullable ColorStateList getIconTintList() { return null; }
/**
* Specifies the blending mode used to apply the tint specified by
@@ -304,8 +303,7 @@
* @see #setIconTintBlendMode(BlendMode)
*
*/
- @Nullable
- public default PorterDuff.Mode getIconTintMode() { return null; }
+ public default @Nullable PorterDuff.Mode getIconTintMode() { return null; }
/**
* Returns the blending mode used to apply the tint to this item's icon, if specified.
@@ -315,8 +313,7 @@
* @see #setIconTintBlendMode(BlendMode)
*
*/
- @Nullable
- default BlendMode getIconTintBlendMode() {
+ default @Nullable BlendMode getIconTintBlendMode() {
PorterDuff.Mode mode = getIconTintMode();
if (mode != null) {
return BlendMode.fromValue(mode.nativeInt);
@@ -343,7 +340,7 @@
* modify it later.
* @return This Item so additional setters can be called.
*/
- public MenuItem setIntent(Intent intent);
+ public @NonNull MenuItem setIntent(@Nullable Intent intent);
/**
* Return the Intent associated with this item. This returns a
@@ -354,7 +351,7 @@
* @return Returns the last value supplied to {@link #setIntent}, or
* null.
*/
- public Intent getIntent();
+ public @Nullable Intent getIntent();
/**
* Change both the numeric and alphabetic shortcut associated with this
@@ -372,7 +369,7 @@
* using a keyboard with alphabetic keys.
* @return This Item so additional setters can be called.
*/
- public MenuItem setShortcut(char numericChar, char alphaChar);
+ public @NonNull MenuItem setShortcut(char numericChar, char alphaChar);
/**
* Change both the numeric and alphabetic shortcut associated with this
@@ -397,8 +394,8 @@
* {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
* @return This Item so additional setters can be called.
*/
- default public MenuItem setShortcut(char numericChar, char alphaChar, int numericModifiers,
- int alphaModifiers) {
+ default public @NonNull MenuItem setShortcut(char numericChar, char alphaChar,
+ int numericModifiers, int alphaModifiers) {
if ((alphaModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON
&& (numericModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) {
return setShortcut(numericChar, alphaChar);
@@ -416,7 +413,7 @@
* using a 12-key (numeric) keyboard.
* @return This Item so additional setters can be called.
*/
- public MenuItem setNumericShortcut(char numericChar);
+ public @NonNull MenuItem setNumericShortcut(char numericChar);
/**
* Change the numeric shortcut and modifiers associated with this item.
@@ -431,7 +428,7 @@
* {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
* @return This Item so additional setters can be called.
*/
- default public MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
+ default public @NonNull MenuItem setNumericShortcut(char numericChar, int numericModifiers) {
if ((numericModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) {
return setNumericShortcut(numericChar);
} else {
@@ -475,7 +472,7 @@
* using a keyboard with alphabetic keys.
* @return This Item so additional setters can be called.
*/
- public MenuItem setAlphabeticShortcut(char alphaChar);
+ public @NonNull MenuItem setAlphabeticShortcut(char alphaChar);
/**
* Change the alphabetic shortcut associated with this item. The shortcut
@@ -495,7 +492,7 @@
* {@link KeyEvent#META_SYM_ON}, {@link KeyEvent#META_FUNCTION_ON}.
* @return This Item so additional setters can be called.
*/
- default public MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
+ default public @NonNull MenuItem setAlphabeticShortcut(char alphaChar, int alphaModifiers) {
if ((alphaModifiers & Menu.SUPPORTED_MODIFIERS_MASK) == KeyEvent.META_CTRL_ON) {
return setAlphabeticShortcut(alphaChar);
} else {
@@ -539,7 +536,7 @@
* @see Menu#setGroupCheckable
* @return This Item so additional setters can be called.
*/
- public MenuItem setCheckable(boolean checkable);
+ public @NonNull MenuItem setCheckable(boolean checkable);
/**
* Return whether the item can currently display a check mark.
@@ -566,7 +563,7 @@
* it. The default value is false.
* @return This Item so additional setters can be called.
*/
- public MenuItem setChecked(boolean checked);
+ public @NonNull MenuItem setChecked(boolean checked);
/**
* Return whether the item is currently displaying a check mark.
@@ -586,7 +583,7 @@
* hidden.
* @return This Item so additional setters can be called.
*/
- public MenuItem setVisible(boolean visible);
+ public @NonNull MenuItem setVisible(boolean visible);
/**
* Return the visibility of the menu item.
@@ -604,7 +601,7 @@
* won't be invokable.
* @return This Item so additional setters can be called.
*/
- public MenuItem setEnabled(boolean enabled);
+ public @NonNull MenuItem setEnabled(boolean enabled);
/**
* Return the enabled state of the menu item.
@@ -628,7 +625,7 @@
*
* @return The associated menu if there is one, else null
*/
- public SubMenu getSubMenu();
+ public @Nullable SubMenu getSubMenu();
/**
* Set a custom listener for invocation of this menu item. In most
@@ -641,7 +638,8 @@
* @see Activity#onOptionsItemSelected(MenuItem)
* @see Activity#onContextItemSelected(MenuItem)
*/
- public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener);
+ public @NonNull MenuItem setOnMenuItemClickListener(
+ @Nullable MenuItem.OnMenuItemClickListener menuItemClickListener);
/**
* Gets the extra information linked to this menu item. This extra
@@ -652,7 +650,7 @@
* @return The extra information linked to the View that added this
* menu item to the menu. This can be null.
*/
- public ContextMenuInfo getMenuInfo();
+ public @Nullable ContextMenuInfo getMenuInfo();
/**
* Sets how this item should display in the presence of an Action Bar.
@@ -690,7 +688,7 @@
* @see #setActionView(View)
* @return This MenuItem instance for call chaining.
*/
- public MenuItem setShowAsActionFlags(int actionEnum);
+ public @NonNull MenuItem setShowAsActionFlags(int actionEnum);
/**
* Set an action view for this menu item. An action view will be displayed in place
@@ -706,7 +704,7 @@
*
* @see #setShowAsAction(int)
*/
- public MenuItem setActionView(View view);
+ public @NonNull MenuItem setActionView(@Nullable View view);
/**
* Set an action view for this menu item. An action view will be displayed in place
@@ -722,7 +720,7 @@
*
* @see #setShowAsAction(int)
*/
- public MenuItem setActionView(@LayoutRes int resId);
+ public @NonNull MenuItem setActionView(@LayoutRes int resId);
/**
* Returns the currently set action view for this menu item.
@@ -732,7 +730,7 @@
* @see #setActionView(View)
* @see #setShowAsAction(int)
*/
- public View getActionView();
+ public @Nullable View getActionView();
/**
* Sets the {@link ActionProvider} responsible for creating an action view if
@@ -748,7 +746,7 @@
*
* @see ActionProvider
*/
- public MenuItem setActionProvider(ActionProvider actionProvider);
+ public @NonNull MenuItem setActionProvider(@Nullable ActionProvider actionProvider);
/**
* Gets the {@link ActionProvider}.
@@ -758,7 +756,7 @@
* @see ActionProvider
* @see #setActionProvider(ActionProvider)
*/
- public ActionProvider getActionProvider();
+ public @Nullable ActionProvider getActionProvider();
/**
* Expand the action view associated with this menu item.
@@ -806,14 +804,14 @@
* @param listener Listener that will respond to expand/collapse events
* @return This menu item instance for call chaining
*/
- public MenuItem setOnActionExpandListener(OnActionExpandListener listener);
+ public @NonNull MenuItem setOnActionExpandListener(@Nullable OnActionExpandListener listener);
/**
* Change the content description associated with this menu item.
*
* @param contentDescription The new content description.
*/
- default MenuItem setContentDescription(CharSequence contentDescription) {
+ default @NonNull MenuItem setContentDescription(@Nullable CharSequence contentDescription) {
return this;
}
@@ -822,7 +820,7 @@
*
* @return The content description.
*/
- default CharSequence getContentDescription() {
+ default @Nullable CharSequence getContentDescription() {
return null;
}
@@ -831,7 +829,7 @@
*
* @param tooltipText The new tooltip text.
*/
- default MenuItem setTooltipText(CharSequence tooltipText) {
+ default @NonNull MenuItem setTooltipText(@Nullable CharSequence tooltipText) {
return this;
}
@@ -840,7 +838,7 @@
*
* @return The tooltip text.
*/
- default CharSequence getTooltipText() {
+ default @Nullable CharSequence getTooltipText() {
return null;
}
diff --git a/core/java/android/view/OnBackInvokedDispatcher.java b/core/java/android/view/OnBackInvokedDispatcher.java
index 05c312b..5c4ee89 100644
--- a/core/java/android/view/OnBackInvokedDispatcher.java
+++ b/core/java/android/view/OnBackInvokedDispatcher.java
@@ -17,8 +17,14 @@
package android.view;
import android.annotation.IntDef;
+import android.annotation.IntRange;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SuppressLint;
+import android.annotation.TestApi;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledSince;
+import android.os.Build;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -31,25 +37,56 @@
* Attribute updates are proactively pushed to the window manager if they change the dispatch
* target (a.k.a. the callback to be invoked next), or its behavior.
*/
-public abstract class OnBackInvokedDispatcher {
+public interface OnBackInvokedDispatcher {
+ /**
+ * Enables dispatching the "back" action via {@link android.view.OnBackInvokedDispatcher}.
+ *
+ * When enabled, the following APIs are no longer invoked:
+ * <ul>
+ * <li> {@link android.app.Activity#onBackPressed}
+ * <li> {@link android.app.Dialog#onBackPressed}
+ * <li> {@link android.view.KeyEvent#KEYCODE_BACK} is no longer dispatched.
+ * </ul>
+ *
+ * @hide
+ */
+ @TestApi
+ @ChangeId
+ @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
+ long DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME = 195946584L;
+
+ /** @hide */
+ String TAG = "OnBackInvokedDispatcher";
+
+ /** @hide */
+ boolean DEBUG = Build.isDebuggable();
+
/** @hide */
@IntDef({
PRIORITY_DEFAULT,
PRIORITY_OVERLAY,
})
@Retention(RetentionPolicy.SOURCE)
- public @interface Priority{}
+ @interface Priority{}
/**
* Priority level of {@link OnBackInvokedCallback}s for overlays such as menus and
* navigation drawers that should receive back dispatch before non-overlays.
*/
- public static final int PRIORITY_OVERLAY = 1000000;
+ int PRIORITY_OVERLAY = 1000000;
/**
* Default priority level of {@link OnBackInvokedCallback}s.
*/
- public static final int PRIORITY_DEFAULT = 0;
+ int PRIORITY_DEFAULT = 0;
+
+ /**
+ * Priority level of {@link OnBackInvokedCallback}s registered by the system.
+ *
+ * System back animation will play when the callback to receive dispatch has this priority.
+ * @hide
+ */
+ int PRIORITY_SYSTEM = -1;
/**
* Registers a {@link OnBackInvokedCallback}.
@@ -61,10 +98,11 @@
* registered, the existing instance (no matter its priority) will be
* unregistered and registered again.
* @param priority The priority of the callback.
+ * @throws {@link IllegalArgumentException} if the priority is negative.
*/
@SuppressLint("SamShouldBeLast")
- public abstract void registerOnBackInvokedCallback(
- @NonNull OnBackInvokedCallback callback, @Priority int priority);
+ void registerOnBackInvokedCallback(
+ @NonNull OnBackInvokedCallback callback, @Priority @IntRange(from = 0) int priority);
/**
* Unregisters a {@link OnBackInvokedCallback}.
@@ -72,5 +110,20 @@
* @param callback The callback to be unregistered. Does nothing if the callback has not been
* registered.
*/
- public abstract void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+ void unregisterOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback);
+
+ /**
+ * Returns the most prioritized callback to receive back dispatch next.
+ * @hide
+ */
+ @Nullable
+ default OnBackInvokedCallback getTopCallback() {
+ return null;
+ }
+
+ /**
+ * Registers a {@link OnBackInvokedCallback} with system priority.
+ * @hide
+ */
+ default void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) { }
}
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/PointerIcon.java b/core/java/android/view/PointerIcon.java
index 38ae03d..2a8e7e4 100644
--- a/core/java/android/view/PointerIcon.java
+++ b/core/java/android/view/PointerIcon.java
@@ -184,7 +184,7 @@
* @see #TYPE_NULL
* @hide
*/
- public static PointerIcon getNullIcon() {
+ public static @NonNull PointerIcon getNullIcon() {
return gNullIcon;
}
@@ -197,7 +197,7 @@
* @throws IllegalArgumentException if context is null.
* @hide
*/
- public static PointerIcon getDefaultIcon(@NonNull Context context) {
+ public static @NonNull PointerIcon getDefaultIcon(@NonNull Context context) {
return getSystemIcon(context, TYPE_DEFAULT);
}
@@ -211,7 +211,7 @@
*
* @throws IllegalArgumentException if context is null.
*/
- public static PointerIcon getSystemIcon(@NonNull Context context, int type) {
+ public static @NonNull PointerIcon getSystemIcon(@NonNull Context context, int type) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -287,7 +287,8 @@
* @throws IllegalArgumentException if bitmap is null, or if the x/y hotspot
* parameters are invalid.
*/
- public static PointerIcon create(@NonNull Bitmap bitmap, float hotSpotX, float hotSpotY) {
+ public static @NonNull PointerIcon create(@NonNull Bitmap bitmap, float hotSpotX,
+ float hotSpotY) {
if (bitmap == null) {
throw new IllegalArgumentException("bitmap must not be null");
}
@@ -321,7 +322,7 @@
* @throws Resources.NotFoundException if the resource was not found or the drawable
* linked in the resource was not found.
*/
- public static PointerIcon load(@NonNull Resources resources, @XmlRes int resourceId) {
+ public static @NonNull PointerIcon load(@NonNull Resources resources, @XmlRes int resourceId) {
if (resources == null) {
throw new IllegalArgumentException("resources must not be null");
}
@@ -342,7 +343,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- public PointerIcon load(@NonNull Context context) {
+ public @NonNull PointerIcon load(@NonNull Context context) {
if (context == null) {
throw new IllegalArgumentException("context must not be null");
}
@@ -362,7 +363,7 @@
return mType;
}
- public static final @android.annotation.NonNull Parcelable.Creator<PointerIcon> CREATOR
+ public static final @NonNull Parcelable.Creator<PointerIcon> CREATOR
= new Parcelable.Creator<PointerIcon>() {
public PointerIcon createFromParcel(Parcel in) {
int type = in.readInt();
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
index 346f76c..a0a172d 100644
--- a/core/java/android/view/ScaleGestureDetector.java
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -16,6 +16,8 @@
package android.view;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.os.Build;
@@ -67,7 +69,7 @@
* only wants to update scaling factors if the change is
* greater than 0.01.
*/
- public boolean onScale(ScaleGestureDetector detector);
+ public boolean onScale(@NonNull ScaleGestureDetector detector);
/**
* Responds to the beginning of a scaling gesture. Reported by
@@ -81,7 +83,7 @@
* sense, onScaleBegin() may return false to ignore the
* rest of the gesture.
*/
- public boolean onScaleBegin(ScaleGestureDetector detector);
+ public boolean onScaleBegin(@NonNull ScaleGestureDetector detector);
/**
* Responds to the end of a scale gesture. Reported by existing
@@ -94,7 +96,7 @@
* @param detector The detector reporting the event - use this to
* retrieve extended info about event state.
*/
- public void onScaleEnd(ScaleGestureDetector detector);
+ public void onScaleEnd(@NonNull ScaleGestureDetector detector);
}
/**
@@ -109,15 +111,15 @@
*/
public static class SimpleOnScaleGestureListener implements OnScaleGestureListener {
- public boolean onScale(ScaleGestureDetector detector) {
+ public boolean onScale(@NonNull ScaleGestureDetector detector) {
return false;
}
- public boolean onScaleBegin(ScaleGestureDetector detector) {
+ public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) {
return true;
}
- public void onScaleEnd(ScaleGestureDetector detector) {
+ public void onScaleEnd(@NonNull ScaleGestureDetector detector) {
// Intentionally empty
}
}
@@ -180,7 +182,8 @@
*
* @throws NullPointerException if {@code listener} is null.
*/
- public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+ public ScaleGestureDetector(@NonNull Context context,
+ @NonNull OnScaleGestureListener listener) {
this(context, listener, null);
}
@@ -195,8 +198,8 @@
*
* @throws NullPointerException if {@code listener} is null.
*/
- public ScaleGestureDetector(Context context, OnScaleGestureListener listener,
- Handler handler) {
+ public ScaleGestureDetector(@NonNull Context context, @NonNull OnScaleGestureListener listener,
+ @Nullable Handler handler) {
mContext = context;
mListener = listener;
final ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
@@ -226,7 +229,7 @@
* @return true if the event was processed and the detector wants to receive the
* rest of the MotionEvents in this event stream.
*/
- public boolean onTouchEvent(MotionEvent event) {
+ public boolean onTouchEvent(@NonNull MotionEvent event) {
if (mInputEventConsistencyVerifier != null) {
mInputEventConsistencyVerifier.onTouchEvent(event, 0);
}
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 904d7c8..8f8c5a9 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");
@@ -2694,6 +2731,17 @@
}
/**
+ * Interface to handle request to
+ * {@link SurfaceControl.Transaction#addTransactionCommittedListener(Executor, TransactionCommittedListener)}
+ */
+ public interface TransactionCommittedListener {
+ /**
+ * Invoked when the transaction has been committed in SurfaceFlinger.
+ */
+ void onTransactionCommitted();
+ }
+
+ /**
* An atomic set of changes to a set of SurfaceControl.
*/
public static class Transaction implements Closeable, Parcelable {
diff --git a/core/java/android/view/TransactionCommittedListener.java b/core/java/android/view/TransactionCommittedListener.java
deleted file mode 100644
index 6abded2..0000000
--- a/core/java/android/view/TransactionCommittedListener.java
+++ /dev/null
@@ -1,30 +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 android.view;
-
-import java.util.concurrent.Executor;
-
-/**
- * Interface to handle request to
- * {@link SurfaceControl.Transaction#addTransactionCommittedListener(Executor, TransactionCommittedListener)}
- */
-public interface TransactionCommittedListener {
- /**
- * Invoked when the transaction has been committed in SurfaceFlinger.
- */
- void onTransactionCommitted();
-}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 4ff7e229..70689bd 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;
@@ -8608,7 +8608,7 @@
*
* @hide
*/
- public AccessibilityNodeInfo createAccessibilityNodeInfoInternal() {
+ public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfoInternal() {
AccessibilityNodeProvider provider = getAccessibilityNodeProvider();
if (provider != null) {
return provider.createAccessibilityNodeInfo(AccessibilityNodeProvider.HOST_VIEW_ID);
@@ -12290,7 +12290,7 @@
/**
* @return whether this view should have haptic feedback enabled for events
- * long presses.
+ * such as long presses.
*
* @see #setHapticFeedbackEnabled(boolean)
* @see #performHapticFeedback(int)
@@ -14226,7 +14226,8 @@
* @param arguments Optional action arguments
* @return true if the action was consumed by a parent
*/
- public boolean dispatchNestedPrePerformAccessibilityAction(int action, Bundle arguments) {
+ public boolean dispatchNestedPrePerformAccessibilityAction(int action,
+ @Nullable Bundle arguments) {
for (ViewParent p = getParent(); p != null; p = p.getParent()) {
if (p.onNestedPrePerformAccessibilityAction(this, action, arguments)) {
return true;
@@ -14254,7 +14255,7 @@
* @param arguments Optional action arguments.
* @return Whether the action was performed.
*/
- public boolean performAccessibilityAction(int action, Bundle arguments) {
+ public boolean performAccessibilityAction(int action, @Nullable Bundle arguments) {
if (mAccessibilityDelegate != null) {
return mAccessibilityDelegate.performAccessibilityAction(this, action, arguments);
} else {
@@ -14270,7 +14271,7 @@
* @hide
*/
@UnsupportedAppUsage
- public boolean performAccessibilityActionInternal(int action, Bundle arguments) {
+ public boolean performAccessibilityActionInternal(int action, @Nullable Bundle arguments) {
if (isNestedScrollingEnabled()
&& (action == AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD
|| action == AccessibilityNodeInfo.ACTION_SCROLL_FORWARD
@@ -29268,12 +29269,12 @@
* Called when the view is attached to a window.
* @param v The view that was attached
*/
- public void onViewAttachedToWindow(View v);
+ public void onViewAttachedToWindow(@NonNull View v);
/**
* Called when the view is detached from a window.
* @param v The view that was detached
*/
- public void onViewDetachedFromWindow(View v);
+ public void onViewDetachedFromWindow(@NonNull View v);
}
/**
@@ -29298,7 +29299,8 @@
* @param insets The insets to apply
* @return The insets supplied, minus any insets that were consumed
*/
- public WindowInsets onApplyWindowInsets(View v, WindowInsets insets);
+ public @NonNull WindowInsets onApplyWindowInsets(@NonNull View v,
+ @NonNull WindowInsets insets);
}
private final class UnsetPressedState implements Runnable {
@@ -30241,7 +30243,7 @@
*
* @see View#sendAccessibilityEvent(int) View#sendAccessibilityEvent(int)
*/
- public void sendAccessibilityEvent(View host, int eventType) {
+ public void sendAccessibilityEvent(@NonNull View host, int eventType) {
host.sendAccessibilityEventInternal(eventType);
}
@@ -30261,7 +30263,8 @@
* @see View#performAccessibilityAction(int, Bundle)
* View#performAccessibilityAction(int, Bundle)
*/
- public boolean performAccessibilityAction(View host, int action, Bundle args) {
+ public boolean performAccessibilityAction(@NonNull View host, int action,
+ @Nullable Bundle args) {
return host.performAccessibilityActionInternal(action, args);
}
@@ -30283,7 +30286,8 @@
* @see View#sendAccessibilityEventUnchecked(AccessibilityEvent)
* View#sendAccessibilityEventUnchecked(AccessibilityEvent)
*/
- public void sendAccessibilityEventUnchecked(View host, AccessibilityEvent event) {
+ public void sendAccessibilityEventUnchecked(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
host.sendAccessibilityEventUncheckedInternal(event);
}
@@ -30304,7 +30308,8 @@
* @see View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
* View#dispatchPopulateAccessibilityEvent(AccessibilityEvent)
*/
- public boolean dispatchPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public boolean dispatchPopulateAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
return host.dispatchPopulateAccessibilityEventInternal(event);
}
@@ -30324,7 +30329,8 @@
* @see View#onPopulateAccessibilityEvent(AccessibilityEvent)
* View#onPopulateAccessibilityEvent(AccessibilityEvent)
*/
- public void onPopulateAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onPopulateAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
host.onPopulateAccessibilityEventInternal(event);
}
@@ -30344,7 +30350,8 @@
* @see View#onInitializeAccessibilityEvent(AccessibilityEvent)
* View#onInitializeAccessibilityEvent(AccessibilityEvent)
*/
- public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
+ public void onInitializeAccessibilityEvent(@NonNull View host,
+ @NonNull AccessibilityEvent event) {
host.onInitializeAccessibilityEventInternal(event);
}
@@ -30363,7 +30370,8 @@
* @see View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
* View#onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo)
*/
- public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
+ public void onInitializeAccessibilityNodeInfo(@NonNull View host,
+ @NonNull AccessibilityNodeInfo info) {
host.onInitializeAccessibilityNodeInfoInternal(info);
}
@@ -30415,8 +30423,8 @@
* @see ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent)
* ViewGroup#onRequestSendAccessibilityEvent(View, AccessibilityEvent)
*/
- public boolean onRequestSendAccessibilityEvent(ViewGroup host, View child,
- AccessibilityEvent event) {
+ public boolean onRequestSendAccessibilityEvent(@NonNull ViewGroup host, @NonNull View child,
+ @NonNull AccessibilityEvent event) {
return host.onRequestSendAccessibilityEventInternal(child, event);
}
@@ -30434,7 +30442,8 @@
*
* @see AccessibilityNodeProvider
*/
- public AccessibilityNodeProvider getAccessibilityNodeProvider(View host) {
+ public @Nullable AccessibilityNodeProvider getAccessibilityNodeProvider(
+ @NonNull View host) {
return null;
}
@@ -30462,7 +30471,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public AccessibilityNodeInfo createAccessibilityNodeInfo(View host) {
+ public AccessibilityNodeInfo createAccessibilityNodeInfo(@NonNull View host) {
return host.createAccessibilityNodeInfoInternal();
}
}
@@ -31447,23 +31456,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/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
index 9ed42f3..b25c025 100644
--- a/core/java/android/view/ViewConfiguration.java
+++ b/core/java/android/view/ViewConfiguration.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
import android.annotation.TestApi;
import android.annotation.UiContext;
import android.app.Activity;
@@ -405,7 +406,7 @@
* @see #get(android.content.Context)
* @see android.util.DisplayMetrics
*/
- private ViewConfiguration(@UiContext Context context) {
+ private ViewConfiguration(@NonNull @UiContext Context context) {
mConstructedWithContext = true;
final Resources res = context.getResources();
final DisplayMetrics metrics = res.getDisplayMetrics();
@@ -517,7 +518,7 @@
* {@link Context#createWindowContext(int, Bundle)}.
*/
// TODO(b/182007470): Use @ConfigurationContext instead
- public static ViewConfiguration get(@UiContext Context context) {
+ public static ViewConfiguration get(@NonNull @UiContext Context context) {
StrictMode.assertConfigurationContext(context, "ViewConfiguration");
final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
index 49f5229..128da31 100644
--- a/core/java/android/view/ViewParent.java
+++ b/core/java/android/view/ViewParent.java
@@ -17,6 +17,7 @@
package android.view;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
@@ -298,7 +299,7 @@
*
* @param child The child whose drawable state has changed.
*/
- public void childDrawableStateChanged(View child);
+ public void childDrawableStateChanged(@NonNull View child);
/**
* Called when a child does not want this parent and its ancestors to
@@ -337,7 +338,7 @@
* false otherwise
* @return Whether the group scrolled to handle the operation
*/
- public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
+ public boolean requestChildRectangleOnScreen(@NonNull View child, Rect rectangle,
boolean immediate);
/**
@@ -356,7 +357,7 @@
* @param event The event to be sent.
* @return True if the event was sent.
*/
- public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event);
+ public boolean requestSendAccessibilityEvent(@NonNull View child, AccessibilityEvent event);
/**
* Called when a child view now has or no longer is tracking transient state.
@@ -381,7 +382,7 @@
* @param child Child view whose state has changed
* @param hasTransientState true if this child has transient state
*/
- public void childHasTransientStateChanged(View child, boolean hasTransientState);
+ public void childHasTransientStateChanged(@NonNull View child, boolean hasTransientState);
/**
* Ask that a new dispatch of {@link View#fitSystemWindows(Rect)
@@ -418,7 +419,7 @@
* </ul>
*/
public void notifySubtreeAccessibilityStateChanged(
- View child, @NonNull View source, int changeType);
+ @NonNull View child, @NonNull View source, int changeType);
/**
* Tells if this view parent can resolve the layout direction.
@@ -525,7 +526,8 @@
* {@link View#SCROLL_AXIS_VERTICAL} or both
* @return true if this ViewParent accepts the nested scroll operation
*/
- public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes);
+ public boolean onStartNestedScroll(@NonNull View child, @NonNull View target,
+ int nestedScrollAxes);
/**
* React to the successful claiming of a nested scroll operation.
@@ -543,7 +545,8 @@
* @see #onStartNestedScroll(View, View, int)
* @see #onStopNestedScroll(View)
*/
- public void onNestedScrollAccepted(View child, View target, int nestedScrollAxes);
+ public void onNestedScrollAccepted(@NonNull View child, @NonNull View target,
+ int nestedScrollAxes);
/**
* React to a nested scroll operation ending.
@@ -556,7 +559,7 @@
*
* @param target View that initiated the nested scroll
*/
- public void onStopNestedScroll(View target);
+ public void onStopNestedScroll(@NonNull View target);
/**
* React to a nested scroll in progress.
@@ -579,7 +582,7 @@
* @param dxUnconsumed Horizontal scroll distance in pixels not consumed by target
* @param dyUnconsumed Vertical scroll distance in pixels not consumed by target
*/
- public void onNestedScroll(View target, int dxConsumed, int dyConsumed,
+ public void onNestedScroll(@NonNull View target, int dxConsumed, int dyConsumed,
int dxUnconsumed, int dyUnconsumed);
/**
@@ -602,7 +605,7 @@
* @param dy Vertical scroll distance in pixels
* @param consumed Output. The horizontal and vertical scroll distance consumed by this parent
*/
- public void onNestedPreScroll(View target, int dx, int dy, int[] consumed);
+ public void onNestedPreScroll(@NonNull View target, int dx, int dy, @NonNull int[] consumed);
/**
* Request a fling from a nested scroll.
@@ -623,7 +626,8 @@
* @param consumed true if the child consumed the fling, false otherwise
* @return true if this parent consumed or otherwise reacted to the fling
*/
- public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed);
+ public boolean onNestedFling(@NonNull View target, float velocityX, float velocityY,
+ boolean consumed);
/**
* React to a nested fling before the target view consumes it.
@@ -645,7 +649,7 @@
* @param velocityY Vertical velocity in pixels per second
* @return true if this parent consumed the fling ahead of the target view
*/
- public boolean onNestedPreFling(View target, float velocityX, float velocityY);
+ public boolean onNestedPreFling(@NonNull View target, float velocityX, float velocityY);
/**
* React to an accessibility action delegated by a target descendant view before the target
@@ -664,7 +668,8 @@
* @param arguments Optional action arguments
* @return true if the action was consumed by this ViewParent
*/
- public boolean onNestedPrePerformAccessibilityAction(View target, int action, Bundle arguments);
+ public boolean onNestedPrePerformAccessibilityAction(@NonNull View target, int action,
+ @Nullable Bundle arguments);
/**
* Given a touchable region of a child, this method reduces region by the bounds of all views on
diff --git a/core/java/android/view/ViewPropertyAnimator.java b/core/java/android/view/ViewPropertyAnimator.java
index 65cc2f8..774d697 100644
--- a/core/java/android/view/ViewPropertyAnimator.java
+++ b/core/java/android/view/ViewPropertyAnimator.java
@@ -20,6 +20,8 @@
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.annotation.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.RenderNode;
import java.util.ArrayList;
@@ -246,7 +248,7 @@
*
* @param view The View associated with this ViewPropertyAnimator
*/
- ViewPropertyAnimator(View view) {
+ ViewPropertyAnimator(@NonNull View view) {
mView = view;
view.ensureTransformationInfo();
}
@@ -259,7 +261,7 @@
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator setDuration(long duration) {
+ public @NonNull ViewPropertyAnimator setDuration(long duration) {
if (duration < 0) {
throw new IllegalArgumentException("Animators cannot have negative duration: " +
duration);
@@ -316,7 +318,7 @@
* cannot be negative.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator setStartDelay(long startDelay) {
+ public @NonNull ViewPropertyAnimator setStartDelay(long startDelay) {
if (startDelay < 0) {
throw new IllegalArgumentException("Animators cannot have negative start " +
"delay: " + startDelay);
@@ -335,7 +337,7 @@
* of <code>null</code> will result in linear interpolation.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
+ public @NonNull ViewPropertyAnimator setInterpolator(TimeInterpolator interpolator) {
mInterpolatorSet = true;
mInterpolator = interpolator;
return this;
@@ -346,7 +348,7 @@
*
* @return The timing interpolator for this animation.
*/
- public TimeInterpolator getInterpolator() {
+ public @Nullable TimeInterpolator getInterpolator() {
if (mInterpolatorSet) {
return mInterpolator;
} else {
@@ -369,12 +371,12 @@
* <code>null</code> removes any existing listener.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator setListener(Animator.AnimatorListener listener) {
+ public @NonNull ViewPropertyAnimator setListener(@Nullable Animator.AnimatorListener listener) {
mListener = listener;
return this;
}
- Animator.AnimatorListener getListener() {
+ @Nullable Animator.AnimatorListener getListener() {
return mListener;
}
@@ -392,12 +394,13 @@
* <code>null</code> removes any existing listener.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator setUpdateListener(ValueAnimator.AnimatorUpdateListener listener) {
+ public @NonNull ViewPropertyAnimator setUpdateListener(
+ @Nullable ValueAnimator.AnimatorUpdateListener listener) {
mUpdateListener = listener;
return this;
}
- ValueAnimator.AnimatorUpdateListener getUpdateListener() {
+ @Nullable ValueAnimator.AnimatorUpdateListener getUpdateListener() {
return mUpdateListener;
}
@@ -441,7 +444,7 @@
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator x(float value) {
+ public @NonNull ViewPropertyAnimator x(float value) {
animateProperty(X, value);
return this;
}
@@ -454,7 +457,7 @@
* @see View#setX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator xBy(float value) {
+ public @NonNull ViewPropertyAnimator xBy(float value) {
animatePropertyBy(X, value);
return this;
}
@@ -467,7 +470,7 @@
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator y(float value) {
+ public @NonNull ViewPropertyAnimator y(float value) {
animateProperty(Y, value);
return this;
}
@@ -480,7 +483,7 @@
* @see View#setY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator yBy(float value) {
+ public @NonNull ViewPropertyAnimator yBy(float value) {
animatePropertyBy(Y, value);
return this;
}
@@ -493,7 +496,7 @@
* @see View#setZ(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator z(float value) {
+ public @NonNull ViewPropertyAnimator z(float value) {
animateProperty(Z, value);
return this;
}
@@ -506,7 +509,7 @@
* @see View#setZ(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator zBy(float value) {
+ public @NonNull ViewPropertyAnimator zBy(float value) {
animatePropertyBy(Z, value);
return this;
}
@@ -519,7 +522,7 @@
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotation(float value) {
+ public @NonNull ViewPropertyAnimator rotation(float value) {
animateProperty(ROTATION, value);
return this;
}
@@ -532,7 +535,7 @@
* @see View#setRotation(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotationBy(float value) {
+ public @NonNull ViewPropertyAnimator rotationBy(float value) {
animatePropertyBy(ROTATION, value);
return this;
}
@@ -545,7 +548,7 @@
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotationX(float value) {
+ public @NonNull ViewPropertyAnimator rotationX(float value) {
animateProperty(ROTATION_X, value);
return this;
}
@@ -558,7 +561,7 @@
* @see View#setRotationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotationXBy(float value) {
+ public @NonNull ViewPropertyAnimator rotationXBy(float value) {
animatePropertyBy(ROTATION_X, value);
return this;
}
@@ -571,7 +574,7 @@
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotationY(float value) {
+ public @NonNull ViewPropertyAnimator rotationY(float value) {
animateProperty(ROTATION_Y, value);
return this;
}
@@ -584,7 +587,7 @@
* @see View#setRotationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator rotationYBy(float value) {
+ public @NonNull ViewPropertyAnimator rotationYBy(float value) {
animatePropertyBy(ROTATION_Y, value);
return this;
}
@@ -597,7 +600,7 @@
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationX(float value) {
+ public @NonNull ViewPropertyAnimator translationX(float value) {
animateProperty(TRANSLATION_X, value);
return this;
}
@@ -610,7 +613,7 @@
* @see View#setTranslationX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationXBy(float value) {
+ public @NonNull ViewPropertyAnimator translationXBy(float value) {
animatePropertyBy(TRANSLATION_X, value);
return this;
}
@@ -623,7 +626,7 @@
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationY(float value) {
+ public @NonNull ViewPropertyAnimator translationY(float value) {
animateProperty(TRANSLATION_Y, value);
return this;
}
@@ -636,7 +639,7 @@
* @see View#setTranslationY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationYBy(float value) {
+ public @NonNull ViewPropertyAnimator translationYBy(float value) {
animatePropertyBy(TRANSLATION_Y, value);
return this;
}
@@ -649,7 +652,7 @@
* @see View#setTranslationZ(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationZ(float value) {
+ public @NonNull ViewPropertyAnimator translationZ(float value) {
animateProperty(TRANSLATION_Z, value);
return this;
}
@@ -662,7 +665,7 @@
* @see View#setTranslationZ(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator translationZBy(float value) {
+ public @NonNull ViewPropertyAnimator translationZBy(float value) {
animatePropertyBy(TRANSLATION_Z, value);
return this;
}
@@ -674,7 +677,7 @@
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator scaleX(float value) {
+ public @NonNull ViewPropertyAnimator scaleX(float value) {
animateProperty(SCALE_X, value);
return this;
}
@@ -687,7 +690,7 @@
* @see View#setScaleX(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator scaleXBy(float value) {
+ public @NonNull ViewPropertyAnimator scaleXBy(float value) {
animatePropertyBy(SCALE_X, value);
return this;
}
@@ -700,7 +703,7 @@
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator scaleY(float value) {
+ public @NonNull ViewPropertyAnimator scaleY(float value) {
animateProperty(SCALE_Y, value);
return this;
}
@@ -713,7 +716,7 @@
* @see View#setScaleY(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator scaleYBy(float value) {
+ public @NonNull ViewPropertyAnimator scaleYBy(float value) {
animatePropertyBy(SCALE_Y, value);
return this;
}
@@ -726,7 +729,7 @@
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) {
+ public @NonNull ViewPropertyAnimator alpha(@FloatRange(from = 0.0f, to = 1.0f) float value) {
animateProperty(ALPHA, value);
return this;
}
@@ -739,7 +742,7 @@
* @see View#setAlpha(float)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator alphaBy(float value) {
+ public @NonNull ViewPropertyAnimator alphaBy(float value) {
animatePropertyBy(ALPHA, value);
return this;
}
@@ -765,7 +768,7 @@
* @see View#setLayerType(int, android.graphics.Paint)
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator withLayer() {
+ public @NonNull ViewPropertyAnimator withLayer() {
mPendingSetupAction= new Runnable() {
@Override
public void run() {
@@ -803,7 +806,7 @@
* @param runnable The action to run when the next animation starts.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator withStartAction(Runnable runnable) {
+ public @NonNull ViewPropertyAnimator withStartAction(Runnable runnable) {
mPendingOnStartAction = runnable;
if (runnable != null && mAnimatorOnStartMap == null) {
mAnimatorOnStartMap = new HashMap<Animator, Runnable>();
@@ -832,7 +835,7 @@
* @param runnable The action to run when the next animation ends.
* @return This object, allowing calls to methods in this class to be chained.
*/
- public ViewPropertyAnimator withEndAction(Runnable runnable) {
+ public @NonNull ViewPropertyAnimator withEndAction(Runnable runnable) {
mPendingOnEndAction = runnable;
if (runnable != null && mAnimatorOnEndMap == null) {
mAnimatorOnEndMap = new HashMap<Animator, Runnable>();
@@ -1061,7 +1064,7 @@
private class AnimatorEventListener
implements Animator.AnimatorListener, ValueAnimator.AnimatorUpdateListener {
@Override
- public void onAnimationStart(Animator animation) {
+ public void onAnimationStart(@NonNull Animator animation) {
if (mAnimatorSetupMap != null) {
Runnable r = mAnimatorSetupMap.get(animation);
if (r != null) {
@@ -1082,7 +1085,7 @@
}
@Override
- public void onAnimationCancel(Animator animation) {
+ public void onAnimationCancel(@NonNull Animator animation) {
if (mListener != null) {
mListener.onAnimationCancel(animation);
}
@@ -1092,14 +1095,14 @@
}
@Override
- public void onAnimationRepeat(Animator animation) {
+ public void onAnimationRepeat(@NonNull Animator animation) {
if (mListener != null) {
mListener.onAnimationRepeat(animation);
}
}
@Override
- public void onAnimationEnd(Animator animation) {
+ public void onAnimationEnd(@NonNull Animator animation) {
mView.setHasTransientState(false);
if (mAnimatorCleanupMap != null) {
Runnable r = mAnimatorCleanupMap.get(animation);
@@ -1130,7 +1133,7 @@
* the current value of each property.
*/
@Override
- public void onAnimationUpdate(ValueAnimator animation) {
+ public void onAnimationUpdate(@NonNull ValueAnimator animation) {
PropertyBundle propertyBundle = mAnimatorMap.get(animation);
if (propertyBundle == null) {
// Shouldn't happen, but just to play it safe
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 386b277..777e89d 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,10 +313,15 @@
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();
+ /**
+ * Compatibility {@link OnBackInvokedCallback} that dispatches KEYCODE_BACK events
+ * to view root for apps using legacy back behavior.
+ */
+ private OnBackInvokedCallback mCompatOnBackInvokedCallback;
/**
* Callback for notifying about global configuration changes.
@@ -893,7 +898,6 @@
mFastScrollSoundEffectsEnabled = audioManager.areNavigationRepeatSoundEffectsEnabled();
mScrollCaptureRequestTimeout = SCROLL_CAPTURE_REQUEST_TIMEOUT_MILLIS;
- mFallbackOnBackInvokedDispatcher.attachToWindow(mWindowSession, mWindow);
}
public static void addFirstDrawHandler(Runnable callback) {
@@ -1144,9 +1148,6 @@
if (pendingInsetsController != null) {
pendingInsetsController.replayAndAttach(mInsetsController);
}
- ((RootViewSurfaceTaker) mView)
- .provideWindowOnBackInvokedDispatcher()
- .attachToWindow(mWindowSession, mWindow);
}
try {
@@ -1193,6 +1194,15 @@
getAttachedWindowFrame(), 1f /* compactScale */,
mTmpFrames.displayFrame, mTempRect2, mTmpFrames.frame);
setFrame(mTmpFrames.frame);
+ registerBackCallbackOnWindow();
+ if (WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // For apps requesting legacy back behavior, we add a compat callback that
+ // dispatches {@link KeyEvent#KEYCODE_BACK} to their root views.
+ // This way from system point of view, these apps are providing custom
+ // {@link OnBackInvokedCallback}s, and will not play system back animations
+ // for them.
+ registerCompatOnBackInvokedCallback();
+ }
if (DEBUG_LAYOUT) Log.v(mTag, "Added window " + mWindow);
if (res < WindowManagerGlobal.ADD_OKAY) {
mAttachInfo.mRootView = null;
@@ -1335,7 +1345,7 @@
private void setTag() {
final String[] split = mWindowAttributes.getTitle().toString().split("\\.");
if (split.length > 0) {
- mTag = TAG + "[" + split[split.length - 1] + "]";
+ mTag = "VRI[" + split[split.length - 1] + "]";
}
}
@@ -5934,7 +5944,7 @@
}
}
- private boolean isBack(InputEvent event) {
+ boolean isBack(InputEvent event) {
if (event instanceof KeyEvent) {
return ((KeyEvent) event).getKeyCode() == KeyEvent.KEYCODE_BACK;
} else {
@@ -6474,6 +6484,19 @@
return FINISH_NOT_HANDLED;
}
+ if (isBack(event)
+ && mContext != null
+ && !WindowOnBackInvokedDispatcher.shouldUseLegacyBack()) {
+ // Invoke the appropriate {@link OnBackInvokedCallback} if the new back
+ // navigation should be used, and the key event is not handled by anything else.
+ OnBackInvokedCallback topCallback =
+ getOnBackInvokedDispatcher().getTopCallback();
+ if (topCallback != null) {
+ topCallback.onBackInvoked();
+ return FINISH_HANDLED;
+ }
+ }
+
// This dispatch is for windows that don't have a Window.Callback. Otherwise,
// the Window.Callback usually will have already called this (see
// DecorView.superDispatchKeyEvent) leaving this call a no-op.
@@ -8417,6 +8440,8 @@
mAdded = false;
}
+ unregisterCompatOnBackInvokedCallback();
+ mOnBackInvokedDispatcher.detachFromWindow();
WindowManagerGlobal.getInstance().doRemoveView(this);
}
@@ -10771,12 +10796,49 @@
* 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();
+ @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);
+ }
+
+ private void sendBackKeyEvent(int action) {
+ long when = SystemClock.uptimeMillis();
+ final KeyEvent ev = new KeyEvent(when, when, action,
+ KeyEvent.KEYCODE_BACK, 0 /* repeat */, 0 /* metaState */,
+ KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
+ KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
+ InputDevice.SOURCE_KEYBOARD);
+
+ ev.setDisplayId(mContext.getDisplay().getDisplayId());
+ if (mView != null) {
+ mView.dispatchKeyEvent(ev);
}
- return mFallbackOnBackInvokedDispatcher;
+ }
+
+ private void registerCompatOnBackInvokedCallback() {
+ mCompatOnBackInvokedCallback = new OnBackInvokedCallback() {
+ @Override
+ public void onBackInvoked() {
+ sendBackKeyEvent(KeyEvent.ACTION_DOWN);
+ sendBackKeyEvent(KeyEvent.ACTION_UP);
+ }
+ };
+ mOnBackInvokedDispatcher.registerOnBackInvokedCallback(
+ mCompatOnBackInvokedCallback, OnBackInvokedDispatcher.PRIORITY_DEFAULT);
+ }
+
+ private void unregisterCompatOnBackInvokedCallback() {
+ if (mCompatOnBackInvokedCallback != null) {
+ mOnBackInvokedDispatcher.unregisterOnBackInvokedCallback(mCompatOnBackInvokedCallback);
+ }
}
@Override
diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java
index a427ab8..cd8dd86 100644
--- a/core/java/android/view/accessibility/AccessibilityEvent.java
+++ b/core/java/android/view/accessibility/AccessibilityEvent.java
@@ -553,8 +553,20 @@
public static final int TYPE_ASSIST_READING_CONTEXT = 0x01000000;
/**
- * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event:
- * The type of change is not defined.
+ * Represents a change in the speech state defined by the content-change types. A change in the
+ * speech state occurs when another service is either speaking or listening for human speech.
+ * This event helps avoid conflicts where two services want to speak or one listens
+ * when another speaks.
+ * @see #SPEECH_STATE_SPEAKING_START
+ * @see #SPEECH_STATE_SPEAKING_END
+ * @see #SPEECH_STATE_LISTENING_START
+ * @see #SPEECH_STATE_LISTENING_END
+ */
+ public static final int TYPE_SPEECH_STATE_CHANGE = 0x02000000;
+
+ /**
+ * Change type for {@link #TYPE_WINDOW_CONTENT_CHANGED} event: The type of change is not
+ * defined.
*/
public static final int CONTENT_CHANGE_TYPE_UNDEFINED = 0x00000000;
@@ -641,6 +653,27 @@
*/
public static final int CONTENT_CHANGE_TYPE_DRAG_CANCELLED = 0x0000200;
+ /** Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is speaking. */
+ public static final int SPEECH_STATE_SPEAKING_START = 0x00000001;
+
+ /**
+ * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is no longer
+ * speaking.
+ */
+ public static final int SPEECH_STATE_SPEAKING_END = 0x00000002;
+
+ /**
+ * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is listening to the
+ * microphone.
+ */
+ public static final int SPEECH_STATE_LISTENING_START = 0x00000004;
+
+ /**
+ * Change type for {@link #TYPE_SPEECH_STATE_CHANGE} event: another service is no longer
+ * listening to the microphone.
+ */
+ public static final int SPEECH_STATE_LISTENING_END = 0x00000008;
+
/**
* Change type for {@link #TYPE_WINDOWS_CHANGED} event:
* The window was added.
@@ -730,50 +763,69 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef(flag = true, prefix = { "CONTENT_CHANGE_TYPE_" },
+ @IntDef(
+ flag = true,
+ prefix = {"CONTENT_CHANGE_TYPE_"},
value = {
- CONTENT_CHANGE_TYPE_UNDEFINED,
- CONTENT_CHANGE_TYPE_SUBTREE,
- CONTENT_CHANGE_TYPE_TEXT,
- CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION,
- CONTENT_CHANGE_TYPE_STATE_DESCRIPTION,
- CONTENT_CHANGE_TYPE_PANE_TITLE,
- CONTENT_CHANGE_TYPE_PANE_APPEARED,
- CONTENT_CHANGE_TYPE_PANE_DISAPPEARED,
- CONTENT_CHANGE_TYPE_DRAG_STARTED,
- CONTENT_CHANGE_TYPE_DRAG_DROPPED,
- CONTENT_CHANGE_TYPE_DRAG_CANCELLED
+ CONTENT_CHANGE_TYPE_UNDEFINED,
+ CONTENT_CHANGE_TYPE_SUBTREE,
+ CONTENT_CHANGE_TYPE_TEXT,
+ CONTENT_CHANGE_TYPE_CONTENT_DESCRIPTION,
+ CONTENT_CHANGE_TYPE_STATE_DESCRIPTION,
+ CONTENT_CHANGE_TYPE_PANE_TITLE,
+ CONTENT_CHANGE_TYPE_PANE_APPEARED,
+ CONTENT_CHANGE_TYPE_PANE_DISAPPEARED,
+ CONTENT_CHANGE_TYPE_DRAG_STARTED,
+ CONTENT_CHANGE_TYPE_DRAG_DROPPED,
+ CONTENT_CHANGE_TYPE_DRAG_CANCELLED,
})
public @interface ContentChangeTypes {}
/** @hide */
- @IntDef(flag = true, prefix = { "TYPE_" }, value = {
- TYPE_VIEW_CLICKED,
- TYPE_VIEW_LONG_CLICKED,
- TYPE_VIEW_SELECTED,
- TYPE_VIEW_FOCUSED,
- TYPE_VIEW_TEXT_CHANGED,
- TYPE_WINDOW_STATE_CHANGED,
- TYPE_NOTIFICATION_STATE_CHANGED,
- TYPE_VIEW_HOVER_ENTER,
- TYPE_VIEW_HOVER_EXIT,
- TYPE_TOUCH_EXPLORATION_GESTURE_START,
- TYPE_TOUCH_EXPLORATION_GESTURE_END,
- TYPE_WINDOW_CONTENT_CHANGED,
- TYPE_VIEW_SCROLLED,
- TYPE_VIEW_TEXT_SELECTION_CHANGED,
- TYPE_ANNOUNCEMENT,
- TYPE_VIEW_ACCESSIBILITY_FOCUSED,
- TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
- TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
- TYPE_GESTURE_DETECTION_START,
- TYPE_GESTURE_DETECTION_END,
- TYPE_TOUCH_INTERACTION_START,
- TYPE_TOUCH_INTERACTION_END,
- TYPE_WINDOWS_CHANGED,
- TYPE_VIEW_CONTEXT_CLICKED,
- TYPE_ASSIST_READING_CONTEXT
- })
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef(
+ flag = true,
+ prefix = {"SPEECH_STATE_"},
+ value = {
+ SPEECH_STATE_SPEAKING_START,
+ SPEECH_STATE_SPEAKING_END,
+ SPEECH_STATE_LISTENING_START,
+ SPEECH_STATE_LISTENING_END
+ })
+ public @interface SpeechStateChangeTypes {}
+
+ /** @hide */
+ @IntDef(
+ flag = true,
+ prefix = {"TYPE_"},
+ value = {
+ TYPE_VIEW_CLICKED,
+ TYPE_VIEW_LONG_CLICKED,
+ TYPE_VIEW_SELECTED,
+ TYPE_VIEW_FOCUSED,
+ TYPE_VIEW_TEXT_CHANGED,
+ TYPE_WINDOW_STATE_CHANGED,
+ TYPE_NOTIFICATION_STATE_CHANGED,
+ TYPE_VIEW_HOVER_ENTER,
+ TYPE_VIEW_HOVER_EXIT,
+ TYPE_TOUCH_EXPLORATION_GESTURE_START,
+ TYPE_TOUCH_EXPLORATION_GESTURE_END,
+ TYPE_WINDOW_CONTENT_CHANGED,
+ TYPE_VIEW_SCROLLED,
+ TYPE_VIEW_TEXT_SELECTION_CHANGED,
+ TYPE_ANNOUNCEMENT,
+ TYPE_VIEW_ACCESSIBILITY_FOCUSED,
+ TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED,
+ TYPE_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
+ TYPE_GESTURE_DETECTION_START,
+ TYPE_GESTURE_DETECTION_END,
+ TYPE_TOUCH_INTERACTION_START,
+ TYPE_TOUCH_INTERACTION_END,
+ TYPE_WINDOWS_CHANGED,
+ TYPE_VIEW_CONTEXT_CLICKED,
+ TYPE_ASSIST_READING_CONTEXT,
+ TYPE_SPEECH_STATE_CHANGE
+ })
@Retention(RetentionPolicy.SOURCE)
public @interface EventType {}
@@ -814,6 +866,7 @@
int mAction;
int mContentChangeTypes;
int mWindowChangeTypes;
+ int mSpeechStateChangeTypes;
/**
* The stack trace describing where this event originated from on the app side.
@@ -867,6 +920,7 @@
mMovementGranularity = event.mMovementGranularity;
mAction = event.mAction;
mContentChangeTypes = event.mContentChangeTypes;
+ mSpeechStateChangeTypes = event.mSpeechStateChangeTypes;
mWindowChangeTypes = event.mWindowChangeTypes;
mEventTime = event.mEventTime;
mPackageName = event.mPackageName;
@@ -1008,6 +1062,51 @@
}
/**
+ * Gets the speech state signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
+ *
+ * @see #SPEECH_STATE_SPEAKING_START
+ * @see #SPEECH_STATE_SPEAKING_END
+ * @see #SPEECH_STATE_LISTENING_START
+ * @see #SPEECH_STATE_LISTENING_END
+ */
+ public int getSpeechStateChangeTypes() {
+ return mSpeechStateChangeTypes;
+ }
+
+ private static String speechStateChangedTypesToString(int types) {
+ return BitUtils.flagsToString(
+ types, AccessibilityEvent::singleSpeechStateChangeTypeToString);
+ }
+
+ private static String singleSpeechStateChangeTypeToString(int type) {
+ switch (type) {
+ case SPEECH_STATE_SPEAKING_START:
+ return "SPEECH_STATE_SPEAKING_START";
+ case SPEECH_STATE_LISTENING_START:
+ return "SPEECH_STATE_LISTENING_START";
+ case SPEECH_STATE_SPEAKING_END:
+ return "SPEECH_STATE_SPEAKING_END";
+ case SPEECH_STATE_LISTENING_END:
+ return "SPEECH_STATE_LISTENING_END";
+ default:
+ return Integer.toHexString(type);
+ }
+ }
+
+ /**
+ * Sets the speech state type signaled by a {@link #TYPE_SPEECH_STATE_CHANGE} event
+ *
+ * @see #SPEECH_STATE_SPEAKING_START
+ * @see #SPEECH_STATE_SPEAKING_END
+ * @see #SPEECH_STATE_LISTENING_START
+ * @see #SPEECH_STATE_LISTENING_END
+ */
+ public void setSpeechStateChangeTypes(int state) {
+ enforceNotSealed();
+ mSpeechStateChangeTypes = state;
+ }
+
+ /**
* Get the bit mask of change types signaled by a {@link #TYPE_WINDOWS_CHANGED} event. A
* single event may represent multiple change types.
*
@@ -1239,6 +1338,7 @@
mAction = 0;
mContentChangeTypes = 0;
mWindowChangeTypes = 0;
+ mSpeechStateChangeTypes = 0;
mPackageName = null;
mEventTime = 0;
if (mRecords != null) {
@@ -1261,6 +1361,7 @@
mAction = parcel.readInt();
mContentChangeTypes = parcel.readInt();
mWindowChangeTypes = parcel.readInt();
+ mSpeechStateChangeTypes = parcel.readInt();
mPackageName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
mEventTime = parcel.readLong();
mConnectionId = parcel.readInt();
@@ -1332,6 +1433,7 @@
parcel.writeInt(mAction);
parcel.writeInt(mContentChangeTypes);
parcel.writeInt(mWindowChangeTypes);
+ parcel.writeInt(mSpeechStateChangeTypes);
TextUtils.writeToParcel(mPackageName, parcel, 0);
parcel.writeLong(mEventTime);
parcel.writeInt(mConnectionId);
@@ -1500,6 +1602,7 @@
case TYPE_WINDOWS_CHANGED: return "TYPE_WINDOWS_CHANGED";
case TYPE_VIEW_CONTEXT_CLICKED: return "TYPE_VIEW_CONTEXT_CLICKED";
case TYPE_ASSIST_READING_CONTEXT: return "TYPE_ASSIST_READING_CONTEXT";
+ case TYPE_SPEECH_STATE_CHANGE: return "TYPE_SPEECH_STATE_CHANGE";
default: return Integer.toHexString(eventType);
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
index 6f4bc71..82e823f 100644
--- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java
+++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java
@@ -526,9 +526,9 @@
* @param prefetchFlags flags to guide prefetching.
* @return An {@link AccessibilityNodeInfo} if found, null otherwise.
*/
- public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId,
- int accessibilityWindowId, long accessibilityNodeId, boolean bypassCache,
- int prefetchFlags, Bundle arguments) {
+ public @Nullable AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(
+ int connectionId, int accessibilityWindowId, long accessibilityNodeId,
+ boolean bypassCache, int prefetchFlags, Bundle arguments) {
if ((prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_SIBLINGS) != 0
&& (prefetchFlags & AccessibilityNodeInfo.FLAG_PREFETCH_PREDECESSORS) == 0) {
throw new IllegalArgumentException("FLAG_PREFETCH_SIBLINGS"
diff --git a/core/java/android/view/accessibility/AccessibilityNodeProvider.java b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
index f4c7b96..91d4a35 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeProvider.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeProvider.java
@@ -17,6 +17,7 @@
package android.view.accessibility;
import android.accessibilityservice.AccessibilityService;
+import android.annotation.Nullable;
import android.os.Bundle;
import android.view.View;
@@ -195,7 +196,7 @@
* @see View#createAccessibilityNodeInfo()
* @see AccessibilityNodeInfo
*/
- public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
+ public @Nullable AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
return null;
}
@@ -234,7 +235,7 @@
* @see #createAccessibilityNodeInfo(int)
* @see AccessibilityNodeInfo
*/
- public boolean performAction(int virtualViewId, int action, Bundle arguments) {
+ public boolean performAction(int virtualViewId, int action, @Nullable Bundle arguments) {
return false;
}
@@ -252,7 +253,7 @@
* @see #createAccessibilityNodeInfo(int)
* @see AccessibilityNodeInfo
*/
- public List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
+ public @Nullable List<AccessibilityNodeInfo> findAccessibilityNodeInfosByText(String text,
int virtualViewId) {
return null;
}
@@ -268,7 +269,7 @@
* @see AccessibilityNodeInfo#FOCUS_INPUT
* @see AccessibilityNodeInfo#FOCUS_ACCESSIBILITY
*/
- public AccessibilityNodeInfo findFocus(int focus) {
+ public @Nullable AccessibilityNodeInfo findFocus(int focus) {
return null;
}
}
diff --git a/core/java/android/view/accessibility/AccessibilityRecord.java b/core/java/android/view/accessibility/AccessibilityRecord.java
index 426a3f4..cf2ea15 100644
--- a/core/java/android/view/accessibility/AccessibilityRecord.java
+++ b/core/java/android/view/accessibility/AccessibilityRecord.java
@@ -133,7 +133,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setSource(View source) {
+ public void setSource(@Nullable View source) {
setSource(source, AccessibilityNodeProvider.HOST_VIEW_ID);
}
@@ -184,7 +184,7 @@
* </p>
* @return The info of the source.
*/
- public AccessibilityNodeInfo getSource() {
+ public @Nullable AccessibilityNodeInfo getSource() {
enforceSealed();
if ((mConnectionId == UNDEFINED)
|| (mSourceWindowId == AccessibilityWindowInfo.UNDEFINED_WINDOW_ID)
@@ -629,7 +629,7 @@
*
* @return The class name.
*/
- public CharSequence getClassName() {
+ public @Nullable CharSequence getClassName() {
return mClassName;
}
@@ -640,7 +640,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setClassName(CharSequence className) {
+ public void setClassName(@Nullable CharSequence className) {
enforceNotSealed();
mClassName = className;
}
@@ -651,7 +651,7 @@
*
* @return The text.
*/
- public List<CharSequence> getText() {
+ public @NonNull List<CharSequence> getText() {
return mText;
}
@@ -660,7 +660,7 @@
*
* @return The text before the change.
*/
- public CharSequence getBeforeText() {
+ public @Nullable CharSequence getBeforeText() {
return mBeforeText;
}
@@ -671,7 +671,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setBeforeText(CharSequence beforeText) {
+ public void setBeforeText(@Nullable CharSequence beforeText) {
enforceNotSealed();
mBeforeText = (beforeText == null) ? null
: beforeText.subSequence(0, beforeText.length());
@@ -682,7 +682,7 @@
*
* @return The description.
*/
- public CharSequence getContentDescription() {
+ public @Nullable CharSequence getContentDescription() {
return mContentDescription;
}
@@ -693,7 +693,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setContentDescription(CharSequence contentDescription) {
+ public void setContentDescription(@Nullable CharSequence contentDescription) {
enforceNotSealed();
mContentDescription = (contentDescription == null) ? null
: contentDescription.subSequence(0, contentDescription.length());
@@ -704,7 +704,7 @@
*
* @return The parcelable data.
*/
- public Parcelable getParcelableData() {
+ public @Nullable Parcelable getParcelableData() {
return mParcelableData;
}
@@ -715,7 +715,7 @@
*
* @throws IllegalStateException If called from an AccessibilityService.
*/
- public void setParcelableData(Parcelable parcelableData) {
+ public void setParcelableData(@Nullable Parcelable parcelableData) {
enforceNotSealed();
mParcelableData = parcelableData;
}
@@ -822,7 +822,7 @@
* @return An instance.
*/
@Deprecated
- public static AccessibilityRecord obtain(AccessibilityRecord record) {
+ public static @NonNull AccessibilityRecord obtain(@NonNull AccessibilityRecord record) {
AccessibilityRecord clone = AccessibilityRecord.obtain();
clone.init(record);
return clone;
@@ -836,7 +836,7 @@
* @return An instance.
*/
@Deprecated
- public static AccessibilityRecord obtain() {
+ public static @NonNull AccessibilityRecord obtain() {
return new AccessibilityRecord();
}
@@ -854,7 +854,7 @@
*
* @param record The to initialize from.
*/
- void init(AccessibilityRecord record) {
+ void init(@NonNull AccessibilityRecord record) {
mSealed = record.mSealed;
mBooleanProperties = record.mBooleanProperties;
mCurrentItemIndex = record.mCurrentItemIndex;
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index 3914a3c..fadbdbb 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -1251,18 +1251,19 @@
public float value;
/**
- * Size descriptions can appear inthree forms:
+ * Size descriptions can appear in four forms:
* <ol>
* <li>An absolute size. This is represented by a number.</li>
* <li>A size relative to the size of the object being animated. This
- * is represented by a number followed by "%".</li> *
+ * is represented by a number followed by "%".</li>
* <li>A size relative to the size of the parent of object being
* animated. This is represented by a number followed by "%p".</li>
+ * <li>(Starting from API 32) A complex number.</li>
* </ol>
* @param value The typed value to parse
* @return The parsed version of the description
*/
- static Description parseValue(TypedValue value) {
+ static Description parseValue(TypedValue value, Context context) {
Description d = new Description();
if (value == null) {
d.type = ABSOLUTE;
@@ -1283,6 +1284,11 @@
d.type = ABSOLUTE;
d.value = value.data;
return d;
+ } else if (value.type == TypedValue.TYPE_DIMENSION) {
+ d.type = ABSOLUTE;
+ d.value = TypedValue.complexToDimension(value.data,
+ context.getResources().getDisplayMetrics());
+ return d;
}
}
diff --git a/core/java/android/view/animation/ClipRectAnimation.java b/core/java/android/view/animation/ClipRectAnimation.java
index 21509d3..3f4b3e7 100644
--- a/core/java/android/view/animation/ClipRectAnimation.java
+++ b/core/java/android/view/animation/ClipRectAnimation.java
@@ -20,7 +20,6 @@
import android.content.res.TypedArray;
import android.graphics.Rect;
import android.util.AttributeSet;
-import android.util.DisplayMetrics;
/**
* An animation that controls the clip of an object. See the
@@ -66,43 +65,43 @@
com.android.internal.R.styleable.ClipRectAnimation);
Description d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_fromLeft));
+ com.android.internal.R.styleable.ClipRectAnimation_fromLeft), context);
mFromLeftType = d.type;
mFromLeftValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_fromTop));
+ com.android.internal.R.styleable.ClipRectAnimation_fromTop), context);
mFromTopType = d.type;
mFromTopValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_fromRight));
+ com.android.internal.R.styleable.ClipRectAnimation_fromRight), context);
mFromRightType = d.type;
mFromRightValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_fromBottom));
+ com.android.internal.R.styleable.ClipRectAnimation_fromBottom), context);
mFromBottomType = d.type;
mFromBottomValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_toLeft));
+ com.android.internal.R.styleable.ClipRectAnimation_toLeft), context);
mToLeftType = d.type;
mToLeftValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_toTop));
+ com.android.internal.R.styleable.ClipRectAnimation_toTop), context);
mToTopType = d.type;
mToTopValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_toRight));
+ com.android.internal.R.styleable.ClipRectAnimation_toRight), context);
mToRightType = d.type;
mToRightValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ClipRectAnimation_toBottom));
+ com.android.internal.R.styleable.ClipRectAnimation_toBottom), context);
mToBottomType = d.type;
mToBottomValue = d.value;
diff --git a/core/java/android/view/animation/ExtendAnimation.java b/core/java/android/view/animation/ExtendAnimation.java
index fd627e5..210eb8a 100644
--- a/core/java/android/view/animation/ExtendAnimation.java
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -63,43 +63,43 @@
com.android.internal.R.styleable.ExtendAnimation);
Description d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft));
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendLeft), context);
mFromLeftType = d.type;
mFromLeftValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_fromExtendTop));
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendTop), context);
mFromTopType = d.type;
mFromTopValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_fromExtendRight));
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendRight), context);
mFromRightType = d.type;
mFromRightValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom));
+ com.android.internal.R.styleable.ExtendAnimation_fromExtendBottom), context);
mFromBottomType = d.type;
mFromBottomValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_toExtendLeft));
+ com.android.internal.R.styleable.ExtendAnimation_toExtendLeft), context);
mToLeftType = d.type;
mToLeftValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_toExtendTop));
+ com.android.internal.R.styleable.ExtendAnimation_toExtendTop), context);
mToTopType = d.type;
mToTopValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_toExtendRight));
+ com.android.internal.R.styleable.ExtendAnimation_toExtendRight), context);
mToRightType = d.type;
mToRightValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ExtendAnimation_toExtendBottom));
+ com.android.internal.R.styleable.ExtendAnimation_toExtendBottom), context);
mToBottomType = d.type;
mToBottomValue = d.value;
diff --git a/core/java/android/view/animation/GridLayoutAnimationController.java b/core/java/android/view/animation/GridLayoutAnimationController.java
index 0f189ae..c77f54f 100644
--- a/core/java/android/view/animation/GridLayoutAnimationController.java
+++ b/core/java/android/view/animation/GridLayoutAnimationController.java
@@ -116,10 +116,12 @@
com.android.internal.R.styleable.GridLayoutAnimation);
Animation.Description d = Animation.Description.parseValue(
- a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay));
+ a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay),
+ context);
mColumnDelay = d.value;
d = Animation.Description.parseValue(
- a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay));
+ a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay),
+ context);
mRowDelay = d.value;
//noinspection PointlessBitwiseExpression
mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction,
diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java
index e2b7519..1d56d29 100644
--- a/core/java/android/view/animation/LayoutAnimationController.java
+++ b/core/java/android/view/animation/LayoutAnimationController.java
@@ -106,7 +106,7 @@
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);
Animation.Description d = Animation.Description.parseValue(
- a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
+ a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay), context);
mDelay = d.value;
mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);
diff --git a/core/java/android/view/animation/PathInterpolator.java b/core/java/android/view/animation/PathInterpolator.java
index 99d6b9c..341c71b 100644
--- a/core/java/android/view/animation/PathInterpolator.java
+++ b/core/java/android/view/animation/PathInterpolator.java
@@ -15,6 +15,8 @@
*/
package android.view.animation;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.Resources.Theme;
@@ -61,7 +63,7 @@
*
* @param path The <code>Path</code> to use to make the line representing the interpolator.
*/
- public PathInterpolator(Path path) {
+ public PathInterpolator(@NonNull Path path) {
initPath(path);
}
@@ -94,7 +96,7 @@
}
/** @hide */
- public PathInterpolator(Resources res, Theme theme, AttributeSet attrs) {
+ public PathInterpolator(Resources res, @Nullable Theme theme, @NonNull AttributeSet attrs) {
TypedArray a;
if (theme != null) {
a = theme.obtainStyledAttributes(attrs, R.styleable.PathInterpolator, 0, 0);
diff --git a/core/java/android/view/animation/RotateAnimation.java b/core/java/android/view/animation/RotateAnimation.java
index 3c325d9..0613cd2 100644
--- a/core/java/android/view/animation/RotateAnimation.java
+++ b/core/java/android/view/animation/RotateAnimation.java
@@ -56,12 +56,12 @@
mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f);
Description d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.RotateAnimation_pivotX));
+ com.android.internal.R.styleable.RotateAnimation_pivotX), context);
mPivotXType = d.type;
mPivotXValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.RotateAnimation_pivotY));
+ com.android.internal.R.styleable.RotateAnimation_pivotY), context);
mPivotYType = d.type;
mPivotYValue = d.value;
diff --git a/core/java/android/view/animation/ScaleAnimation.java b/core/java/android/view/animation/ScaleAnimation.java
index e9a8436..533ef45 100644
--- a/core/java/android/view/animation/ScaleAnimation.java
+++ b/core/java/android/view/animation/ScaleAnimation.java
@@ -118,12 +118,12 @@
}
Description d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ScaleAnimation_pivotX));
+ com.android.internal.R.styleable.ScaleAnimation_pivotX), context);
mPivotXType = d.type;
mPivotXValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.ScaleAnimation_pivotY));
+ com.android.internal.R.styleable.ScaleAnimation_pivotY), context);
mPivotYType = d.type;
mPivotYValue = d.value;
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
index 3365c70..e27469c 100644
--- a/core/java/android/view/animation/TranslateAnimation.java
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -73,22 +73,22 @@
com.android.internal.R.styleable.TranslateAnimation);
Description d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.TranslateAnimation_fromXDelta));
+ com.android.internal.R.styleable.TranslateAnimation_fromXDelta), context);
mFromXType = d.type;
mFromXValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.TranslateAnimation_toXDelta));
+ com.android.internal.R.styleable.TranslateAnimation_toXDelta), context);
mToXType = d.type;
mToXValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.TranslateAnimation_fromYDelta));
+ com.android.internal.R.styleable.TranslateAnimation_fromYDelta), context);
mFromYType = d.type;
mFromYValue = d.value;
d = Description.parseValue(a.peekValue(
- com.android.internal.R.styleable.TranslateAnimation_toYDelta));
+ com.android.internal.R.styleable.TranslateAnimation_toYDelta), context);
mToYType = d.type;
mToYValue = d.value;
diff --git a/core/java/android/view/autofill/AutofillClientController.java b/core/java/android/view/autofill/AutofillClientController.java
index c47f6f7..0f0fa4a 100644
--- a/core/java/android/view/autofill/AutofillClientController.java
+++ b/core/java/android/view/autofill/AutofillClientController.java
@@ -27,6 +27,7 @@
import android.os.Bundle;
import android.os.IBinder;
import android.text.TextUtils;
+import android.util.Dumpable;
import android.util.Log;
import android.util.Slog;
import android.view.KeyEvent;
@@ -43,7 +44,7 @@
*
* @hide
*/
-public final class AutofillClientController implements AutofillManager.AutofillClient {
+public final class AutofillClientController implements AutofillManager.AutofillClient, Dumpable {
private static final String TAG = "AutofillClientController";
@@ -54,6 +55,8 @@
public static final String AUTOFILL_RESET_NEEDED = "@android:autofillResetNeeded";
public static final String AUTO_FILL_AUTH_WHO_PREFIX = "@android:autoFillAuth:";
+ public static final String DUMPABLE_NAME = "AutofillManager";
+
/** The last autofill id that was returned from {@link #getNextAutofillId()} */
public int mLastAutofillId = View.LAST_APP_AUTOFILL_ID;
@@ -73,6 +76,7 @@
*/
public AutofillClientController(Activity activity) {
mActivity = activity;
+ activity.addDumpable(this);
}
private AutofillManager getAutofillManager() {
@@ -280,10 +284,14 @@
}
}
- /**
- * Prints autofill related information for the Activity.
- */
- public void dumpAutofillManager(String prefix, PrintWriter writer) {
+ @Override
+ public String getDumpableName() {
+ return DUMPABLE_NAME;
+ }
+
+ @Override
+ public void dump(PrintWriter writer, String[] args) {
+ final String prefix = "";
final AutofillManager afm = getAutofillManager();
if (afm != null) {
afm.dump(prefix, writer);
diff --git a/core/java/android/view/contentcapture/ContentCaptureManager.java b/core/java/android/view/contentcapture/ContentCaptureManager.java
index bcab366..54bd9e7 100644
--- a/core/java/android/view/contentcapture/ContentCaptureManager.java
+++ b/core/java/android/view/contentcapture/ContentCaptureManager.java
@@ -29,6 +29,7 @@
import android.annotation.TestApi;
import android.annotation.UiThread;
import android.annotation.UserIdInt;
+import android.app.Activity;
import android.app.Service;
import android.content.ComponentName;
import android.content.ContentCaptureOptions;
@@ -41,6 +42,7 @@
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.Dumpable;
import android.util.Log;
import android.util.Slog;
import android.view.View;
@@ -215,6 +217,9 @@
/** @hide */
public static final boolean DEBUG = false;
+ /** @hide */
+ public static final String DUMPABLE_NAME = "ContentCaptureManager";
+
/** Error happened during the data sharing session. */
public static final int DATA_SHARE_ERROR_UNKNOWN = 1;
@@ -402,6 +407,9 @@
mService = Objects.requireNonNull(service, "service cannot be null");
mOptions = Objects.requireNonNull(options, "options cannot be null");
+ if (context instanceof Activity) {
+ ((Activity) context).addDumpable(new Dumper());
+ }
ContentCaptureHelper.setLoggingLevel(mOptions.loggingLevel);
if (sVerbose) Log.v(TAG, "Constructor for " + context.getPackageName());
@@ -740,28 +748,37 @@
return resultReceiver;
}
- /** @hide */
- public void dump(String prefix, PrintWriter pw) {
- pw.print(prefix); pw.println("ContentCaptureManager");
- final String prefix2 = prefix + " ";
- synchronized (mLock) {
- pw.print(prefix2); pw.print("isContentCaptureEnabled(): ");
- pw.println(isContentCaptureEnabled());
- pw.print(prefix2); pw.print("Debug: "); pw.print(sDebug);
- pw.print(" Verbose: "); pw.println(sVerbose);
- pw.print(prefix2); pw.print("Context: "); pw.println(mContext);
- pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId());
- pw.print(prefix2); pw.print("Service: "); pw.println(mService);
- pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags);
- pw.print(prefix2); pw.print("Options: "); mOptions.dumpShort(pw); pw.println();
- if (mMainSession != null) {
- final String prefix3 = prefix2 + " ";
- pw.print(prefix2); pw.println("Main session:");
- mMainSession.dump(prefix3, pw);
- } else {
- pw.print(prefix2); pw.println("No sessions");
+ // NOTE: ContentCaptureManager cannot implement it directly as it would be exposed as public API
+ private final class Dumper implements Dumpable {
+ @Override
+ public void dump(@NonNull PrintWriter pw, @Nullable String[] args) {
+ String prefix = "";
+ pw.print(prefix); pw.println("ContentCaptureManager");
+ final String prefix2 = prefix + " ";
+ synchronized (mLock) {
+ pw.print(prefix2); pw.print("isContentCaptureEnabled(): ");
+ pw.println(isContentCaptureEnabled());
+ pw.print(prefix2); pw.print("Debug: "); pw.print(sDebug);
+ pw.print(" Verbose: "); pw.println(sVerbose);
+ pw.print(prefix2); pw.print("Context: "); pw.println(mContext);
+ pw.print(prefix2); pw.print("User: "); pw.println(mContext.getUserId());
+ pw.print(prefix2); pw.print("Service: "); pw.println(mService);
+ pw.print(prefix2); pw.print("Flags: "); pw.println(mFlags);
+ pw.print(prefix2); pw.print("Options: "); mOptions.dumpShort(pw); pw.println();
+ if (mMainSession != null) {
+ final String prefix3 = prefix2 + " ";
+ pw.print(prefix2); pw.println("Main session:");
+ mMainSession.dump(prefix3, pw);
+ } else {
+ pw.print(prefix2); pw.println("No sessions");
+ }
}
}
+
+ @Override
+ public String getDumpableName() {
+ return DUMPABLE_NAME;
+ }
}
/**
diff --git a/core/java/android/view/translation/UiTranslationController.java b/core/java/android/view/translation/UiTranslationController.java
index 2702c2d..8cf032b 100644
--- a/core/java/android/view/translation/UiTranslationController.java
+++ b/core/java/android/view/translation/UiTranslationController.java
@@ -33,6 +33,7 @@
import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
+import android.util.Dumpable;
import android.util.IntArray;
import android.util.Log;
import android.util.LongSparseArray;
@@ -62,11 +63,15 @@
*
* @hide
*/
-public class UiTranslationController {
+public class UiTranslationController implements Dumpable {
public static final boolean DEBUG = Log.isLoggable(UiTranslationManager.LOG_TAG, Log.DEBUG);
+ /** @hide */
+ public static final String DUMPABLE_NAME = "UiTranslationController";
+
private static final String TAG = "UiTranslationController";
+
@NonNull
private final Activity mActivity;
@NonNull
@@ -104,6 +109,7 @@
Process.THREAD_PRIORITY_FOREGROUND);
mWorkerThread.start();
mWorkerHandler = mWorkerThread.getThreadHandler();
+ activity.addDumpable(this);
}
/**
@@ -206,10 +212,14 @@
mLastRequestAutofillIds.addAll(views);
}
- /**
- * Called to dump the translation information for Activity.
- */
- public void dump(String outerPrefix, PrintWriter pw) {
+ @Override
+ public String getDumpableName() {
+ return DUMPABLE_NAME;
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
+ String outerPrefix = "";
pw.print(outerPrefix); pw.println("UiTranslationController:");
final String pfx = outerPrefix + " ";
pw.print(pfx); pw.print("activity: "); pw.print(mActivity);
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 0fe2ed5..9efa583 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -10821,8 +10821,11 @@
&& mSavedMarqueeModeLayout.getLineWidth(0) > width));
}
+ /**
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
- private void startMarquee() {
+ protected void startMarquee() {
// Do not ellipsize EditText
if (getKeyListener() != null) return;
@@ -10848,7 +10851,10 @@
}
}
- private void stopMarquee() {
+ /**
+ * @hide
+ */
+ protected void stopMarquee() {
if (mMarquee != null && !mMarquee.isStopped()) {
mMarquee.stop();
}
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..4de977a
--- /dev/null
+++ b/core/java/android/window/ProxyOnBackInvokedDispatcher.java
@@ -0,0 +1,179 @@
+/*
+ * 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 implements 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));
+ }
+ if (priority < 0) {
+ throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
+ + "cannot have negative priority. Priority: " + priority);
+ }
+ registerOnBackInvokedCallbackUnchecked(callback, priority);
+ }
+
+ @Override
+ public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ registerOnBackInvokedCallbackUnchecked(callback, PRIORITY_SYSTEM);
+ }
+
+ @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));
+ }
+ }
+
+ private void registerOnBackInvokedCallbackUnchecked(
+ @NonNull OnBackInvokedCallback callback, int priority) {
+ synchronized (mLock) {
+ mCallbacks.add(Pair.create(callback, priority));
+ if (mActualDispatcherOwner != null) {
+ mActualDispatcherOwner.getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
+ callback, priority);
+ }
+ }
+ }
+
+ /**
+ * 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) {
+ int priority = callbackPair.second;
+ if (priority >= 0) {
+ dispatcher.registerOnBackInvokedCallback(callbackPair.first, priority);
+ } else {
+ dispatcher.registerSystemOnBackInvokedCallback(callbackPair.first);
+ }
+ }
+ 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/android/window/WindowOnBackInvokedDispatcher.java b/core/java/android/window/WindowOnBackInvokedDispatcher.java
index 2978604..d37d3b4 100644
--- a/core/java/android/window/WindowOnBackInvokedDispatcher.java
+++ b/core/java/android/window/WindowOnBackInvokedDispatcher.java
@@ -18,8 +18,10 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.compat.CompatChanges;
import android.os.Handler;
import android.os.RemoteException;
+import android.os.SystemProperties;
import android.util.Log;
import android.view.IWindow;
import android.view.IWindowSession;
@@ -31,7 +33,7 @@
import java.util.TreeMap;
/**
- * Provides window based implementation of {@link OnBackInvokedDispatcher}.
+ * Provides window based implementation of {@link android.view.OnBackInvokedDispatcher}.
*
* Callbacks with higher priorities receive back dispatching first.
* Within the same priority, callbacks receive back dispatching in the reverse order
@@ -39,16 +41,19 @@
*
* When the top priority callback is updated, the new callback is propagated to the Window Manager
* if the window the instance is associated with has been attached. It is allowed to register /
- * unregister {@link OnBackInvokedCallback}s before the window is attached, although callbacks
- * will not receive dispatches until window attachment.
+ * unregister {@link android.view.OnBackInvokedCallback}s before the window is attached, although
+ * callbacks will not receive dispatches until window attachment.
*
* @hide
*/
-public class WindowOnBackInvokedDispatcher extends OnBackInvokedDispatcher {
+public class WindowOnBackInvokedDispatcher implements OnBackInvokedDispatcher {
private IWindowSession mWindowSession;
private IWindow mWindow;
private static final String TAG = "WindowOnBackDispatcher";
private static final boolean DEBUG = false;
+ private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
+ private static final boolean IS_BACK_PREDICTABILITY_ENABLED = SystemProperties
+ .getInt(BACK_PREDICTABILITY_PROP, 0) > 0;
/** The currently most prioritized callback. */
@Nullable
@@ -82,6 +87,15 @@
@Override
public void registerOnBackInvokedCallback(
@NonNull OnBackInvokedCallback callback, @Priority int priority) {
+ if (priority < 0) {
+ throw new IllegalArgumentException("Application registered OnBackInvokedCallback "
+ + "cannot have negative priority. Priority: " + priority);
+ }
+ registerOnBackInvokedCallbackUnchecked(callback, priority);
+ }
+
+ private void registerOnBackInvokedCallbackUnchecked(
+ @NonNull OnBackInvokedCallback callback, @Priority int priority) {
if (!mOnBackInvokedCallbacks.containsKey(priority)) {
mOnBackInvokedCallbacks.put(priority, new ArrayList<>());
}
@@ -120,6 +134,11 @@
}
}
+ @Override
+ public void registerSystemOnBackInvokedCallback(@NonNull OnBackInvokedCallback callback) {
+ registerOnBackInvokedCallbackUnchecked(callback, OnBackInvokedDispatcher.PRIORITY_SYSTEM);
+ }
+
/** Clears all registered callbacks on the instance. */
public void clear() {
mAllCallbacks.clear();
@@ -198,4 +217,21 @@
Handler.getMain().post(() -> mCallback.onBackInvoked());
}
}
+
+ @Override
+ public OnBackInvokedCallback getTopCallback() {
+ return mTopCallback == null ? null : mTopCallback.getCallback();
+ }
+
+ /**
+ * Returns if the legacy back behavior should be used.
+ *
+ * Legacy back behavior dispatches KEYCODE_BACK instead of invoking the application registered
+ * {@link android.view.OnBackInvokedCallback}.
+ *
+ */
+ public static boolean shouldUseLegacyBack() {
+ return !CompatChanges.isChangeEnabled(DISPATCH_BACK_INVOCATION_AHEAD_OF_TIME)
+ || !IS_BACK_PREDICTABILITY_ENABLED;
+ }
}
diff --git a/core/java/com/android/internal/app/BlockedAppActivity.java b/core/java/com/android/internal/app/BlockedAppActivity.java
index 65526eb..fbdbbfb 100644
--- a/core/java/com/android/internal/app/BlockedAppActivity.java
+++ b/core/java/com/android/internal/app/BlockedAppActivity.java
@@ -17,7 +17,6 @@
package com.android.internal.app;
import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
@@ -36,9 +35,6 @@
private static final String TAG = "BlockedAppActivity";
private static final String PACKAGE_NAME = "com.android.internal.app";
private static final String EXTRA_BLOCKED_PACKAGE = PACKAGE_NAME + ".extra.BLOCKED_PACKAGE";
- private static final String EXTRA_BLOCKED_ACTIVITY_INFO =
- PACKAGE_NAME + ".extra.BLOCKED_ACTIVITY_INFO";
- private static final String EXTRA_STREAMED_DEVICE = PACKAGE_NAME + ".extra.STREAMED_DEVICE";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -52,30 +48,17 @@
return;
}
- CharSequence appLabel = null;
String packageName = intent.getStringExtra(EXTRA_BLOCKED_PACKAGE);
- ActivityInfo activityInfo = intent.getParcelableExtra(EXTRA_BLOCKED_ACTIVITY_INFO);
- if (activityInfo != null) {
- appLabel = activityInfo.loadLabel(getPackageManager());
- } else if (!TextUtils.isEmpty(packageName)) {
- appLabel = getAppLabel(userId, packageName);
- }
-
- if (TextUtils.isEmpty(appLabel)) {
- Slog.wtf(TAG, "Invalid package: " + packageName + " or activity info: " + activityInfo);
+ if (TextUtils.isEmpty(packageName)) {
+ Slog.wtf(TAG, "Invalid package: " + packageName);
finish();
return;
}
- CharSequence streamedDeviceName = intent.getCharSequenceExtra(EXTRA_STREAMED_DEVICE);
- if (!TextUtils.isEmpty(streamedDeviceName)) {
- mAlertParams.mTitle = getString(R.string.app_streaming_blocked_title, appLabel);
- mAlertParams.mMessage =
- getString(R.string.app_streaming_blocked_message, streamedDeviceName);
- } else {
- mAlertParams.mTitle = getString(R.string.app_blocked_title);
- mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
- }
+ CharSequence appLabel = getAppLabel(userId, packageName);
+
+ mAlertParams.mTitle = getString(R.string.app_blocked_title);
+ mAlertParams.mMessage = getString(R.string.app_blocked_message, appLabel);
mAlertParams.mPositiveButtonText = getString(android.R.string.ok);
setupAlert();
}
@@ -100,19 +83,4 @@
.putExtra(Intent.EXTRA_USER_ID, userId)
.putExtra(EXTRA_BLOCKED_PACKAGE, packageName);
}
-
- /**
- * Creates an intent that launches {@link BlockedAppActivity} when app streaming is blocked.
- *
- * Using this method and providing a non-empty {@code streamedDeviceName} will cause the dialog
- * to use streaming-specific error messages.
- */
- public static Intent createStreamingBlockedIntent(int userId, ActivityInfo activityInfo,
- CharSequence streamedDeviceName) {
- return new Intent()
- .setClassName("android", BlockedAppActivity.class.getName())
- .putExtra(Intent.EXTRA_USER_ID, userId)
- .putExtra(EXTRA_BLOCKED_ACTIVITY_INFO, activityInfo)
- .putExtra(EXTRA_STREAMED_DEVICE, streamedDeviceName);
- }
}
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/policy/TransitionAnimation.java b/core/java/com/android/internal/policy/TransitionAnimation.java
index ece6f2f3..37c96e7 100644
--- a/core/java/com/android/internal/policy/TransitionAnimation.java
+++ b/core/java/com/android/internal/policy/TransitionAnimation.java
@@ -98,6 +98,10 @@
private static final String DEFAULT_PACKAGE = "android";
+ // TODO (b/215515255): remove once we full migrate to shell transitions
+ private static final boolean SHELL_TRANSITIONS_ENABLED =
+ SystemProperties.getBoolean("persist.debug.shell_transit", false);
+
private final Context mContext;
private final String mTag;
@@ -252,6 +256,9 @@
resId = ent.array.getResourceId(animAttr, 0);
}
}
+ if (!SHELL_TRANSITIONS_ENABLED) {
+ resId = updateToLegacyIfNeeded(resId);
+ }
resId = updateToTranslucentAnimIfNeeded(resId, transit);
if (ResourceId.isValid(resId)) {
return loadAnimationSafely(context, resId, mTag);
@@ -259,6 +266,24 @@
return null;
}
+ /**
+ * Replace animations that are not compatible with the legacy transition system with ones that
+ * are compatible with it.
+ * TODO (b/215515255): remove once we full migrate to shell transitions
+ */
+ private int updateToLegacyIfNeeded(int anim) {
+ if (anim == R.anim.activity_open_enter) {
+ return R.anim.activity_open_enter_legacy;
+ } else if (anim == R.anim.activity_open_exit) {
+ return R.anim.activity_open_exit_legacy;
+ } else if (anim == R.anim.activity_close_enter) {
+ return R.anim.activity_close_enter_legacy;
+ } else if (anim == R.anim.activity_close_exit) {
+ return R.anim.activity_close_exit_legacy;
+ }
+ return anim;
+ }
+
/** Load animation by attribute Id from a specific AnimationStyle resource. */
@Nullable
public Animation loadAnimationAttr(String packageName, int animStyleResId, int animAttr,
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/util/dump/DumpableContainerImpl.java b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
index d48b4b1..d391684 100644
--- a/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
+++ b/core/java/com/android/internal/util/dump/DumpableContainerImpl.java
@@ -15,7 +15,6 @@
*/
package com.android.internal.util.dump;
-import android.annotation.Nullable;
import android.util.ArrayMap;
import android.util.Dumpable;
import android.util.DumpableContainer;
@@ -38,7 +37,6 @@
private static final boolean DEBUG = false;
- @Nullable
private final ArrayMap<String, Dumpable> mDumpables = new ArrayMap<>();
@Override
@@ -64,7 +62,7 @@
* Dumps the number of dumpable, without a newline.
*/
private int dumpNumberDumpables(IndentingPrintWriter writer) {
- int size = mDumpables == null ? 0 : mDumpables.size();
+ int size = mDumpables.size();
if (size == 0) {
writer.print("No dumpables");
} else {
@@ -102,7 +100,7 @@
ipw.println();
return;
}
- ipw.println(": ");
+ ipw.println(":");
for (int i = 0; i < size; i++) {
String dumpableName = mDumpables.keyAt(i);
diff --git a/core/java/com/android/internal/view/OneShotPreDrawListener.java b/core/java/com/android/internal/view/OneShotPreDrawListener.java
index 42d104a..c750c0b 100644
--- a/core/java/com/android/internal/view/OneShotPreDrawListener.java
+++ b/core/java/com/android/internal/view/OneShotPreDrawListener.java
@@ -15,6 +15,7 @@
*/
package com.android.internal.view;
+import android.annotation.NonNull;
import android.view.View;
import android.view.ViewTreeObserver;
@@ -36,7 +37,8 @@
private final Runnable mRunnable;
private final boolean mReturnValue;
- private OneShotPreDrawListener(View view, boolean returnValue, Runnable runnable) {
+ private OneShotPreDrawListener(@NonNull View view, boolean returnValue,
+ @NonNull Runnable runnable) {
mView = view;
mViewTreeObserver = view.getViewTreeObserver();
mRunnable = runnable;
@@ -52,7 +54,7 @@
* @return The added OneShotPreDrawListener. It can be removed prior to
* the onPreDraw by calling {@link #removeListener()}.
*/
- public static OneShotPreDrawListener add(View view, Runnable runnable) {
+ public static OneShotPreDrawListener add(@NonNull View view, @NonNull Runnable runnable) {
return add(view, true, runnable);
}
@@ -65,7 +67,8 @@
* @return The added OneShotPreDrawListener. It can be removed prior to
* the onPreDraw by calling {@link #removeListener()}.
*/
- public static OneShotPreDrawListener add(View view, boolean returnValue, Runnable runnable) {
+ public static OneShotPreDrawListener add(@NonNull View view, boolean returnValue,
+ @NonNull Runnable runnable) {
OneShotPreDrawListener listener = new OneShotPreDrawListener(view, returnValue, runnable);
view.getViewTreeObserver().addOnPreDrawListener(listener);
view.addOnAttachStateChangeListener(listener);
@@ -93,12 +96,12 @@
}
@Override
- public void onViewAttachedToWindow(View v) {
+ public void onViewAttachedToWindow(@NonNull View v) {
mViewTreeObserver = v.getViewTreeObserver();
}
@Override
- public void onViewDetachedFromWindow(View v) {
+ public void onViewDetachedFromWindow(@NonNull View v) {
removeListener();
}
}
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/internal/widget/NestedScrollingChild.java b/core/java/com/android/internal/widget/NestedScrollingChild.java
index 20285b5..c7f5891 100644
--- a/core/java/com/android/internal/widget/NestedScrollingChild.java
+++ b/core/java/com/android/internal/widget/NestedScrollingChild.java
@@ -16,6 +16,7 @@
package com.android.internal.widget;
+import android.annotation.Nullable;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
@@ -149,7 +150,7 @@
* @see #dispatchNestedPreScroll(int, int, int[], int[])
*/
boolean dispatchNestedScroll(int dxConsumed, int dyConsumed,
- int dxUnconsumed, int dyUnconsumed, int[] offsetInWindow);
+ int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow);
/**
* Dispatch one step of a nested scroll in progress before this view consumes any portion of it.
@@ -170,7 +171,8 @@
* @return true if the parent consumed some or all of the scroll delta
* @see #dispatchNestedScroll(int, int, int, int, int[])
*/
- boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow);
+ boolean dispatchNestedPreScroll(int dx, int dy, @Nullable int[] consumed,
+ @Nullable int[] offsetInWindow);
/**
* Dispatch a fling to a nested scrolling parent.
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 39f17e5..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.bp b/core/jni/Android.bp
index 8bb9a0a..955f46b 100644
--- a/core/jni/Android.bp
+++ b/core/jni/Android.bp
@@ -309,8 +309,6 @@
"libdl_android",
"libtimeinstate",
"server_configurable_flags",
- // TODO: delete when ConnectivityT moves to APEX.
- "libframework-connectivity-tiramisu-jni",
],
export_shared_lib_headers: [
// our headers include libnativewindow's public headers
diff --git a/core/jni/android_media_AudioSystem.cpp b/core/jni/android_media_AudioSystem.cpp
index edc8c5b..2bec733 100644
--- a/core/jni/android_media_AudioSystem.cpp
+++ b/core/jni/android_media_AudioSystem.cpp
@@ -18,25 +18,25 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "AudioSystem-JNI"
-#include <utils/Log.h>
-
-#include <sstream>
-#include <vector>
-#include <jni.h>
-#include <nativehelper/JNIHelp.h>
-#include "core_jni_helpers.h"
-
#include <android/media/AudioVibratorInfo.h>
#include <android/media/INativeSpatializerCallback.h>
#include <android/media/ISpatializer.h>
+#include <android_os_Parcel.h>
#include <audiomanager/AudioManager.h>
+#include <jni.h>
#include <media/AudioContainers.h>
#include <media/AudioPolicy.h>
#include <media/AudioSystem.h>
#include <media/MicrophoneInfo.h>
+#include <nativehelper/JNIHelp.h>
#include <nativehelper/ScopedLocalRef.h>
#include <system/audio.h>
#include <system/audio_policy.h>
+#include <utils/Log.h>
+
+#include <sstream>
+#include <vector>
+
#include "android_media_AudioAttributes.h"
#include "android_media_AudioDescriptor.h"
#include "android_media_AudioDeviceAttributes.h"
@@ -46,6 +46,7 @@
#include "android_media_AudioProfile.h"
#include "android_media_MicrophoneInfo.h"
#include "android_util_Binder.h"
+#include "core_jni_helpers.h"
// ----------------------------------------------------------------------------
@@ -584,18 +585,26 @@
env->DeleteLocalRef(clazz);
}
-static jint
-android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz, jint device, jint state, jstring device_address, jstring device_name,
- jint codec)
-{
- const char *c_address = env->GetStringUTFChars(device_address, NULL);
- const char *c_name = env->GetStringUTFChars(device_name, NULL);
- int status = check_AudioSystem_Command(AudioSystem::setDeviceConnectionState(static_cast <audio_devices_t>(device),
- static_cast <audio_policy_dev_state_t>(state),
- c_address, c_name,
- static_cast <audio_format_t>(codec)));
- env->ReleaseStringUTFChars(device_address, c_address);
- env->ReleaseStringUTFChars(device_name, c_name);
+static jint android_media_AudioSystem_setDeviceConnectionState(JNIEnv *env, jobject thiz,
+ jint state, jobject jParcel,
+ jint codec) {
+ int status;
+ if (Parcel *parcel = parcelForJavaObject(env, jParcel); parcel != nullptr) {
+ android::media::audio::common::AudioPort port{};
+ if (status_t statusOfParcel = port.readFromParcel(parcel); statusOfParcel == OK) {
+ status = check_AudioSystem_Command(
+ AudioSystem::setDeviceConnectionState(static_cast<audio_policy_dev_state_t>(
+ state),
+ port,
+ static_cast<audio_format_t>(codec)));
+ } else {
+ ALOGE("Failed to read from parcel: %s", statusToString(statusOfParcel).c_str());
+ status = kAudioStatusError;
+ }
+ } else {
+ ALOGE("Failed to retrieve the native parcel from Java parcel");
+ status = kAudioStatusError;
+ }
return (jint) status;
}
@@ -2912,7 +2921,7 @@
{"newAudioSessionId", "()I", (void *)android_media_AudioSystem_newAudioSessionId},
{"newAudioPlayerId", "()I", (void *)android_media_AudioSystem_newAudioPlayerId},
{"newAudioRecorderId", "()I", (void *)android_media_AudioSystem_newAudioRecorderId},
- {"setDeviceConnectionState", "(IILjava/lang/String;Ljava/lang/String;I)I",
+ {"setDeviceConnectionState", "(ILandroid/os/Parcel;I)I",
(void *)android_media_AudioSystem_setDeviceConnectionState},
{"getDeviceConnectionState", "(ILjava/lang/String;)I",
(void *)android_media_AudioSystem_getDeviceConnectionState},
diff --git a/core/jni/android_util_Binder.cpp b/core/jni/android_util_Binder.cpp
index 61b91dd..13ca133 100644
--- a/core/jni/android_util_Binder.cpp
+++ b/core/jni/android_util_Binder.cpp
@@ -873,7 +873,7 @@
const char* exceptionToThrow;
char msg[128];
// TransactionTooLargeException is a checked exception, only throw from certain methods.
- // FIXME: Transaction too large is the most common reason for FAILED_TRANSACTION
+ // TODO(b/28321379): Transaction size is the most common cause for FAILED_TRANSACTION
// but it is not the only one. The Binder driver can return BR_FAILED_REPLY
// for other reasons also, such as if the transaction is malformed or
// refers to an FD that has been closed. We should change the driver
@@ -890,8 +890,9 @@
exceptionToThrow = (canThrowRemoteException)
? "android/os/DeadObjectException"
: "java/lang/RuntimeException";
- snprintf(msg, sizeof(msg)-1,
- "Transaction failed on small parcel; remote process probably died");
+ snprintf(msg, sizeof(msg) - 1,
+ "Transaction failed on small parcel; remote process probably died, but "
+ "this could also be caused by running out of binder buffer space");
}
jniThrowException(env, exceptionToThrow, msg);
} break;
diff --git a/core/jni/android_util_Process.cpp b/core/jni/android_util_Process.cpp
index 7c67cbc..a6fbf094 100644
--- a/core/jni/android_util_Process.cpp
+++ b/core/jni/android_util_Process.cpp
@@ -235,9 +235,7 @@
void android_os_Process_setProcessGroup(JNIEnv* env, jobject clazz, int pid, jint grp)
{
ALOGV("%s pid=%d grp=%" PRId32, __func__, pid, grp);
- DIR *d;
char proc_path[255];
- struct dirent *de;
if (!verifyGroup(env, grp)) {
return;
@@ -277,84 +275,8 @@
}
}
- sprintf(proc_path, "/proc/%d/task", pid);
- if (!(d = opendir(proc_path))) {
- // If the process exited on us, don't generate an exception
- if (errno != ENOENT)
- signalExceptionForGroupError(env, errno, pid);
- return;
- }
-
- while ((de = readdir(d))) {
- int t_pid;
- int t_pri;
- std::string taskprofile;
-
- if (de->d_name[0] == '.')
- continue;
- t_pid = atoi(de->d_name);
-
- if (!t_pid) {
- ALOGE("Error getting pid for '%s'\n", de->d_name);
- continue;
- }
-
- t_pri = getpriority(PRIO_PROCESS, t_pid);
-
- if (t_pri <= ANDROID_PRIORITY_AUDIO) {
- int scheduler = sched_getscheduler(t_pid) & ~SCHED_RESET_ON_FORK;
- if ((scheduler == SCHED_FIFO) || (scheduler == SCHED_RR)) {
- // This task wants to stay in its current audio group so it can keep its budget
- // don't update its cpuset or cgroup
- continue;
- }
- }
-
- errno = 0;
- // grp == SP_BACKGROUND. Set background cpuset policy profile for all threads.
- if (grp == SP_BACKGROUND) {
- if (!SetTaskProfiles(t_pid, {"CPUSET_SP_BACKGROUND"}, true)) {
- signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid);
- break;
- }
- continue;
- }
-
- // grp != SP_BACKGROUND. Only change the cpuset cgroup for low priority thread, so it could
- // preserve it sched policy profile setting.
- if (t_pri >= ANDROID_PRIORITY_BACKGROUND) {
- switch (grp) {
- case SP_SYSTEM:
- taskprofile = "ServiceCapacityLow";
- break;
- case SP_RESTRICTED:
- taskprofile = "ServiceCapacityRestricted";
- break;
- case SP_FOREGROUND:
- case SP_AUDIO_APP:
- case SP_AUDIO_SYS:
- taskprofile = "ProcessCapacityHigh";
- break;
- case SP_TOP_APP:
- taskprofile = "ProcessCapacityMax";
- break;
- default:
- taskprofile = "ProcessCapacityNormal";
- break;
- }
- if (!SetTaskProfiles(t_pid, {taskprofile}, true)) {
- signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid);
- break;
- }
- // Change the cpuset policy profile for non-low priority thread according to the grp
- } else {
- if (!SetTaskProfiles(t_pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}, true)) {
- signalExceptionForGroupError(env, errno ? errno : EPERM, t_pid);
- break;
- }
- }
- }
- closedir(d);
+ if (!SetProcessProfilesCached(0, pid, {get_cpuset_policy_profile_name((SchedPolicy)grp)}))
+ signalExceptionForGroupError(env, errno ? errno : EPERM, pid);
}
void android_os_Process_setProcessFrozen(
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index a8cf253..1033efa5 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",
@@ -2137,7 +2168,7 @@
(void*)nativeGetLayerId },
{"nativeSetDropInputMode", "(JJI)V",
(void*)nativeSetDropInputMode },
- {"nativeAddTransactionCommittedListener", "(JLandroid/view/TransactionCommittedListener;)V",
+ {"nativeAddTransactionCommittedListener", "(JLandroid/view/SurfaceControl$TransactionCommittedListener;)V",
(void*) nativeAddTransactionCommittedListener },
{"nativeSanitize", "(J)V",
(void*) nativeSanitize }
@@ -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);
@@ -2360,7 +2393,7 @@
"([Landroid/view/SurfaceControl$JankData;)V");
jclass transactionCommittedListenerClazz =
- FindClassOrDie(env, "android/view/TransactionCommittedListener");
+ FindClassOrDie(env, "android/view/SurfaceControl$TransactionCommittedListener");
gTransactionCommittedListenerClassInfo.clazz =
MakeGlobalRefOrDie(env, transactionCommittedListenerClazz);
gTransactionCommittedListenerClassInfo.onTransactionCommitted =
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index aacf700..5efc4db 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -202,6 +202,8 @@
*/
static constexpr int STORAGE_DIR_CHECK_TIMEOUT_US = 1000 * 1000 * 60 * 5;
+static void WaitUntilDirReady(const std::string& target, fail_fn_t fail_fn);
+
/**
* A helper class containing accounting information for USAPs.
*/
@@ -1249,7 +1251,11 @@
auto volPath = StringPrintf("%s/%s", externalPrivateMountPath, ent->d_name);
auto cePath = StringPrintf("%s/user", volPath.c_str());
auto dePath = StringPrintf("%s/user_de", volPath.c_str());
+ // Wait until dir user is created.
+ WaitUntilDirReady(cePath.c_str(), fail_fn);
MountAppDataTmpFs(cePath.c_str(), fail_fn);
+ // Wait until dir user_de is created.
+ WaitUntilDirReady(dePath.c_str(), fail_fn);
MountAppDataTmpFs(dePath.c_str(), fail_fn);
}
closedir(dir);
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 66d5e88..74bf152 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -398,6 +398,7 @@
<protected-broadcast android:name="android.net.wifi.WIFI_AP_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.WIFI_CREDENTIAL_CHANGED" />
<protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_STATE_CHANGED" />
+ <protected-broadcast android:name="android.net.wifi.aware.action.WIFI_AWARE_RESOURCE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.rtt.action.WIFI_RTT_STATE_CHANGED" />
<protected-broadcast android:name="android.net.wifi.SCAN_RESULTS" />
<protected-broadcast android:name="android.net.wifi.RSSI_CHANGED" />
@@ -977,6 +978,61 @@
android:permissionFlags="softRestricted|immutablyRestricted"
android:protectionLevel="dangerous" />
+ <!-- Required to be able to read audio files from shared storage.
+ <p>Protection level: dangerous -->
+ <permission-group android:name="android.permission-group.READ_MEDIA_AURAL"
+ android:icon="@drawable/perm_group_read_media_aural"
+ android:label="@string/permgrouplab_readMediaAural"
+ android:description="@string/permgroupdesc_readMediaAural"
+ android:priority="950" />
+
+ <!-- Allows an application to read audio files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+ must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_AUDIO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaAudio"
+ android:description="@string/permdesc_readMediaAudio"
+ android:protectionLevel="dangerous" />
+
+ <!-- Required to be able to read image and video files from shared storage.
+ <p>Protection level: dangerous -->
+ <permission-group android:name="android.permission-group.READ_MEDIA_VISUAL"
+ android:icon="@drawable/perm_group_read_media_visual"
+ android:label="@string/permgrouplab_readMediaVisual"
+ android:description="@string/permgroupdesc_readMediaVisual"
+ android:priority="1000" />
+
+ <!-- Allows an application to read audio files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+ must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_VIDEO"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaVideo"
+ android:description="@string/permdesc_readMediaVideo"
+ android:protectionLevel="dangerous" />
+
+ <!-- Allows an application to read image files from external storage.
+ <p>This permission is enforced starting in API level
+ {@link android.os.Build.VERSION_CODES#TIRAMISU}.
+ For apps with a <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code
+ targetSdkVersion}</a> of {@link android.os.Build.VERSION_CODES#S} or lower, this permission
+ must not be used and the READ_EXTERNAL_STORAGE permission must be used instead.
+ <p>Protection level: dangerous -->
+ <permission android:name="android.permission.READ_MEDIA_IMAGE"
+ android:permissionGroup="android.permission-group.UNDEFINED"
+ android:label="@string/permlab_readMediaImage"
+ android:description="@string/permdesc_readMediaImage"
+ android:protectionLevel="dangerous" />
+
<!-- Allows an application to write to external storage.
<p class="note"><strong>Note:</strong> If <em>both</em> your <a
href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code
@@ -2027,6 +2083,11 @@
<permission android:name="android.permission.CONTROL_OEM_PAID_NETWORK_PREFERENCE"
android:protectionLevel="signature" />
+ <!-- @SystemApi @hide Allows an application to manage ethernet networks.
+ <p>Not for use by third-party or privileged applications. -->
+ <permission android:name="android.permission.MANAGE_ETHERNET_NETWORKS"
+ android:protectionLevel="signature" />
+
<!-- ======================================= -->
<!-- Permissions for short range, peripheral networks -->
<!-- ======================================= -->
@@ -4125,6 +4186,16 @@
<permission android:name="android.permission.BIND_CONTENT_SUGGESTIONS_SERVICE"
android:protectionLevel="signature" />
+ <!-- Must be required by a
+ android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService,
+ to ensure that only the system can bind to it.
+ @SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
+ <p>Protection level: signature
+ -->
+ <permission android:name="android.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE"
+ android:protectionLevel="signature" />
+
+
<!-- Must be declared by a android.service.musicrecognition.MusicRecognitionService,
to ensure that only the system can bind to it.
@SystemApi @hide This is not a third-party API (intended for OEMs and system apps).
@@ -5824,6 +5895,13 @@
<permission android:name="android.permission.MANAGE_SMARTSPACE"
android:protectionLevel="signature" />
+ <!-- @SystemApi Allows an application to manage the wallpaper effects
+ generation service.
+ @hide <p>Not for use by third-party applications.</p> -->
+ <permission android:name="android.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION"
+ android:protectionLevel="signature" />
+
+
<!-- Allows an app to set the theme overlay in /vendor/overlay
being used.
@hide <p>Not for use by third-party applications.</p> -->
@@ -5923,6 +6001,10 @@
<p>Not for use by third-party applications. -->
<permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND"
android:protectionLevel="signature" />
+ <!-- @hide Permission that suppresses the notification when the clipboard is accessed.
+ <p>Not for use by third-party applications. -->
+ <permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION"
+ android:protectionLevel="signature" />
<!-- @SystemApi Allows modifying accessibility state.
@hide -->
@@ -6519,12 +6601,27 @@
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"
android:exported="false">
</activity>
+ <activity android:name="android.service.games.GameSessionTrampolineActivity"
+ android:excludeFromRecents="true"
+ android:exported="true"
+ android:permission="android.permission.MANAGE_GAME_ACTIVITY"
+ android:theme="@style/Theme.Translucent.NoTitleBar">
+ </activity>
+
<receiver android:name="com.android.server.BootReceiver"
android:exported="true"
android:systemUserOnly="true">
diff --git a/core/res/res/anim/activity_close_enter.xml b/core/res/res/anim/activity_close_enter.xml
index 9fa7c54..0fefb51 100644
--- a/core/res/res/anim/activity_close_enter.xml
+++ b/core/res/res/anim/activity_close_enter.xml
@@ -19,16 +19,37 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <scale
- android:fromXScale="1.1"
- android:toXScale="1"
- android:fromYScale="1.1"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
+
+ <alpha
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="0"
+ android:duration="450" />
+
+ <translate
+ android:fromXDelta="-10%"
+ android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
+ android:startOffset="0"
+ android:duration="450" />
+
+ <extend
+ android:fromExtendLeft="0"
+ android:fromExtendTop="0"
+ android:fromExtendRight="10%"
+ android:fromExtendBottom="0"
+ android:toExtendLeft="0"
+ android:toExtendTop="0"
+ android:toExtendRight="10%"
+ android:toExtendBottom="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_enter_legacy.xml b/core/res/res/anim/activity_close_enter_legacy.xml
new file mode 100644
index 0000000..9fa7c54
--- /dev/null
+++ b/core/res/res/anim/activity_close_enter_legacy.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <scale
+ android:fromXScale="1.1"
+ android:toXScale="1"
+ android:fromYScale="1.1"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="400"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_close_exit.xml b/core/res/res/anim/activity_close_exit.xml
index 1599ae8..f807c26 100644
--- a/core/res/res/anim/activity_close_exit.xml
+++ b/core/res/res/anim/activity_close_exit.xml
@@ -18,27 +18,38 @@
-->
<set xmlns:android="http://schemas.android.com/apk/res/android"
- android:shareInterpolator="false"
- android:zAdjustment="top">
+ android:shareInterpolator="false">
+
<alpha
- android:fromAlpha="1"
+ android:fromAlpha="1.0"
android:toAlpha="0.0"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/linear"
- android:startOffset="33"
- android:duration="50"/>
- <scale
- android:fromXScale="1"
- android:toXScale="0.9"
- android:fromYScale="1"
- android:toYScale="0.9"
- android:pivotX="50%"
- android:pivotY="50%"
+ android:startOffset="35"
+ android:duration="83" />
+
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="10%"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
+ android:startOffset="0"
+ android:duration="450" />
+
+ <extend
+ android:fromExtendLeft="10%"
+ android:fromExtendTop="0"
+ android:fromExtendRight="0"
+ android:fromExtendBottom="0"
+ android:toExtendLeft="10%"
+ android:toExtendTop="0"
+ android:toExtendRight="0"
+ android:toExtendBottom="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
</set>
diff --git a/core/res/res/anim/activity_close_exit_legacy.xml b/core/res/res/anim/activity_close_exit_legacy.xml
new file mode 100644
index 0000000..1599ae8
--- /dev/null
+++ b/core/res/res/anim/activity_close_exit_legacy.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false"
+ android:zAdjustment="top">
+ <alpha
+ android:fromAlpha="1"
+ android:toAlpha="0.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="33"
+ android:duration="50"/>
+ <scale
+ android:fromXScale="1"
+ android:toXScale="0.9"
+ android:fromYScale="1"
+ android:toYScale="0.9"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="400"/>
+</set>
diff --git a/core/res/res/anim/activity_open_enter.xml b/core/res/res/anim/activity_open_enter.xml
index 38d3e8ed..1674dab 100644
--- a/core/res/res/anim/activity_open_enter.xml
+++ b/core/res/res/anim/activity_open_enter.xml
@@ -18,6 +18,7 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
+
<alpha
android:fromAlpha="0"
android:toAlpha="1.0"
@@ -26,17 +27,27 @@
android:fillAfter="true"
android:interpolator="@interpolator/linear"
android:startOffset="50"
- android:duration="50"/>
- <scale
- android:fromXScale="0.85"
- android:toXScale="1"
- android:fromYScale="0.85"
- android:toYScale="1"
- android:pivotX="50%"
- android:pivotY="50%"
+ android:duration="83" />
+
+ <translate
+ android:fromXDelta="10%"
+ android:toXDelta="0"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
+ android:duration="450" />
+
+ <extend
+ android:fromExtendLeft="10%"
+ android:fromExtendTop="0"
+ android:fromExtendRight="0"
+ android:fromExtendBottom="0"
+ android:toExtendLeft="10%"
+ android:toExtendTop="0"
+ android:toExtendRight="0"
+ android:toExtendBottom="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
</set>
diff --git a/core/res/res/anim/activity_open_enter_legacy.xml b/core/res/res/anim/activity_open_enter_legacy.xml
new file mode 100644
index 0000000..38d3e8ed
--- /dev/null
+++ b/core/res/res/anim/activity_open_enter_legacy.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+ <alpha
+ android:fromAlpha="0"
+ android:toAlpha="1.0"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="50"
+ android:duration="50"/>
+ <scale
+ android:fromXScale="0.85"
+ android:toXScale="1"
+ android:fromYScale="0.85"
+ android:toYScale="1"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="400"/>
+</set>
diff --git a/core/res/res/anim/activity_open_exit.xml b/core/res/res/anim/activity_open_exit.xml
index 3865d21..372f2c8 100644
--- a/core/res/res/anim/activity_open_exit.xml
+++ b/core/res/res/anim/activity_open_exit.xml
@@ -19,27 +19,36 @@
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
- <!-- Fade out, over a black surface, which simulates a black scrim -->
<alpha
- android:fromAlpha="1"
- android:toAlpha="0.4"
+ android:fromAlpha="1.0"
+ android:toAlpha="1.0"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
- android:interpolator="@interpolator/linear"
- android:startOffset="83"
- android:duration="167"/>
+ android:interpolator="@interpolator/standard_accelerate"
+ android:startOffset="0"
+ android:duration="450" />
- <scale
- android:fromXScale="1"
- android:toXScale="1.05"
- android:fromYScale="1"
- android:toYScale="1.05"
- android:pivotX="50%"
- android:pivotY="50%"
+ <translate
+ android:fromXDelta="0"
+ android:toXDelta="-10%"
android:fillEnabled="true"
android:fillBefore="true"
android:fillAfter="true"
android:interpolator="@interpolator/fast_out_extra_slow_in"
- android:duration="400"/>
+ android:startOffset="0"
+ android:duration="450" />
+
+ <extend
+ android:fromExtendLeft="0"
+ android:fromExtendTop="0"
+ android:fromExtendRight="10%"
+ android:fromExtendBottom="0"
+ android:toExtendLeft="0"
+ android:toExtendTop="0"
+ android:toExtendRight="10%"
+ android:toExtendBottom="0"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:startOffset="0"
+ android:duration="450" />
</set>
\ No newline at end of file
diff --git a/core/res/res/anim/activity_open_exit_legacy.xml b/core/res/res/anim/activity_open_exit_legacy.xml
new file mode 100644
index 0000000..3865d21
--- /dev/null
+++ b/core/res/res/anim/activity_open_exit_legacy.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+/*
+** Copyright 2009, 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.
+*/
+-->
+
+<set xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shareInterpolator="false">
+
+ <!-- Fade out, over a black surface, which simulates a black scrim -->
+ <alpha
+ android:fromAlpha="1"
+ android:toAlpha="0.4"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/linear"
+ android:startOffset="83"
+ android:duration="167"/>
+
+ <scale
+ android:fromXScale="1"
+ android:toXScale="1.05"
+ android:fromYScale="1"
+ android:toYScale="1.05"
+ android:pivotX="50%"
+ android:pivotY="50%"
+ android:fillEnabled="true"
+ android:fillBefore="true"
+ android:fillAfter="true"
+ android:interpolator="@interpolator/fast_out_extra_slow_in"
+ android:duration="400"/>
+</set>
\ No newline at end of file
diff --git a/core/res/res/drawable/perm_group_read_media_aural.xml b/core/res/res/drawable/perm_group_read_media_aural.xml
new file mode 100644
index 0000000..6fc9c69
--- /dev/null
+++ b/core/res/res/drawable/perm_group_read_media_aural.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M10,21q-1.65,0 -2.825,-1.175Q6,18.65 6,17q0,-1.65 1.175,-2.825Q8.35,13 10,13q0.575,0 1.063,0.137 0.487,0.138 0.937,0.413V3h6v4h-4v10q0,1.65 -1.175,2.825Q11.65,21 10,21z"/>
+</vector>
\ No newline at end of file
diff --git a/core/res/res/drawable/perm_group_read_media_visual.xml b/core/res/res/drawable/perm_group_read_media_visual.xml
new file mode 100644
index 0000000..a5db271
--- /dev/null
+++ b/core/res/res/drawable/perm_group_read_media_visual.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2015 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="?attr/colorControlNormal">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M9,14h10l-3.45,-4.5 -2.3,3 -1.55,-2zM8,18q-0.825,0 -1.412,-0.587Q6,16.825 6,16L6,4q0,-0.825 0.588,-1.413Q7.175,2 8,2h12q0.825,0 1.413,0.587Q22,3.175 22,4v12q0,0.825 -0.587,1.413Q20.825,18 20,18zM8,16h12L20,4L8,4v12zM4,22q-0.825,0 -1.413,-0.587Q2,20.825 2,20L2,6h2v14h14v2zM8,4v12L8,4z"/>
+</vector>
\ No newline at end of file
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.xml b/core/res/res/values/attrs.xml
index 04e2989..d774fd4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -6953,10 +6953,10 @@
</declare-styleable>
<declare-styleable name="TranslateAnimation">
- <attr name="fromXDelta" format="float|fraction" />
- <attr name="toXDelta" format="float|fraction" />
- <attr name="fromYDelta" format="float|fraction" />
- <attr name="toYDelta" format="float|fraction" />
+ <attr name="fromXDelta" format="float|fraction|dimension" />
+ <attr name="toXDelta" format="float|fraction|dimension" />
+ <attr name="fromYDelta" format="float|fraction|dimension" />
+ <attr name="toYDelta" format="float|fraction|dimension" />
</declare-styleable>
<declare-styleable name="AlphaAnimation">
@@ -8875,6 +8875,22 @@
<attr name="gameSessionService" format="string" />
</declare-styleable>
+ <!-- Use <code>game-mode-config</code> as the root tag of the XML resource that
+ describes a GameModeConfig.
+ Described here are the attributes that can be included in that tag. -->
+ <declare-styleable name="GameModeConfig">
+ <!-- Set true to opt in BATTERY mode. -->
+ <attr name="supportsBatteryGameMode" format="boolean" />
+ <!-- Set true to opt in PERFORMANCE mode. -->
+ <attr name="supportsPerformanceGameMode" format="boolean" />
+ <!-- Set true to enable ANGLE. -->
+ <attr name="allowGameAngleDriver" format="boolean" />
+ <!-- Set true to allow resolution downscaling intervention. -->
+ <attr name="allowGameDownscaling" format="boolean" />
+ <!-- Set true to allow FPS override intervention. -->
+ <attr name="allowGameFpsOverride" format="boolean" />
+ </declare-styleable>
+
<!-- Use <code>voice-enrollment-application</code>
as the root tag of the XML resource that escribes the supported keyphrases (hotwords)
by the enrollment application.
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/colors.xml b/core/res/res/values/colors.xml
index 59d6005..54325e5 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -448,4 +448,7 @@
<color name="accessibility_magnification_background">#F50D60</color>
<color name="accessibility_daltonizer_background">#00BCD4</color>
<color name="accessibility_color_inversion_background">#546E7A</color>
+
+ <!-- Color of camera light when camera is in use -->
+ <color name="camera_privacy_light">#FFFFFF</color>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a06b2cb..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>
@@ -4173,6 +4179,15 @@
<string name="config_defaultMusicRecognitionService" translatable="false"></string>
+ <!-- The package name for the system's wallpaper effects generation service.
+ This service returns wallpaper effects results.
+ This service must be trusted, as it can be activated without explicit consent of the user.
+ If no service with the specified name exists on the device, wallpaper effects
+ generation service will be disabled.
+ Example: "com.android.intelligence/.WallpaperEffectsGenerationService"
+-->
+ <string name="config_defaultWallpaperEffectsGenerationService" translatable="false"></string>
+
<!-- The package name for the default retail demo app.
This package must be trusted, as it has the permissions to query the usage stats on the
device.
@@ -5267,6 +5282,12 @@
<bool name="config_cecTvSendStandbyOnSleepDisabled_allowed">true</bool>
<bool name="config_cecTvSendStandbyOnSleepDisabled_default">false</bool>
+ <bool name="config_cecSetMenuLanguage_userConfigurable">true</bool>
+ <bool name="config_cecSetMenuLanguageEnabled_allowed">true</bool>
+ <bool name="config_cecSetMenuLanguageEnabled_default">true</bool>
+ <bool name="config_cecSetMenuLanguageDisabled_allowed">true</bool>
+ <bool name="config_cecSetMenuLanguageDisabled_default">false</bool>
+
<bool name="config_cecRcProfileTv_userConfigurable">true</bool>
<bool name="config_cecRcProfileTvNone_allowed">true</bool>
<bool name="config_cecRcProfileTvNone_default">true</bool>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 047c04b..d57f5ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3255,6 +3255,11 @@
<public name="showClockAndComplications" />
<!-- @hide @SystemApi -->
<public name="gameSessionService" />
+ <public name="supportsBatteryGameMode" />
+ <public name="supportsPerformanceGameMode" />
+ <public name="allowGameAngleDriver" />
+ <public name="allowGameDownscaling" />
+ <public name="allowGameFpsOverride" />
<public name="localeConfig" />
<public name="showBackground" />
<public name="inheritKeyStoreKeys" />
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index 610c6a6..47b4d38 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -876,9 +876,19 @@
<string name="permgroupdesc_sms">send and view SMS messages</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgrouplab_storage">Files and media</string>
+ <string name="permgrouplab_storage">Files & documents</string>
<!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
- <string name="permgroupdesc_storage">access photos, media, and files on your device</string>
+ <string name="permgroupdesc_storage">access files and documents on your device</string>
+
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
+ <string name="permgrouplab_readMediaAural">Music & other audio</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
+ <string name="permgroupdesc_readMediaAural">access audio files on your device</string>
+
+ <!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=40]-->
+ <string name="permgrouplab_readMediaVisual">Photos & videos</string>
+ <!-- Description of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. [CHAR LIMIT=NONE]-->
+ <string name="permgroupdesc_readMediaVisual">access images and video files on your device</string>
<!-- Title of a category of application permissions, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permgrouplab_microphone">Microphone</string>
@@ -1893,6 +1903,21 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
<string name="permdesc_sdcardRead">Allows the app to read the contents of your shared storage.</string>
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permlab_readMediaAudio">read audio files from shared storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permdesc_readMediaAudio">Allows the app to read audio files from your shared storage.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permlab_readMediaVideo">read video files from shared storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permdesc_readMediaVideo">Allows the app to read video files from your shared storage.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permlab_readMediaImage">read image files from shared storage</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can read from. [CHAR LIMIT=none] -->
+ <string name="permdesc_readMediaImage">Allows the app to read image files from your shared storage.</string>
+
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
<string name="permlab_sdcardWrite">modify or delete the contents of your shared storage</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. "shared storage" refers to a storage space on the device that all apps with this permission can write to. [CHAR LIMIT=none] -->
@@ -5439,15 +5464,6 @@
<xliff:g id="app_name" example="Gmail">%1$s</xliff:g> is not available right now.
</string>
- <!-- Title of the dialog shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
- <string name="app_streaming_blocked_title"><xliff:g id="activity" example="Permission dialog">%1$s</xliff:g> unavailable</string>
- <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
- <string name="app_streaming_blocked_message" product="tv">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your Android TV device instead.</string>
- <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
- <string name="app_streaming_blocked_message" product="tablet">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your tablet instead.</string>
- <!-- Message shown when an app is blocked from being streamed to a remote device. [CHAR LIMIT=NONE] -->
- <string name="app_streaming_blocked_message" product="default">This can’t be accessed on your <xliff:g id="device" example="Chromebook">%1$s</xliff:g> at this time. Try on your phone instead.</string>
-
<!-- Message displayed in dialog when app is too old to run on this verison of android. [CHAR LIMIT=NONE] -->
<string name="deprecated_target_sdk_message">This app was built for an older version of Android and may not work properly. Try checking for updates, or contact the developer.</string>
<!-- Title for button to see application detail in app store which it came from - it may allow user to update to newer version. [CHAR LIMIT=50] -->
@@ -5689,6 +5705,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>
@@ -5857,6 +5887,8 @@
<string name="accessibility_system_action_lock_screen_label">Lock Screen</string>
<!-- Label for taking screenshot action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_screenshot_label">Screenshot</string>
+ <!-- Label for headset hook action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_headset_hook_label">Headset Hook</string>
<!-- Label for triggering on-screen accessibility shortcut action [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_on_screen_a11y_shortcut_label">On-screen Accessibility Shortcut</string>
<!-- Label for showing on-screen accessibility shortcut chooser action [CHAR LIMIT=NONE] -->
@@ -5865,6 +5897,16 @@
<string name="accessibility_system_action_hardware_a11y_shortcut_label">Accessibility Shortcut</string>
<!-- Label for dismissing the notification shade [CHAR LIMIT=NONE] -->
<string name="accessibility_system_action_dismiss_notification_shade">Dismiss Notification Shade</string>
+ <!-- Label for Dpad up action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dpad_up_label">Dpad Up</string>
+ <!-- Label for Dpad down action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dpad_down_label">Dpad Down</string>
+ <!-- Label for Dpad left action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dpad_left_label">Dpad Left</string>
+ <!-- Label for Dpad right action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dpad_right_label">Dpad Right</string>
+ <!-- Label for Dpad center action [CHAR LIMIT=NONE] -->
+ <string name="accessibility_system_action_dpad_center_label">Dpad Center</string>
<!-- Accessibility description of caption view -->
<string name="accessibility_freeform_caption">Caption bar of <xliff:g id="app_name">%1$s</xliff:g>.</string>
@@ -5924,9 +5966,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 4c1cc4d..2b25c3e 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" />
@@ -1697,7 +1704,13 @@
<java-symbol type="anim" name="activity_translucent_open_enter" />
<java-symbol type="anim" name="activity_translucent_close_exit" />
<java-symbol type="anim" name="activity_open_enter" />
+ <java-symbol type="anim" name="activity_open_exit" />
+ <java-symbol type="anim" name="activity_close_enter" />
<java-symbol type="anim" name="activity_close_exit" />
+ <java-symbol type="anim" name="activity_open_enter_legacy" />
+ <java-symbol type="anim" name="activity_open_exit_legacy" />
+ <java-symbol type="anim" name="activity_close_enter_legacy" />
+ <java-symbol type="anim" name="activity_close_exit_legacy" />
<java-symbol type="anim" name="task_fragment_close_enter" />
<java-symbol type="anim" name="task_fragment_close_exit" />
<java-symbol type="anim" name="task_fragment_open_enter" />
@@ -2215,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" />
@@ -2707,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" />
@@ -3269,9 +3285,6 @@
<java-symbol type="string" name="app_blocked_title" />
<java-symbol type="string" name="app_blocked_message" />
- <java-symbol type="string" name="app_streaming_blocked_title" />
- <java-symbol type="string" name="app_streaming_blocked_message" />
-
<!-- Used internally for assistant to launch activity transitions -->
<java-symbol type="id" name="cross_task_transition" />
@@ -3673,6 +3686,7 @@
<java-symbol type="string" name="config_defaultContentSuggestionsService" />
<java-symbol type="string" name="config_defaultSearchUiService" />
<java-symbol type="string" name="config_defaultSmartspaceService" />
+ <java-symbol type="string" name="config_defaultWallpaperEffectsGenerationService" />
<java-symbol type="string" name="config_defaultMusicRecognitionService" />
<java-symbol type="string" name="config_defaultAttentionService" />
<java-symbol type="string" name="config_defaultRotationResolverService" />
@@ -3848,6 +3862,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" />
@@ -4129,10 +4148,16 @@
<java-symbol type="string" name="accessibility_system_action_quick_settings_label" />
<java-symbol type="string" name="accessibility_system_action_recents_label" />
<java-symbol type="string" name="accessibility_system_action_screenshot_label" />
+ <java-symbol type="string" name="accessibility_system_action_headset_hook_label" />
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_label" />
<java-symbol type="string" name="accessibility_system_action_on_screen_a11y_shortcut_chooser_label" />
<java-symbol type="string" name="accessibility_system_action_hardware_a11y_shortcut_label" />
<java-symbol type="string" name="accessibility_system_action_dismiss_notification_shade" />
+ <java-symbol type="string" name="accessibility_system_action_dpad_up_label" />
+ <java-symbol type="string" name="accessibility_system_action_dpad_down_label" />
+ <java-symbol type="string" name="accessibility_system_action_dpad_left_label" />
+ <java-symbol type="string" name="accessibility_system_action_dpad_right_label" />
+ <java-symbol type="string" name="accessibility_system_action_dpad_center_label" />
<java-symbol type="string" name="accessibility_freeform_caption" />
@@ -4453,6 +4478,12 @@
<java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_allowed" />
<java-symbol type="bool" name="config_cecTvSendStandbyOnSleepDisabled_default" />
+ <java-symbol type="bool" name="config_cecSetMenuLanguage_userConfigurable" />
+ <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_allowed" />
+ <java-symbol type="bool" name="config_cecSetMenuLanguageEnabled_default" />
+ <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_allowed" />
+ <java-symbol type="bool" name="config_cecSetMenuLanguageDisabled_default" />
+
<java-symbol type="bool" name="config_cecRcProfileTv_userConfigurable" />
<java-symbol type="bool" name="config_cecRcProfileTvNone_allowed" />
<java-symbol type="bool" name="config_cecRcProfileTvNone_default" />
@@ -4691,4 +4722,6 @@
<java-symbol type="bool" name="config_lowPowerStandbySupported" />
<java-symbol type="bool" name="config_lowPowerStandbyEnabledByDefault" />
<java-symbol type="integer" name="config_lowPowerStandbyNonInteractiveTimeout" />
+
+ <java-symbol type="color" name="camera_privacy_light"/>
</resources>
diff --git a/core/tests/bandwidthtests/Android.bp b/core/tests/bandwidthtests/Android.bp
index f1ecd45..d0b42f7 100644
--- a/core/tests/bandwidthtests/Android.bp
+++ b/core/tests/bandwidthtests/Android.bp
@@ -23,6 +23,7 @@
android_test {
name: "BandwidthTests",
+ defaults: ["framework-connectivity-test-defaults"],
// Include all test java files.
srcs: ["src/**/*.java"],
libs: [
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/benchmarks/Android.bp b/core/tests/benchmarks/Android.bp
index 4cd5467..0888776 100644
--- a/core/tests/benchmarks/Android.bp
+++ b/core/tests/benchmarks/Android.bp
@@ -27,6 +27,7 @@
java_library {
name: "frameworks-base-core-benchmarks",
+ defaults: ["framework-connectivity-test-defaults"],
installable: true,
srcs: ["src/**/*.java"],
libs: ["caliper-api-target"],
diff --git a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
index 3c8f90c..6360a2d 100644
--- a/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
+++ b/core/tests/coretests/src/android/net/NetworkPolicyTest.kt
@@ -16,7 +16,9 @@
package android.net
+import android.net.NetworkStats.METERED_YES
import android.net.NetworkTemplate.MATCH_BLUETOOTH
+import android.net.NetworkTemplate.MATCH_CARRIER
import android.net.NetworkTemplate.MATCH_ETHERNET
import android.net.NetworkTemplate.MATCH_MOBILE
import android.net.NetworkTemplate.MATCH_WIFI
@@ -39,11 +41,19 @@
@Test
fun testTemplateBackupRestore() {
assertPolicyBackupRestore(createTestPolicyForTemplate(
- NetworkTemplate.buildTemplateWifi(TEST_WIFI_NETWORK_KEY1)))
+ NetworkTemplate.Builder(MATCH_WIFI)
+ .setWifiNetworkKeys(setOf(TEST_WIFI_NETWORK_KEY1))
+ .build()))
assertPolicyBackupRestore(createTestPolicyForTemplate(
- NetworkTemplate.buildTemplateMobileAll(TEST_IMSI1)))
+ NetworkTemplate.Builder(MATCH_MOBILE)
+ .setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES)
+ .build()))
assertPolicyBackupRestore(createTestPolicyForTemplate(
- NetworkTemplate.buildTemplateCarrierMetered(TEST_IMSI1)))
+ NetworkTemplate.Builder(MATCH_CARRIER)
+ .setSubscriberIds(setOf(TEST_IMSI1))
+ .setMeteredness(METERED_YES)
+ .build()))
}
private fun createTestPolicyForTemplate(template: NetworkTemplate): NetworkPolicy {
diff --git a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
index 6df9002..ddc27aa 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityEventTest.java
@@ -42,7 +42,7 @@
// and assertAccessibilityEventCleared
/** The number of properties of the {@link AccessibilityEvent} class. */
- private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 32;
+ private static final int A11Y_EVENT_NON_STATIC_FIELD_COUNT = 33;
// The number of fields tested in the corresponding CTS AccessibilityRecordTest:
// assertAccessibilityRecordCleared, fullyPopulateAccessibilityRecord,
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/os/MobileRadioPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
index c20293b..95225b2 100644
--- a/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/MobileRadioPowerCalculatorTest.java
@@ -16,6 +16,9 @@
package com.android.internal.os;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
@@ -94,7 +97,8 @@
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
@@ -160,7 +164,8 @@
// Note application network activity
mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 10000, 10000,
mNetworkStatsManager);
@@ -169,7 +174,8 @@
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, POWER_DATA_UNAVAILABLE, 12000, 12000,
mNetworkStatsManager);
@@ -241,7 +247,8 @@
// Note application network activity
NetworkStats networkStats = new NetworkStats(10000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100);
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100));
mStatsRule.setNetworkStats(networkStats);
ModemActivityInfo mai = new ModemActivityInfo(10000, 2000, 3000,
@@ -306,7 +313,8 @@
// Note application network activity
mStatsRule.setNetworkStats(new NetworkStats(10000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 100, 2000, 20, 100));
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 100, 2000, 20, 100)));
stats.noteModemControllerActivity(null, 10_000_000, 10000, 10000, mNetworkStatsManager);
@@ -314,7 +322,8 @@
BatteryStats.Uid.PROCESS_STATE_BACKGROUND, 11000);
mStatsRule.setNetworkStats(new NetworkStats(12000, 1)
- .insertEntry("cellular", APP_UID, 0, 0, 1000, 250, 2000, 80, 200));
+ .addEntry(new NetworkStats.Entry("cellular", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1000, 250, 2000, 80, 200)));
stats.noteModemControllerActivity(null, 15_000_000, 12000, 12000, mNetworkStatsManager);
diff --git a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
index a368399..f74e72b 100644
--- a/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
+++ b/core/tests/coretests/src/com/android/internal/os/WifiPowerCalculatorTest.java
@@ -17,6 +17,9 @@
package com.android.internal.os;
+import static android.net.NetworkStats.DEFAULT_NETWORK_NO;
+import static android.net.NetworkStats.METERED_NO;
+import static android.net.NetworkStats.ROAMING_NO;
import static android.os.BatteryStats.POWER_DATA_UNAVAILABLE;
import static com.google.common.truth.Truth.assertThat;
@@ -77,8 +80,12 @@
private NetworkStats buildNetworkStats(long elapsedRealtime, int rxBytes, int rxPackets,
int txBytes, int txPackets) {
return new NetworkStats(elapsedRealtime, 1)
- .insertEntry("wifi", APP_UID, 0, 0, rxBytes, rxPackets, txBytes, txPackets, 100)
- .insertEntry("wifi", Process.WIFI_UID, 0, 0, 1111, 111, 2222, 22, 111);
+ .addEntry(new NetworkStats.Entry("wifi", APP_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, rxBytes, rxPackets,
+ txBytes, txPackets, 100))
+ .addEntry(new NetworkStats.Entry("wifi", Process.WIFI_UID, 0, 0,
+ METERED_NO, ROAMING_NO, DEFAULT_NETWORK_NO, 1111, 111,
+ 2222, 22, 111));
}
/** Sets up an WifiActivityEnergyInfo for ActivityController-model-based tests. */
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/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
index f04a9f7..e16a2f8 100644
--- a/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
+++ b/core/tests/hdmitests/src/android/hardware/hdmi/HdmiAudioSystemClientTest.java
@@ -288,9 +288,8 @@
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
- }
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {}
@Override
public void sendVendorCommand(final int deviceType, final int targetAddress,
diff --git a/core/tests/utillib/Android.bp b/core/tests/utillib/Android.bp
index d40d1d2..1d5c16c 100644
--- a/core/tests/utillib/Android.bp
+++ b/core/tests/utillib/Android.bp
@@ -23,6 +23,7 @@
java_library {
name: "frameworks-core-util-lib",
+ defaults: ["framework-connectivity-test-defaults"],
srcs: ["**/*.java"],
diff --git a/data/etc/com.android.settings.xml b/data/etc/com.android.settings.xml
index ddcab6e..5dcc599 100644
--- a/data/etc/com.android.settings.xml
+++ b/data/etc/com.android.settings.xml
@@ -61,5 +61,6 @@
<permission name="android.permission.READ_DREAM_SUPPRESSION"/>
<permission name="android.permission.RESTART_WIFI_SUBSYSTEM"/>
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
+ <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
</privapp-permissions>
</permissions>
diff --git a/data/etc/platform.xml b/data/etc/platform.xml
index 92fca36..88920c8 100644
--- a/data/etc/platform.xml
+++ b/data/etc/platform.xml
@@ -231,6 +231,18 @@
targetSdk="29">
<new-permission name="android.permission.ACCESS_MEDIA_LOCATION" />
</split-permission>
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="33">
+ <new-permission name="android.permission.READ_MEDIA_AUDIO" />
+ </split-permission>
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="33">
+ <new-permission name="android.permission.READ_MEDIA_VIDEO" />
+ </split-permission>
+ <split-permission name="android.permission.READ_EXTERNAL_STORAGE"
+ targetSdk="33">
+ <new-permission name="android.permission.READ_MEDIA_IMAGE" />
+ </split-permission>
<split-permission name="android.permission.BLUETOOTH"
targetSdk="31">
<new-permission name="android.permission.BLUETOOTH_SCAN" />
diff --git a/data/etc/privapp-permissions-platform.xml b/data/etc/privapp-permissions-platform.xml
index b3dcc34..83c4024 100644
--- a/data/etc/privapp-permissions-platform.xml
+++ b/data/etc/privapp-permissions-platform.xml
@@ -334,6 +334,7 @@
<permission name="android.permission.MANAGE_ACCESSIBILITY"/>
<permission name="android.permission.MANAGE_DEVICE_ADMINS"/>
<permission name="android.permission.MANAGE_GAME_MODE"/>
+ <permission name="android.permission.MANAGE_GAME_ACTIVITY" />
<permission name="android.permission.MANAGE_LOW_POWER_STANDBY" />
<permission name="android.permission.MANAGE_ROLLBACKS"/>
<permission name="android.permission.MANAGE_USB"/>
@@ -400,6 +401,7 @@
<!-- Permission required for CTS test - TrustTestCases -->
<permission name="android.permission.PROVIDE_TRUST_AGENT" />
<permission name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <permission name="android.permission.TRUST_LISTENER" />
<!-- Permissions required for Incremental CTS tests -->
<permission name="com.android.permission.USE_INSTALLER_V2"/>
<permission name="android.permission.LOADER_USAGE_STATS"/>
@@ -474,6 +476,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"/>
@@ -581,6 +584,7 @@
<permission name="android.permission.INSTALL_DYNAMIC_SYSTEM"/>
<permission name="android.permission.BIND_CELL_BROADCAST_SERVICE"/>
<permission name="android.permission.READ_SAFETY_CENTER_STATUS" />
+ <permission name="android.permission.SEND_SAFETY_CENTER_UPDATE" />
</privapp-permissions>
<privapp-permissions package="com.android.bips">
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/graphics/java/android/graphics/ImageDecoder.java b/graphics/java/android/graphics/ImageDecoder.java
index 3732285..1629b6a 100644
--- a/graphics/java/android/graphics/ImageDecoder.java
+++ b/graphics/java/android/graphics/ImageDecoder.java
@@ -368,7 +368,7 @@
* Further, unlike other Sources, this one is not reusable.
*/
private static class InputStreamSource extends Source {
- InputStreamSource(Resources res, InputStream is, int inputDensity) {
+ InputStreamSource(Resources res, @NonNull InputStream is, int inputDensity) {
if (is == null) {
throw new IllegalArgumentException("The InputStream cannot be null");
}
@@ -1020,7 +1020,7 @@
*/
@AnyThread
@NonNull
- public static Source createSource(Resources res, InputStream is) {
+ public static Source createSource(Resources res, @NonNull InputStream is) {
return new InputStreamSource(res, is, Bitmap.getDefaultDensity());
}
@@ -1034,7 +1034,7 @@
@AnyThread
@TestApi
@NonNull
- public static Source createSource(Resources res, InputStream is, int density) {
+ public static Source createSource(Resources res, @NonNull InputStream is, int density) {
return new InputStreamSource(res, is, density);
}
diff --git a/graphics/java/android/graphics/RuntimeShader.java b/graphics/java/android/graphics/RuntimeShader.java
index 57046f5..2ff888b 100644
--- a/graphics/java/android/graphics/RuntimeShader.java
+++ b/graphics/java/android/graphics/RuntimeShader.java
@@ -34,8 +34,6 @@
RuntimeShader.class.getClassLoader(), nativeGetFinalizer());
}
- private boolean mForceOpaque;
-
/**
* Current native shader builder instance.
*/
@@ -47,33 +45,17 @@
* @param shader The text of AGSL shader program to run.
*/
public RuntimeShader(@NonNull String shader) {
- this(shader, false);
- }
-
- /**
- * Creates a new RuntimeShader.
- *
- * @param shader The text of AGSL shader program to run.
- * @param forceOpaque If true then all pixels produced by the AGSL shader program will have an
- * alpha of 1.0f.
- */
- public RuntimeShader(@NonNull String shader, boolean forceOpaque) {
// colorspace is required, but the RuntimeShader always produces colors in the destination
// buffer's colorspace regardless of the value specified here.
super(ColorSpace.get(ColorSpace.Named.SRGB));
if (shader == null) {
throw new NullPointerException("RuntimeShader requires a non-null AGSL string");
}
- mForceOpaque = forceOpaque;
mNativeInstanceRuntimeShaderBuilder = nativeCreateBuilder(shader);
NoImagePreloadHolder.sRegistry.registerNativeAllocation(
this, mNativeInstanceRuntimeShaderBuilder);
}
- public boolean isForceOpaque() {
- return mForceOpaque;
- }
-
/**
* Sets the uniform color value corresponding to this shader. If the shader does not have a
* uniform with that name or if the uniform is declared with a type other than vec3 or vec4 and
@@ -322,7 +304,7 @@
/** @hide */
@Override
protected long createNativeInstance(long nativeMatrix, boolean filterFromPaint) {
- return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix, mForceOpaque);
+ return nativeCreateShader(mNativeInstanceRuntimeShaderBuilder, nativeMatrix);
}
/** @hide */
@@ -332,8 +314,7 @@
private static native long nativeGetFinalizer();
private static native long nativeCreateBuilder(String agsl);
- private static native long nativeCreateShader(
- long shaderBuilder, long matrix, boolean isOpaque);
+ private static native long nativeCreateShader(long shaderBuilder, long matrix);
private static native void nativeUpdateUniforms(
long shaderBuilder, String uniformName, float[] uniforms, boolean isColor);
private static native void nativeUpdateUniforms(
diff --git a/graphics/java/android/graphics/drawable/Drawable.java b/graphics/java/android/graphics/drawable/Drawable.java
index 28b3b04..4972e92 100644
--- a/graphics/java/android/graphics/drawable/Drawable.java
+++ b/graphics/java/android/graphics/drawable/Drawable.java
@@ -288,8 +288,7 @@
*
* @return A copy of the drawable's bounds
*/
- @NonNull
- public final Rect copyBounds() {
+ public final @NonNull Rect copyBounds() {
return new Rect(mBounds);
}
@@ -308,8 +307,7 @@
* @see #copyBounds()
* @see #copyBounds(android.graphics.Rect)
*/
- @NonNull
- public final Rect getBounds() {
+ public final @NonNull Rect getBounds() {
if (mBounds == ZERO_BOUNDS_RECT) {
mBounds = new Rect();
}
@@ -327,8 +325,7 @@
*
* @return The dirty bounds of this drawable
*/
- @NonNull
- public Rect getDirtyBounds() {
+ public @NonNull Rect getDirtyBounds() {
return getBounds();
}
@@ -457,8 +454,7 @@
*
* @see #setCallback(android.graphics.drawable.Drawable.Callback)
*/
- @Nullable
- public Callback getCallback() {
+ public @Nullable Callback getCallback() {
return mCallback != null ? mCallback.get() : null;
}
@@ -569,8 +565,7 @@
* The default return value is 255 if the class does not override this method to return a value
* specific to its use of alpha.
*/
- @IntRange(from=0,to=255)
- public int getAlpha() {
+ public @IntRange(from=0,to=255) int getAlpha() {
return 0xFF;
}
@@ -999,7 +994,8 @@
*
* @see android.graphics.PixelFormat
*/
- @Deprecated public abstract @PixelFormat.Opacity int getOpacity();
+ @Deprecated
+ public abstract @PixelFormat.Opacity int getOpacity();
/**
* Return the appropriate opacity value for two source opacities. If
@@ -1059,7 +1055,7 @@
* if it looks the same and there is no need to redraw it since its
* last state.
*/
- protected boolean onStateChange(int[] state) {
+ protected boolean onStateChange(@NonNull int[] state) {
return false;
}
@@ -1078,7 +1074,7 @@
* Override this in your subclass to change appearance if you vary based on
* the bounds.
*/
- protected void onBoundsChange(Rect bounds) {
+ protected void onBoundsChange(@NonNull Rect bounds) {
// Stub method.
}
@@ -1209,7 +1205,8 @@
/**
* Create a drawable from an inputstream
*/
- public static Drawable createFromStream(InputStream is, String srcName) {
+ public static @Nullable Drawable createFromStream(@Nullable InputStream is,
+ @Nullable String srcName) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
return createFromResourceStream(null, null, is, srcName);
@@ -1222,8 +1219,8 @@
* Create a drawable from an inputstream, using the given resources and
* value to determine density information.
*/
- public static Drawable createFromResourceStream(Resources res, TypedValue value,
- InputStream is, String srcName) {
+ public static @Nullable Drawable createFromResourceStream(@Nullable Resources res,
+ @Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName) {
Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, srcName != null ? srcName : "Unknown drawable");
try {
return createFromResourceStream(res, value, is, srcName, null);
@@ -1238,8 +1235,7 @@
*
* @deprecated Prefer the version without an Options object.
*/
- @Nullable
- public static Drawable createFromResourceStream(@Nullable Resources res,
+ public static @Nullable Drawable createFromResourceStream(@Nullable Resources res,
@Nullable TypedValue value, @Nullable InputStream is, @Nullable String srcName,
@Nullable BitmapFactory.Options opts) {
if (is == null) {
@@ -1281,7 +1277,8 @@
return null;
}
- private static Drawable getBitmapDrawable(Resources res, TypedValue value, InputStream is) {
+ private static Drawable getBitmapDrawable(Resources res, @Nullable TypedValue value,
+ @NonNull InputStream is) {
try {
ImageDecoder.Source source = null;
if (value != null) {
@@ -1369,9 +1366,9 @@
* a tag in an XML document, tries to create a Drawable from that tag.
* Returns null if the tag is not a valid drawable.
*/
- @NonNull
- public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
- @NonNull AttributeSet attrs) throws XmlPullParserException, IOException {
+ public static @NonNull Drawable createFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs)
+ throws XmlPullParserException, IOException {
return createFromXmlInner(r, parser, attrs, null);
}
@@ -1381,9 +1378,8 @@
* document, tries to create a Drawable from that tag. Returns {@code null}
* if the tag is not a valid drawable.
*/
- @NonNull
- public static Drawable createFromXmlInner(@NonNull Resources r, @NonNull XmlPullParser parser,
- @NonNull AttributeSet attrs, @Nullable Theme theme)
+ public static @NonNull Drawable createFromXmlInner(@NonNull Resources r,
+ @NonNull XmlPullParser parser, @NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
return createFromXmlInnerForDensity(r, parser, attrs, 0, theme);
}
@@ -1392,8 +1388,7 @@
* Version of {@link #createFromXmlInner(Resources, XmlPullParser, AttributeSet, Theme)} that
* accepts an override density.
*/
- @NonNull
- static Drawable createFromXmlInnerForDensity(@NonNull Resources r,
+ static @NonNull Drawable createFromXmlInnerForDensity(@NonNull Resources r,
@NonNull XmlPullParser parser, @NonNull AttributeSet attrs, int density,
@Nullable Theme theme) throws XmlPullParserException, IOException {
return r.getDrawableInflater().inflateFromXmlForDensity(parser.getName(), parser, attrs,
@@ -1403,8 +1398,7 @@
/**
* Create a drawable from file path name.
*/
- @Nullable
- public static Drawable createFromPath(String pathName) {
+ public static @Nullable Drawable createFromPath(String pathName) {
if (pathName == null) {
return null;
}
diff --git a/graphics/java/android/graphics/drawable/DrawableInflater.java b/graphics/java/android/graphics/drawable/DrawableInflater.java
index 66752a2..8debe26 100644
--- a/graphics/java/android/graphics/drawable/DrawableInflater.java
+++ b/graphics/java/android/graphics/drawable/DrawableInflater.java
@@ -61,8 +61,7 @@
* @param id the identifier of the drawable resource
* @return a drawable, or {@code null} if the drawable failed to load
*/
- @Nullable
- public static Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) {
+ public static @Nullable Drawable loadDrawable(@NonNull Context context, @DrawableRes int id) {
return loadDrawable(context.getResources(), context.getTheme(), id);
}
@@ -74,9 +73,8 @@
* @param id the identifier of the drawable resource
* @return a drawable, or {@code null} if the drawable failed to load
*/
- @Nullable
- public static Drawable loadDrawable(
- @NonNull Resources resources, @Nullable Theme theme, @DrawableRes int id) {
+ public static @Nullable Drawable loadDrawable(@NonNull Resources resources,
+ @Nullable Theme theme, @DrawableRes int id) {
return resources.getDrawable(id, theme);
}
@@ -111,8 +109,7 @@
* @throws XmlPullParserException
* @throws IOException
*/
- @NonNull
- public Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser,
+ public @NonNull Drawable inflateFromXml(@NonNull String name, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, @Nullable Theme theme)
throws XmlPullParserException, IOException {
return inflateFromXmlForDensity(name, parser, attrs, 0, theme);
@@ -122,8 +119,7 @@
* Version of {@link #inflateFromXml(String, XmlPullParser, AttributeSet, Theme)} that accepts
* an override density.
*/
- @NonNull
- Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser,
+ @NonNull Drawable inflateFromXmlForDensity(@NonNull String name, @NonNull XmlPullParser parser,
@NonNull AttributeSet attrs, int density, @Nullable Theme theme)
throws XmlPullParserException, IOException {
// Inner classes must be referenced as Outer$Inner, but XML tag names
@@ -146,9 +142,8 @@
return drawable;
}
- @NonNull
@SuppressWarnings("deprecation")
- private Drawable inflateFromTag(@NonNull String name) {
+ private @Nullable Drawable inflateFromTag(@NonNull String name) {
switch (name) {
case "selector":
return new StateListDrawable();
@@ -195,8 +190,7 @@
}
}
- @NonNull
- private Drawable inflateFromClass(@NonNull String className) {
+ private @NonNull Drawable inflateFromClass(@NonNull String className) {
try {
Constructor<? extends Drawable> constructor;
synchronized (CONSTRUCTOR_MAP) {
diff --git a/graphics/java/android/graphics/drawable/DrawableWrapper.java b/graphics/java/android/graphics/drawable/DrawableWrapper.java
index ebde757..a63d7f6 100644
--- a/graphics/java/android/graphics/drawable/DrawableWrapper.java
+++ b/graphics/java/android/graphics/drawable/DrawableWrapper.java
@@ -352,7 +352,7 @@
}
@Override
- protected boolean onStateChange(int[] state) {
+ protected boolean onStateChange(@NonNull int[] state) {
if (mDrawable != null && mDrawable.isStateful()) {
final boolean changed = mDrawable.setState(state);
if (changed) {
diff --git a/graphics/java/android/graphics/drawable/Icon.java b/graphics/java/android/graphics/drawable/Icon.java
index a03931b..b04b826 100644
--- a/graphics/java/android/graphics/drawable/Icon.java
+++ b/graphics/java/android/graphics/drawable/Icon.java
@@ -302,7 +302,7 @@
* is available. The {@link android.os.Message#obj obj}
* property is populated with the Drawable.
*/
- public void loadDrawableAsync(Context context, Message andThen) {
+ public void loadDrawableAsync(@NonNull Context context, @NonNull Message andThen) {
if (andThen.getTarget() == null) {
throw new IllegalArgumentException("callback message must have a target handler");
}
@@ -320,7 +320,7 @@
* {@link #loadDrawable(Context)} finished
* @param handler {@link Handler} on which to notify the {@code listener}
*/
- public void loadDrawableAsync(Context context, final OnDrawableLoadedListener listener,
+ public void loadDrawableAsync(@NonNull Context context, final OnDrawableLoadedListener listener,
Handler handler) {
new LoadDrawableTask(context, handler, listener).runAsync();
}
@@ -335,7 +335,7 @@
* to access {@link android.content.res.Resources Resources}, for example.
* @return A fresh instance of a drawable for this image, yours to keep.
*/
- public Drawable loadDrawable(Context context) {
+ public @Nullable Drawable loadDrawable(Context context) {
final Drawable result = loadDrawableInner(context);
if (result != null && hasTint()) {
result.mutate();
@@ -415,7 +415,7 @@
return null;
}
- private InputStream getUriInputStream(Context context) {
+ private @Nullable InputStream getUriInputStream(Context context) {
final Uri uri = getUri();
final String scheme = uri.getScheme();
if (ContentResolver.SCHEME_CONTENT.equals(scheme)
@@ -496,7 +496,7 @@
* @param stream The stream on which to serialize the Icon.
* @hide
*/
- public void writeToStream(OutputStream stream) throws IOException {
+ public void writeToStream(@NonNull OutputStream stream) throws IOException {
DataOutputStream dataStream = new DataOutputStream(stream);
dataStream.writeInt(VERSION_STREAM_SERIALIZER);
@@ -532,7 +532,7 @@
* @param stream The input stream from which to reconstruct the Icon.
* @hide
*/
- public static Icon createFromStream(InputStream stream) throws IOException {
+ public static @Nullable Icon createFromStream(@NonNull InputStream stream) throws IOException {
DataInputStream inputStream = new DataInputStream(stream);
final int version = inputStream.readInt();
@@ -571,7 +571,7 @@
* @return whether this icon is the same as the another one
* @hide
*/
- public boolean sameAs(Icon otherIcon) {
+ public boolean sameAs(@NonNull Icon otherIcon) {
if (otherIcon == this) {
return true;
}
@@ -602,7 +602,7 @@
* given resource ID.
* @param resId ID of the drawable resource
*/
- public static Icon createWithResource(Context context, @DrawableRes int resId) {
+ public static @NonNull Icon createWithResource(Context context, @DrawableRes int resId) {
if (context == null) {
throw new IllegalArgumentException("Context must not be null.");
}
@@ -617,7 +617,7 @@
* @hide
*/
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- public static Icon createWithResource(Resources res, @DrawableRes int resId) {
+ public static @NonNull Icon createWithResource(Resources res, @DrawableRes int resId) {
if (res == null) {
throw new IllegalArgumentException("Resource must not be null.");
}
@@ -632,7 +632,7 @@
* @param resPackage Name of the package containing the resource in question
* @param resId ID of the drawable resource
*/
- public static Icon createWithResource(String resPackage, @DrawableRes int resId) {
+ public static @NonNull Icon createWithResource(String resPackage, @DrawableRes int resId) {
if (resPackage == null) {
throw new IllegalArgumentException("Resource package name must not be null.");
}
@@ -646,7 +646,7 @@
* Create an Icon pointing to a bitmap in memory.
* @param bits A valid {@link android.graphics.Bitmap} object
*/
- public static Icon createWithBitmap(Bitmap bits) {
+ public static @NonNull Icon createWithBitmap(Bitmap bits) {
if (bits == null) {
throw new IllegalArgumentException("Bitmap must not be null.");
}
@@ -660,7 +660,7 @@
* by {@link AdaptiveIconDrawable}.
* @param bits A valid {@link android.graphics.Bitmap} object
*/
- public static Icon createWithAdaptiveBitmap(Bitmap bits) {
+ public static @NonNull Icon createWithAdaptiveBitmap(Bitmap bits) {
if (bits == null) {
throw new IllegalArgumentException("Bitmap must not be null.");
}
@@ -677,7 +677,7 @@
* @param offset Offset into <code>data</code> at which the bitmap data starts
* @param length Length of the bitmap data
*/
- public static Icon createWithData(byte[] data, int offset, int length) {
+ public static @NonNull Icon createWithData(byte[] data, int offset, int length) {
if (data == null) {
throw new IllegalArgumentException("Data must not be null.");
}
@@ -693,7 +693,7 @@
*
* @param uri A uri referring to local content:// or file:// image data.
*/
- public static Icon createWithContentUri(String uri) {
+ public static @NonNull Icon createWithContentUri(String uri) {
if (uri == null) {
throw new IllegalArgumentException("Uri must not be null.");
}
@@ -707,7 +707,7 @@
*
* @param uri A uri referring to local content:// or file:// image data.
*/
- public static Icon createWithContentUri(Uri uri) {
+ public static @NonNull Icon createWithContentUri(Uri uri) {
if (uri == null) {
throw new IllegalArgumentException("Uri must not be null.");
}
@@ -720,8 +720,7 @@
*
* @param uri A uri referring to local content:// or file:// image data.
*/
- @NonNull
- public static Icon createWithAdaptiveBitmapContentUri(@NonNull String uri) {
+ public static @NonNull Icon createWithAdaptiveBitmapContentUri(@NonNull String uri) {
if (uri == null) {
throw new IllegalArgumentException("Uri must not be null.");
}
@@ -750,7 +749,7 @@
* @param tint a color, as in {@link Drawable#setTint(int)}
* @return this same object, for use in chained construction
*/
- public Icon setTint(@ColorInt int tint) {
+ public @NonNull Icon setTint(@ColorInt int tint) {
return setTintList(ColorStateList.valueOf(tint));
}
@@ -760,7 +759,7 @@
* @param tintList as in {@link Drawable#setTintList(ColorStateList)}, null to remove tint
* @return this same object, for use in chained construction
*/
- public Icon setTintList(ColorStateList tintList) {
+ public @NonNull Icon setTintList(ColorStateList tintList) {
mTintList = tintList;
return this;
}
@@ -809,7 +808,7 @@
* @param path A path to a file that contains compressed bitmap data of
* a type that {@link android.graphics.BitmapFactory} can decode.
*/
- public static Icon createWithFilePath(String path) {
+ public static @NonNull Icon createWithFilePath(String path) {
if (path == null) {
throw new IllegalArgumentException("Path must not be null.");
}
diff --git a/graphics/java/android/graphics/drawable/RippleShader.java b/graphics/java/android/graphics/drawable/RippleShader.java
index 53a6731..4461f39 100644
--- a/graphics/java/android/graphics/drawable/RippleShader.java
+++ b/graphics/java/android/graphics/drawable/RippleShader.java
@@ -126,7 +126,7 @@
private static final double PI_ROTATE_LEFT = Math.PI * -0.0078125;
RippleShader() {
- super(SHADER, false);
+ super(SHADER);
}
public void setShader(Shader shader) {
diff --git a/identity/java/android/security/identity/IdentityCredential.java b/identity/java/android/security/identity/IdentityCredential.java
index cdf746f..f440b69 100644
--- a/identity/java/android/security/identity/IdentityCredential.java
+++ b/identity/java/android/security/identity/IdentityCredential.java
@@ -454,7 +454,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] proveOwnership(@NonNull byte[] challenge) {
@@ -485,7 +486,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* generated CBOR and enables the issuing authority to verify that the
- * returned proof is fresh.
+ * returned proof is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the COSE_Sign1 data structure above
*/
public @NonNull byte[] delete(@NonNull byte[] challenge) {
diff --git a/identity/java/android/security/identity/WritableIdentityCredential.java b/identity/java/android/security/identity/WritableIdentityCredential.java
index 305d0ea..6d56964 100644
--- a/identity/java/android/security/identity/WritableIdentityCredential.java
+++ b/identity/java/android/security/identity/WritableIdentityCredential.java
@@ -59,7 +59,8 @@
* @param challenge is a non-empty byte array whose contents should be unique, fresh and
* provided by the issuing authority. The value provided is embedded in the
* attestation extension and enables the issuing authority to verify that the
- * attestation certificate is fresh.
+ * attestation certificate is fresh. Implementations are required to support
+ * challenges at least 32 bytes of length.
* @return the X.509 certificate for this credential's CredentialKey.
*/
public abstract @NonNull Collection<X509Certificate> getCredentialKeyCertificateChain(
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/back/BackAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
index b310dd6..9a6df23 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimation.java
@@ -18,9 +18,12 @@
import android.view.MotionEvent;
+import com.android.wm.shell.common.annotations.ExternalThread;
+
/**
- * Interface for SysUI to get access to the Back animation related methods.
+ * Interface for external process to get access to the Back animation related methods.
*/
+@ExternalThread
public interface BackAnimation {
/**
@@ -32,4 +35,11 @@
* Sets whether the back gesture is past the trigger threshold or not.
*/
void setTriggerBack(boolean triggerBack);
+
+ /**
+ * Returns a binder that can be passed to an external process to update back animations.
+ */
+ default IBackAnimation createExternalInterface() {
+ return null;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
index 229e8ee0..a5140c3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/BackAnimationController.java
@@ -16,6 +16,7 @@
package com.android.wm.shell.back;
+import static com.android.wm.shell.common.ExecutorUtils.executeRemoteCallWithTaskPermission;
import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BACK_PREVIEW;
import android.animation.Animator;
@@ -26,6 +27,7 @@
import android.app.ActivityTaskManager;
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.HardwareBuffer;
@@ -35,16 +37,18 @@
import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.window.BackNavigationInfo;
+import android.window.IOnBackInvokedCallback;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.protolog.common.ProtoLog;
+import com.android.wm.shell.common.RemoteCallable;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.annotations.ShellMainThread;
/**
* Controls the window animation run when a user initiates a back gesture.
*/
-public class BackAnimationController {
+public class BackAnimationController implements RemoteCallable<BackAnimationController> {
private static final String BACK_PREDICTABILITY_PROP = "persist.debug.back_predictability";
public static final boolean IS_ENABLED = SystemProperties
@@ -72,18 +76,26 @@
private BackNavigationInfo mBackNavigationInfo;
private final SurfaceControl.Transaction mTransaction;
private final IActivityTaskManager mActivityTaskManager;
+ private final Context mContext;
+ @Nullable
+ private IOnBackInvokedCallback mBackToLauncherCallback;
- public BackAnimationController(@ShellMainThread ShellExecutor shellExecutor) {
- this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService());
+ public BackAnimationController(
+ @ShellMainThread ShellExecutor shellExecutor,
+ Context context) {
+ this(shellExecutor, new SurfaceControl.Transaction(), ActivityTaskManager.getService(),
+ context);
}
@VisibleForTesting
BackAnimationController(@NonNull ShellExecutor shellExecutor,
@NonNull SurfaceControl.Transaction transaction,
- @NonNull IActivityTaskManager activityTaskManager) {
+ @NonNull IActivityTaskManager activityTaskManager,
+ Context context) {
mShellExecutor = shellExecutor;
mTransaction = transaction;
mActivityTaskManager = activityTaskManager;
+ mContext = context;
}
public BackAnimation getBackAnimationImpl() {
@@ -92,7 +104,27 @@
private final BackAnimation mBackAnimation = new BackAnimationImpl();
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public ShellExecutor getRemoteCallExecutor() {
+ return mShellExecutor;
+ }
+
private class BackAnimationImpl implements BackAnimation {
+ private IBackAnimationImpl mBackAnimation;
+
+ @Override
+ public IBackAnimation createExternalInterface() {
+ if (mBackAnimation != null) {
+ mBackAnimation.invalidate();
+ }
+ mBackAnimation = new IBackAnimationImpl(BackAnimationController.this);
+ return mBackAnimation;
+ }
@Override
public void onBackMotion(MotionEvent event) {
@@ -105,6 +137,48 @@
}
}
+ private static class IBackAnimationImpl extends IBackAnimation.Stub {
+ private BackAnimationController mController;
+
+ IBackAnimationImpl(BackAnimationController controller) {
+ mController = controller;
+ }
+
+ @Override
+ public void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ executeRemoteCallWithTaskPermission(mController, "setBackToLauncherCallback",
+ (controller) -> mController.setBackToLauncherCallback(callback));
+ }
+
+ @Override
+ public void clearBackToLauncherCallback() {
+ executeRemoteCallWithTaskPermission(mController, "clearBackToLauncherCallback",
+ (controller) -> mController.clearBackToLauncherCallback());
+ }
+
+ @Override
+ public void onBackToLauncherAnimationFinished() {
+ executeRemoteCallWithTaskPermission(mController, "onBackToLauncherAnimationFinished",
+ (controller) -> mController.onBackToLauncherAnimationFinished());
+ }
+
+ void invalidate() {
+ mController = null;
+ }
+ }
+
+ private void setBackToLauncherCallback(IOnBackInvokedCallback callback) {
+ mBackToLauncherCallback = callback;
+ }
+
+ private void clearBackToLauncherCallback() {
+ mBackToLauncherCallback = null;
+ }
+
+ private void onBackToLauncherAnimationFinished() {
+ finishAnimation();
+ }
+
/**
* Called when a new motion event needs to be transferred to this
* {@link BackAnimationController}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
new file mode 100644
index 0000000..6311f87
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/back/IBackAnimation.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.back;
+
+import android.window.IOnBackInvokedCallback;
+
+/**
+ * Interface for Launcher process to register back invocation callbacks.
+ */
+interface IBackAnimation {
+
+ /**
+ * Sets a {@link IOnBackInvokedCallback} to be invoked when
+ * back navigation has type {@link BackNavigationInfo#TYPE_RETURN_TO_HOME}.
+ */
+ void setBackToLauncherCallback(in IOnBackInvokedCallback callback);
+
+ /**
+ * Clears the previously registered {@link IOnBackInvokedCallback}.
+ */
+ void clearBackToLauncherCallback();
+
+ /**
+ * Notifies Shell that the back to launcher animation has fully finished
+ * (including the transition animation that runs after the finger is lifted).
+ *
+ * At this point the top window leash (if one was created) should be ready to be released.
+ * //TODO: Remove once we play the transition animation through shell transitions.
+ */
+ void onBackToLauncherAnimationFinished();
+}
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/CompatUIController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
index 8f4cfb0..4d279bc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIController.java
@@ -49,7 +49,7 @@
import java.util.function.Predicate;
/**
- * Controls to show/update restart-activity buttons on Tasks based on whether the foreground
+ * Controller to show/update compat UI components on Tasks based on whether the foreground
* activities are in compatibility mode.
*/
public class CompatUIController implements OnDisplaysChangedListener,
@@ -228,8 +228,7 @@
final CompatUIWindowManager compatUIWindowManager =
createLayout(context, taskInfo, taskListener);
mActiveLayouts.put(taskInfo.taskId, compatUIWindowManager);
- compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId),
- taskInfo.topActivityInSizeCompat, taskInfo.cameraCompatControlState);
+ compatUIWindowManager.createLayout(showOnDisplay(taskInfo.displayId), taskInfo);
}
@VisibleForTesting
@@ -254,9 +253,7 @@
if (layout == null) {
return;
}
- layout.updateCompatInfo(taskInfo.configuration, taskListener,
- showOnDisplay(layout.getDisplayId()), taskInfo.topActivityInSizeCompat,
- taskInfo.cameraCompatControlState);
+ layout.updateCompatInfo(taskInfo, taskListener, showOnDisplay(layout.getDisplayId()));
}
private void removeLayout(int taskId) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
index 44526b0..9c001a3 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManager.java
@@ -20,28 +20,16 @@
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_HIDDEN;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED;
import static android.app.TaskInfo.CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
import android.annotation.Nullable;
+import android.app.TaskInfo;
import android.app.TaskInfo.CameraCompatControlState;
import android.content.Context;
import android.content.res.Configuration;
-import android.graphics.PixelFormat;
import android.graphics.Rect;
-import android.os.Binder;
import android.util.Log;
-import android.view.IWindow;
import android.view.LayoutInflater;
-import android.view.SurfaceControl;
-import android.view.SurfaceControlViewHost;
-import android.view.SurfaceSession;
import android.view.View;
-import android.view.WindowManager;
-import android.view.WindowlessWindowManager;
import com.android.internal.annotations.VisibleForTesting;
import com.android.wm.shell.R;
@@ -50,26 +38,19 @@
import com.android.wm.shell.common.SyncTransactionQueue;
/**
- * Holds view hierarchy of a root surface and helps to inflate and manage layout for compat
- * controls.
+ * Window manager for the Size Compat restart button and Camera Compat control.
*/
-class CompatUIWindowManager extends WindowlessWindowManager {
+class CompatUIWindowManager extends CompatUIWindowManagerAbstract {
- private static final String TAG = "CompatUIWindowManager";
+ /**
+ * The Compat UI should be the topmost child of the Task in case there can be more than one
+ * child.
+ */
+ private static final int Z_ORDER = Integer.MAX_VALUE;
- private final SyncTransactionQueue mSyncQueue;
private final CompatUIController.CompatUICallback mCallback;
- private final int mDisplayId;
- private final int mTaskId;
- private final Rect mStableBounds;
- private Context mContext;
- private Configuration mTaskConfig;
- private ShellTaskOrganizer.TaskListener mTaskListener;
- private DisplayLayout mDisplayLayout;
-
- // Remember the last reported states in case visibility changes due to keyguard or
- // IME updates.
+ // Remember the last reported states in case visibility changes due to keyguard or IME updates.
@VisibleForTesting
boolean mHasSizeCompat;
@CameraCompatControlState
@@ -82,147 +63,83 @@
@Nullable
@VisibleForTesting
- CompatUILayout mCompatUILayout;
-
- @Nullable
- private SurfaceControlViewHost mViewHost;
- @Nullable
- private SurfaceControl mLeash;
+ CompatUILayout mLayout;
CompatUIWindowManager(Context context, Configuration taskConfig,
SyncTransactionQueue syncQueue, CompatUIController.CompatUICallback callback,
int taskId, ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout,
- boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
- super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
- mContext = context;
- mSyncQueue = syncQueue;
+ boolean hasShownSizeCompatHint, boolean hasShownCameraCompatHint) {
+ super(context, taskConfig, syncQueue, taskId, taskListener, displayLayout);
mCallback = callback;
- mTaskConfig = taskConfig;
- mDisplayId = mContext.getDisplayId();
- mTaskId = taskId;
- mTaskListener = taskListener;
- mDisplayLayout = displayLayout;
mShouldShowSizeCompatHint = !hasShownSizeCompatHint;
mShouldShowCameraCompatHint = !hasShownCameraCompatHint;
- mStableBounds = new Rect();
- mDisplayLayout.getStableBounds(mStableBounds);
}
@Override
- public void setConfiguration(Configuration configuration) {
- super.setConfiguration(configuration);
- mContext = mContext.createConfigurationContext(configuration);
+ protected int getZOrder() {
+ return Z_ORDER;
+ }
+
+
+ @Override
+ protected @Nullable View getLayout() {
+ return mLayout;
}
@Override
- protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
- // Can't set position for the ViewRootImpl SC directly. Create a leash to manipulate later.
- final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
- .setContainerLayer()
- .setName("CompatUILeash")
- .setHidden(false)
- .setCallsite("CompatUIWindowManager#attachToParentSurface");
- attachToParentSurface(builder);
- mLeash = builder.build();
- b.setParent(mLeash);
+ protected void removeLayout() {
+ mLayout = null;
}
- /** Creates the layout for compat controls. */
- void createLayout(boolean show, boolean hasSizeCompat,
- @CameraCompatControlState int cameraCompatControlState) {
- mHasSizeCompat = hasSizeCompat;
- mCameraCompatControlState = cameraCompatControlState;
- if (!show || mCompatUILayout != null) {
- // Wait until compat controls should be visible.
- return;
- }
+ @Override
+ protected boolean eligibleToShowLayout() {
+ return mHasSizeCompat || shouldShowCameraControl();
+ }
- initCompatUi();
- updateSurfacePosition();
+ /**
+ * Updates the internal state with respect to {@code taskInfo} and calls {@link
+ * #createLayout(boolean)}.
+ */
+ void createLayout(boolean canShow, TaskInfo taskInfo) {
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
+ createLayout(canShow);
+ }
- if (hasSizeCompat) {
+ @Override
+ protected View createLayout() {
+ mLayout = inflateLayout();
+ mLayout.inject(this);
+
+ updateVisibilityOfViews();
+
+ if (mHasSizeCompat) {
mCallback.onSizeCompatRestartButtonAppeared(mTaskId);
}
+
+ return mLayout;
}
- private void createLayout(boolean show) {
- createLayout(show, mHasSizeCompat, mCameraCompatControlState);
+ @VisibleForTesting
+ CompatUILayout inflateLayout() {
+ return (CompatUILayout) LayoutInflater.from(mContext).inflate(R.layout.compat_ui_layout,
+ null);
}
- /** Called when compat info changed. */
- void updateCompatInfo(Configuration taskConfig,
- ShellTaskOrganizer.TaskListener taskListener, boolean show, boolean hasSizeCompat,
- @CameraCompatControlState int cameraCompatControlState) {
- final Configuration prevTaskConfig = mTaskConfig;
- final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
- mTaskConfig = taskConfig;
- mTaskListener = taskListener;
+ @Override
+ public void updateCompatInfo(TaskInfo taskInfo, ShellTaskOrganizer.TaskListener taskListener,
+ boolean canShow) {
final boolean prevHasSizeCompat = mHasSizeCompat;
final int prevCameraCompatControlState = mCameraCompatControlState;
- mHasSizeCompat = hasSizeCompat;
- mCameraCompatControlState = cameraCompatControlState;
+ mHasSizeCompat = taskInfo.topActivityInSizeCompat;
+ mCameraCompatControlState = taskInfo.cameraCompatControlState;
- // Update configuration.
- mContext = mContext.createConfigurationContext(taskConfig);
- setConfiguration(taskConfig);
-
- if (mCompatUILayout == null || prevTaskListener != taskListener) {
- // TaskListener changed, recreate the layout for new surface parent.
- release();
- createLayout(show);
- return;
- }
+ super.updateCompatInfo(taskInfo, taskListener, canShow);
if (prevHasSizeCompat != mHasSizeCompat
|| prevCameraCompatControlState != mCameraCompatControlState) {
updateVisibilityOfViews();
}
-
- if (!taskConfig.windowConfiguration.getBounds()
- .equals(prevTaskConfig.windowConfiguration.getBounds())) {
- // Reposition the UI surfaces.
- updateSurfacePosition();
- }
-
- if (taskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection()) {
- // Update layout for RTL.
- mCompatUILayout.setLayoutDirection(taskConfig.getLayoutDirection());
- updateSurfacePosition();
- }
-
- }
-
- /** Called when the visibility of the UI should change. */
- void updateVisibility(boolean show) {
- if (mCompatUILayout == null) {
- // Layout may not have been created because it was hidden previously.
- createLayout(show);
- return;
- }
-
- // Hide compat UIs when IME is showing.
- final int newVisibility = show ? View.VISIBLE : View.GONE;
- if (mCompatUILayout.getVisibility() != newVisibility) {
- mCompatUILayout.setVisibility(newVisibility);
- }
- }
-
- /** Called when display layout changed. */
- void updateDisplayLayout(DisplayLayout displayLayout) {
- final Rect prevStableBounds = mStableBounds;
- final Rect curStableBounds = new Rect();
- displayLayout.getStableBounds(curStableBounds);
- mDisplayLayout = displayLayout;
- if (!prevStableBounds.equals(curStableBounds)) {
- // Stable bounds changed, update UI surface positions.
- updateSurfacePosition();
- mStableBounds.set(curStableBounds);
- }
- }
-
- /** Called when it is ready to be placed compat UI surface. */
- void attachToParentSurface(SurfaceControl.Builder b) {
- mTaskListener.attachChildSurfaceToTask(mTaskId, b);
}
/** Called when the restart button is clicked. */
@@ -233,7 +150,7 @@
/** Called when the camera treatment button is clicked. */
void onCameraTreatmentButtonClicked() {
if (!shouldShowCameraControl()) {
- Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
// When a camera control is shown, only two states are allowed: "treament applied" and
@@ -244,141 +161,72 @@
? CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED
: CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED;
mCallback.onCameraControlStateUpdated(mTaskId, mCameraCompatControlState);
- mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
/** Called when the camera dismiss button is clicked. */
void onCameraDismissButtonClicked() {
if (!shouldShowCameraControl()) {
- Log.w(TAG, "Camera compat shouldn't receive clicks in the hidden state.");
+ Log.w(getTag(), "Camera compat shouldn't receive clicks in the hidden state.");
return;
}
mCameraCompatControlState = CAMERA_COMPAT_CONTROL_DISMISSED;
mCallback.onCameraControlStateUpdated(mTaskId, CAMERA_COMPAT_CONTROL_DISMISSED);
- mCompatUILayout.setCameraControlVisibility(/* show= */ false);
+ mLayout.setCameraControlVisibility(/* show= */ false);
}
/** Called when the restart button is long clicked. */
void onRestartButtonLongClicked() {
- if (mCompatUILayout == null) {
+ if (mLayout == null) {
return;
}
- mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
}
/** Called when either dismiss or treatment camera buttons is long clicked. */
void onCameraButtonLongClicked() {
- if (mCompatUILayout == null) {
+ if (mLayout == null) {
return;
}
- mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
}
- int getDisplayId() {
- return mDisplayId;
- }
-
- int getTaskId() {
- return mTaskId;
- }
-
- /** Releases the surface control and tears down the view hierarchy. */
- void release() {
- // Hiding before releasing to avoid flickering when transitioning to the Home screen.
- mCompatUILayout.setVisibility(View.GONE);
- mCompatUILayout = null;
-
- if (mViewHost != null) {
- mViewHost.release();
- mViewHost = null;
- }
-
- if (mLeash != null) {
- final SurfaceControl leash = mLeash;
- mSyncQueue.runInSync(t -> t.remove(leash));
- mLeash = null;
- }
- }
-
- void relayout() {
- mViewHost.relayout(getWindowLayoutParams());
- updateSurfacePosition();
- }
-
- @VisibleForTesting
- void updateSurfacePosition() {
- if (mCompatUILayout == null || mLeash == null) {
+ @Override
+ protected void updateSurfacePosition(Rect taskBounds, Rect stableBounds) {
+ if (mLayout == null) {
return;
}
-
- // Use stable bounds to prevent controls from overlapping with system bars.
- final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
- final Rect stableBounds = new Rect();
- mDisplayLayout.getStableBounds(stableBounds);
- stableBounds.intersect(taskBounds);
-
// Position of the button in the container coordinate.
final int positionX = getLayoutDirection() == View.LAYOUT_DIRECTION_RTL
? stableBounds.left - taskBounds.left
- : stableBounds.right - taskBounds.left - mCompatUILayout.getMeasuredWidth();
+ : stableBounds.right - taskBounds.left - mLayout.getMeasuredWidth();
final int positionY = stableBounds.bottom - taskBounds.top
- - mCompatUILayout.getMeasuredHeight();
+ - mLayout.getMeasuredHeight();
updateSurfacePosition(positionX, positionY);
}
- private int getLayoutDirection() {
- return mContext.getResources().getConfiguration().getLayoutDirection();
- }
-
- private void updateSurfacePosition(int positionX, int positionY) {
- mSyncQueue.runInSync(t -> {
- if (mLeash == null || !mLeash.isValid()) {
- Log.w(TAG, "The leash has been released.");
- return;
- }
- t.setPosition(mLeash, positionX, positionY);
- // The compat UI should be the topmost child of the Task in case there can be more
- // than one children.
- t.setLayer(mLeash, Integer.MAX_VALUE);
- });
- }
-
- /** Inflates {@link CompatUILayout} on to the root surface. */
- private void initCompatUi() {
- if (mViewHost != null) {
- throw new IllegalStateException(
- "A UI has already been created with this window manager.");
- }
-
- // Construction extracted into the separate methods to allow injection for tests.
- mViewHost = createSurfaceViewHost();
- mCompatUILayout = inflateCompatUILayout();
- mCompatUILayout.inject(this);
-
- updateVisibilityOfViews();
-
- mViewHost.setView(mCompatUILayout, getWindowLayoutParams());
- }
-
private void updateVisibilityOfViews() {
+ if (mLayout == null) {
+ return;
+ }
// Size Compat mode restart button.
- mCompatUILayout.setRestartButtonVisibility(mHasSizeCompat);
+ mLayout.setRestartButtonVisibility(mHasSizeCompat);
if (mHasSizeCompat && mShouldShowSizeCompatHint) {
- mCompatUILayout.setSizeCompatHintVisibility(/* show= */ true);
+ mLayout.setSizeCompatHintVisibility(/* show= */ true);
// Only show by default for the first time.
mShouldShowSizeCompatHint = false;
}
// Camera control for stretched issues.
- mCompatUILayout.setCameraControlVisibility(shouldShowCameraControl());
+ mLayout.setCameraControlVisibility(shouldShowCameraControl());
if (shouldShowCameraControl() && mShouldShowCameraCompatHint) {
- mCompatUILayout.setCameraCompatHintVisibility(/* show= */ true);
+ mLayout.setCameraCompatHintVisibility(/* show= */ true);
// Only show by default for the first time.
mShouldShowCameraCompatHint = false;
}
if (shouldShowCameraControl()) {
- mCompatUILayout.updateCameraTreatmentButton(mCameraCompatControlState);
+ mLayout.updateCameraTreatmentButton(mCameraCompatControlState);
}
}
@@ -386,32 +234,4 @@
return mCameraCompatControlState != CAMERA_COMPAT_CONTROL_HIDDEN
&& mCameraCompatControlState != CAMERA_COMPAT_CONTROL_DISMISSED;
}
-
- @VisibleForTesting
- CompatUILayout inflateCompatUILayout() {
- return (CompatUILayout) LayoutInflater.from(mContext)
- .inflate(R.layout.compat_ui_layout, null);
- }
-
- @VisibleForTesting
- SurfaceControlViewHost createSurfaceViewHost() {
- return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
- }
-
- /** Gets the layout params. */
- private WindowManager.LayoutParams getWindowLayoutParams() {
- // Measure how big the hint is since its size depends on the text size.
- mCompatUILayout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
- final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
- // Cannot be wrap_content as this determines the actual window size
- mCompatUILayout.getMeasuredWidth(), mCompatUILayout.getMeasuredHeight(),
- TYPE_APPLICATION_OVERLAY,
- FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
- PixelFormat.TRANSLUCENT);
- winParams.token = new Binder();
- winParams.setTitle(CompatUILayout.class.getSimpleName() + mTaskId);
- winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
- return winParams;
- }
-
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
new file mode 100644
index 0000000..b9a9db1
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/compatui/CompatUIWindowManagerAbstract.java
@@ -0,0 +1,364 @@
+/*
+ * 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;
+
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+
+import android.annotation.Nullable;
+import android.app.TaskInfo;
+import android.content.Context;
+import android.content.res.Configuration;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.util.Log;
+import android.view.IWindow;
+import android.view.SurfaceControl;
+import android.view.SurfaceControlViewHost;
+import android.view.SurfaceSession;
+import android.view.View;
+import android.view.WindowManager;
+import android.view.WindowlessWindowManager;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.wm.shell.ShellTaskOrganizer;
+import com.android.wm.shell.common.DisplayLayout;
+import com.android.wm.shell.common.SyncTransactionQueue;
+
+/**
+ * A superclass for all Compat UI {@link WindowlessWindowManager}s that holds shared logic and
+ * exposes general API for {@link CompatUIController}.
+ *
+ * <p>Holds view hierarchy of a root surface and helps to inflate and manage layout.
+ */
+abstract class CompatUIWindowManagerAbstract extends WindowlessWindowManager {
+
+ protected final SyncTransactionQueue mSyncQueue;
+ protected final int mDisplayId;
+ protected final int mTaskId;
+
+ protected Context mContext;
+ protected Configuration mTaskConfig;
+ protected ShellTaskOrganizer.TaskListener mTaskListener;
+ protected DisplayLayout mDisplayLayout;
+ protected final Rect mStableBounds;
+
+ /**
+ * Utility class for adding and releasing a View hierarchy for this {@link
+ * WindowlessWindowManager} to {@code mLeash}.
+ */
+ @Nullable
+ protected SurfaceControlViewHost mViewHost;
+
+ /**
+ * A surface leash to position the layout relative to the task, since we can't set position for
+ * the {@code mViewHost} directly.
+ */
+ @Nullable
+ protected SurfaceControl mLeash;
+
+ protected CompatUIWindowManagerAbstract(Context context, Configuration taskConfig,
+ SyncTransactionQueue syncQueue, int taskId,
+ ShellTaskOrganizer.TaskListener taskListener, DisplayLayout displayLayout) {
+ super(taskConfig, null /* rootSurface */, null /* hostInputToken */);
+ mContext = context;
+ mSyncQueue = syncQueue;
+ mTaskConfig = taskConfig;
+ mDisplayId = mContext.getDisplayId();
+ mTaskId = taskId;
+ mTaskListener = taskListener;
+ mDisplayLayout = displayLayout;
+ mStableBounds = new Rect();
+ mDisplayLayout.getStableBounds(mStableBounds);
+ }
+
+ /**
+ * Returns the z-order of this window which will be passed to the {@link SurfaceControl} once
+ * {@link #attachToParentSurface} is called.
+ *
+ * <p>See {@link SurfaceControl.Transaction#setLayer}.
+ */
+ protected abstract int getZOrder();
+
+ /** Returns the layout of this window manager. */
+ protected abstract @Nullable View getLayout();
+
+ /**
+ * Inflates and inits the layout of this window manager on to the root surface if both {@code
+ * canShow} and {@link #eligibleToShowLayout} are true.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void createLayout(boolean canShow) {
+ if (!canShow || !eligibleToShowLayout() || getLayout() != null) {
+ // Wait until layout should be visible.
+ return;
+ }
+
+ if (mViewHost != null) {
+ throw new IllegalStateException(
+ "A UI has already been created with this window manager.");
+ }
+
+ // Construction extracted into separate methods to allow injection for tests.
+ mViewHost = createSurfaceViewHost();
+ mViewHost.setView(createLayout(), getWindowLayoutParams());
+
+ updateSurfacePosition();
+ }
+
+ /** Inflates and inits the layout of this window manager. */
+ protected abstract View createLayout();
+
+ protected abstract void removeLayout();
+
+ /**
+ * Whether the layout is eligible to be shown according to the internal state of the subclass.
+ * Returns true by default if subclass doesn't override this method.
+ */
+ protected boolean eligibleToShowLayout() {
+ return true;
+ }
+
+ @Override
+ public void setConfiguration(Configuration configuration) {
+ super.setConfiguration(configuration);
+ mContext = mContext.createConfigurationContext(configuration);
+ }
+
+ @Override
+ protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
+ String className = getClass().getSimpleName();
+ final SurfaceControl.Builder builder = new SurfaceControl.Builder(new SurfaceSession())
+ .setContainerLayer()
+ .setName(className + "Leash")
+ .setHidden(false)
+ .setCallsite(className + "#attachToParentSurface");
+ attachToParentSurface(builder);
+ mLeash = builder.build();
+ b.setParent(mLeash);
+
+ initSurface(mLeash);
+ }
+
+ /** Inits the z-order of the surface. */
+ private void initSurface(SurfaceControl leash) {
+ final int z = getZOrder();
+ mSyncQueue.runInSync(t -> {
+ if (leash == null || !leash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setLayer(leash, z);
+ });
+ }
+
+ /**
+ * Called when compat info changed.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateCompatInfo(TaskInfo taskInfo,
+ ShellTaskOrganizer.TaskListener taskListener, boolean canShow) {
+ final Configuration prevTaskConfig = mTaskConfig;
+ final ShellTaskOrganizer.TaskListener prevTaskListener = mTaskListener;
+ mTaskConfig = taskInfo.configuration;
+ mTaskListener = taskListener;
+
+ // Update configuration.
+ setConfiguration(mTaskConfig);
+
+ View layout = getLayout();
+ if (layout == null || prevTaskListener != taskListener) {
+ // TaskListener changed, recreate the layout for new surface parent.
+ release();
+ createLayout(canShow);
+ return;
+ }
+
+ boolean boundsUpdated = !mTaskConfig.windowConfiguration.getBounds().equals(
+ prevTaskConfig.windowConfiguration.getBounds());
+ boolean layoutDirectionUpdated =
+ mTaskConfig.getLayoutDirection() != prevTaskConfig.getLayoutDirection();
+ if (boundsUpdated || layoutDirectionUpdated) {
+ // Reposition the UI surfaces.
+ updateSurfacePosition();
+ }
+
+ if (layout != null && layoutDirectionUpdated) {
+ // Update layout for RTL.
+ layout.setLayoutDirection(mTaskConfig.getLayoutDirection());
+ }
+ }
+
+
+ /**
+ * Updates the visibility of the layout.
+ *
+ * @param canShow whether the layout is allowed to be shown by the parent controller.
+ */
+ void updateVisibility(boolean canShow) {
+ View layout = getLayout();
+ if (layout == null) {
+ // Layout may not have been created because it was hidden previously.
+ createLayout(canShow);
+ return;
+ }
+
+ final int newVisibility = canShow && eligibleToShowLayout() ? View.VISIBLE : View.GONE;
+ if (layout.getVisibility() != newVisibility) {
+ layout.setVisibility(newVisibility);
+ }
+ }
+
+ /** Called when display layout changed. */
+ void updateDisplayLayout(DisplayLayout displayLayout) {
+ final Rect prevStableBounds = mStableBounds;
+ final Rect curStableBounds = new Rect();
+ displayLayout.getStableBounds(curStableBounds);
+ mDisplayLayout = displayLayout;
+ if (!prevStableBounds.equals(curStableBounds)) {
+ // Stable bounds changed, update UI surface positions.
+ updateSurfacePosition();
+ mStableBounds.set(curStableBounds);
+ }
+ }
+
+ /** Called when the surface is ready to be placed under the task surface. */
+ @VisibleForTesting
+ void attachToParentSurface(SurfaceControl.Builder b) {
+ mTaskListener.attachChildSurfaceToTask(mTaskId, b);
+ }
+
+ int getDisplayId() {
+ return mDisplayId;
+ }
+
+ int getTaskId() {
+ return mTaskId;
+ }
+
+ /** Releases the surface control and tears down the view hierarchy. */
+ void release() {
+ // Hiding before releasing to avoid flickering when transitioning to the Home screen.
+ View layout = getLayout();
+ if (layout != null) {
+ layout.setVisibility(View.GONE);
+ }
+ removeLayout();
+
+ if (mViewHost != null) {
+ mViewHost.release();
+ mViewHost = null;
+ }
+
+ if (mLeash != null) {
+ final SurfaceControl leash = mLeash;
+ mSyncQueue.runInSync(t -> t.remove(leash));
+ mLeash = null;
+ }
+ }
+
+ /** Re-layouts the view host and updates the surface position. */
+ void relayout() {
+ if (mViewHost == null) {
+ return;
+ }
+ mViewHost.relayout(getWindowLayoutParams());
+ updateSurfacePosition();
+ }
+
+ /**
+ * Updates the position of the surface with respect to the task bounds and display layout
+ * stable bounds.
+ */
+ @VisibleForTesting
+ void updateSurfacePosition() {
+ if (mLeash == null) {
+ return;
+ }
+ // Use stable bounds to prevent controls from overlapping with system bars.
+ final Rect taskBounds = mTaskConfig.windowConfiguration.getBounds();
+ final Rect stableBounds = new Rect();
+ mDisplayLayout.getStableBounds(stableBounds);
+ stableBounds.intersect(taskBounds);
+
+ updateSurfacePosition(taskBounds, stableBounds);
+ }
+
+ /**
+ * Updates the position of the surface with respect to the given {@code taskBounds} and {@code
+ * stableBounds}.
+ */
+ protected abstract void updateSurfacePosition(Rect taskBounds, Rect stableBounds);
+
+ /**
+ * Updates the position of the surface with respect to the given {@code positionX} and {@code
+ * positionY}.
+ */
+ protected void updateSurfacePosition(int positionX, int positionY) {
+ mSyncQueue.runInSync(t -> {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(getTag(), "The leash has been released.");
+ return;
+ }
+ t.setPosition(mLeash, positionX, positionY);
+ });
+ }
+
+ protected int getLayoutDirection() {
+ return mContext.getResources().getConfiguration().getLayoutDirection();
+ }
+
+ @VisibleForTesting
+ SurfaceControlViewHost createSurfaceViewHost() {
+ return new SurfaceControlViewHost(mContext, mContext.getDisplay(), this);
+ }
+
+ /** Gets the layout params. */
+ private WindowManager.LayoutParams getWindowLayoutParams() {
+ View layout = getLayout();
+ if (layout == null) {
+ return new WindowManager.LayoutParams();
+ }
+ // Measure how big the hint is since its size depends on the text size.
+ layout.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
+ return getWindowLayoutParams(layout.getMeasuredWidth(), layout.getMeasuredHeight());
+ }
+
+ /** Gets the layout params given the width and height of the layout. */
+ private WindowManager.LayoutParams getWindowLayoutParams(int width, int height) {
+ final WindowManager.LayoutParams winParams = new WindowManager.LayoutParams(
+ // Cannot be wrap_content as this determines the actual window size
+ width, height,
+ TYPE_APPLICATION_OVERLAY,
+ FLAG_NOT_FOCUSABLE | FLAG_NOT_TOUCH_MODAL,
+ PixelFormat.TRANSLUCENT);
+ winParams.token = new Binder();
+ winParams.setTitle(getClass().getSimpleName() + mTaskId);
+ winParams.privateFlags |= PRIVATE_FLAG_NO_MOVE_ANIMATION | PRIVATE_FLAG_TRUSTED_OVERLAY;
+ return winParams;
+ }
+
+ protected final String getTag() {
+ return getClass().getSimpleName();
+ }
+}
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/dagger/WMShellBaseModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
index 9f4ff7c..2e54c79 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellBaseModule.java
@@ -696,11 +696,12 @@
@WMSingleton
@Provides
static Optional<BackAnimationController> provideBackAnimationController(
+ Context context,
@ShellMainThread ShellExecutor shellExecutor
) {
if (BackAnimationController.IS_ENABLED) {
return Optional.of(
- new BackAnimationController(shellExecutor));
+ new BackAnimationController(shellExecutor, context));
}
return Optional.empty();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index 17005ea..6b0d7f5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -867,6 +867,10 @@
}
private void fadeExistingPip(boolean show) {
+ if (mLeash == null || !mLeash.isValid()) {
+ Log.w(TAG, "Invalid leash on fadeExistingPip: " + mLeash);
+ return;
+ }
final float alphaStart = show ? 0 : 1;
final float alphaEnd = show ? 1 : 0;
mPipAnimationController
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 72ead00..32861b6 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -103,7 +103,7 @@
};
context.registerReceiverForAllUsers(closeSystemDialogsBroadcastReceiver,
new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS), null /* permission */,
- mainHandler);
+ mainHandler, Context.RECEIVER_EXPORTED);
pipMediaController.addActionListener(this::onMediaActionsChanged);
}
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/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
index 6524182..9061239 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/ExitPipWithSwipeDownTest.kt
@@ -93,7 +93,7 @@
fun getParams(): List<FlickerTestParameter> {
return FlickerTestParameterFactory.getInstance()
.getConfigNonRotationTests(supportedRotations = listOf(Surface.ROTATION_0),
- repetitions = 20)
+ repetitions = 5)
}
}
}
\ No newline at end of file
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..21ced0d 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
@@ -23,6 +23,7 @@
import android.app.IActivityTaskManager;
import android.app.WindowConfiguration;
+import android.content.Context;
import android.hardware.HardwareBuffer;
import android.os.RemoteCallback;
import android.os.RemoteException;
@@ -52,6 +53,9 @@
private final ShellExecutor mShellExecutor = new TestShellExecutor();
@Mock
+ private Context mContext;
+
+ @Mock
private SurfaceControl.Transaction mTransaction;
@Mock
@@ -63,7 +67,7 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mController = new BackAnimationController(
- mShellExecutor, mTransaction, mActivityTaskManager);
+ mShellExecutor, mTransaction, mActivityTaskManager, mContext);
}
private void createNavigationInfo(SurfaceControl topWindowLeash,
@@ -75,7 +79,8 @@
screenshotSurface,
hardwareBuffer,
new WindowConfiguration(),
- new RemoteCallback((bundle) -> {}));
+ new RemoteCallback((bundle) -> {}),
+ null);
try {
doReturn(navigationInfo).when(mActivityTaskManager).startBackNavigation();
} catch (RemoteException ex) {
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
index 4352fd3..741da3f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIControllerTest.java
@@ -116,35 +116,17 @@
TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_HIDDEN);
- // Verify that the restart button is added with non-null size compat info.
+ // Verify that the compat controls are added with non-null size compat info.
mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
verify(mController).createLayout(any(), eq(taskInfo), eq(mMockTaskListener));
- // Verify that the restart button is updated with non-null new size compat info.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
-
- // Verify that the restart button is updated with new camera state.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */,
+ // Verify that the compat controls are updated with non-null new size compat info.
+ taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED),
- mMockTaskListener);
-
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, true /* canShow */);
// Verify that compat controls are removed with null compat info.
mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
@@ -155,7 +137,7 @@
clearInvocations(mMockLayout);
clearInvocations(mController);
- // Verify that compat controls are removed with dismissed camera state.
+ // Verify that compat controls are removed with no size compat and dismissed camera state.
taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID,
true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
@@ -245,11 +227,11 @@
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while IME is showing.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after IME is hidden.
mController.onImeVisibilityChanged(DISPLAY_ID, false /* isShowing */);
@@ -268,11 +250,11 @@
verify(mMockLayout).updateVisibility(false);
// Verify button remains hidden while keyguard is occluded.
- mController.onCompatInfoChanged(createTaskInfo(DISPLAY_ID, TASK_ID,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN), mMockTaskListener);
+ TaskInfo taskInfo = createTaskInfo(DISPLAY_ID, TASK_ID, true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN);
+ mController.onCompatInfoChanged(taskInfo, mMockTaskListener);
- verify(mMockLayout).updateCompatInfo(new Configuration(), mMockTaskListener,
- false /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mMockLayout).updateCompatInfo(taskInfo, mMockTaskListener, false /* canShow */);
// Verify button is shown after keyguard becomes not occluded.
mController.onKeyguardOccludedChanged(false);
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
index 353d8fe..2117817 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUILayoutTest.java
@@ -27,6 +27,9 @@
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
+import android.app.TaskInfo.CameraCompatControlState;
import android.content.res.Configuration;
import android.testing.AndroidTestingRunner;
import android.view.LayoutInflater;
@@ -83,7 +86,7 @@
spyOn(mWindowManager);
spyOn(mCompatUILayout);
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
}
@Test
@@ -107,8 +110,8 @@
@Test
public void testOnClickForSizeCompatHint() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
final LinearLayout sizeCompatHint = mCompatUILayout.findViewById(R.id.size_compat_hint);
sizeCompatHint.performClick();
@@ -117,8 +120,8 @@
@Test
public void testUpdateCameraTreatmentButton_treatmentAppliedByDefault() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
@@ -135,8 +138,8 @@
@Test
public void testUpdateCameraTreatmentButton_treatmentSuggestedByDefault() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_treatment_button);
button.performClick();
@@ -153,8 +156,8 @@
@Test
public void testOnCameraDismissButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final ImageButton button =
mCompatUILayout.findViewById(R.id.camera_compat_dismiss_button);
button.performClick();
@@ -188,11 +191,19 @@
@Test
public void testOnClickForCameraCompatHint() {
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* show */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
final LinearLayout hint = mCompatUILayout.findViewById(R.id.camera_compat_hint);
hint.performClick();
verify(mCompatUILayout).setCameraCompatHintVisibility(/* show= */ false);
}
+
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
index 11c7973..de882ea 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/compatui/CompatUIWindowManagerTest.java
@@ -26,8 +26,8 @@
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
-import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.clearInvocations;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
@@ -35,6 +35,8 @@
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import android.app.ActivityManager;
+import android.app.TaskInfo;
import android.content.res.Configuration;
import android.graphics.Rect;
import android.testing.AndroidTestingRunner;
@@ -75,47 +77,45 @@
@Mock private ShellTaskOrganizer.TaskListener mTaskListener;
@Mock private CompatUILayout mCompatUILayout;
@Mock private SurfaceControlViewHost mViewHost;
- private Configuration mTaskConfig;
private CompatUIWindowManager mWindowManager;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
- mTaskConfig = new Configuration();
mWindowManager = new CompatUIWindowManager(mContext, new Configuration(),
mSyncTransactionQueue, mCallback, TASK_ID, mTaskListener, new DisplayLayout(),
false /* hasShownSizeCompatHint */, false /* hasShownSizeCompatHint */);
spyOn(mWindowManager);
- doReturn(mCompatUILayout).when(mWindowManager).inflateCompatUILayout();
+ doReturn(mCompatUILayout).when(mWindowManager).inflateLayout();
doReturn(mViewHost).when(mWindowManager).createSurfaceViewHost();
}
@Test
public void testCreateSizeCompatButton() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, never()).inflateCompatUILayout();
+ verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
mWindowManager.mShouldShowSizeCompatHint = false;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
mWindowManager.mShouldShowSizeCompatHint = true;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager, times(2)).inflateCompatUILayout();
+ verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout).setSizeCompatHintVisibility(true /* show */);
assertFalse(mWindowManager.mShouldShowSizeCompatHint);
@@ -123,10 +123,10 @@
@Test
public void testRelease() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
mWindowManager.release();
@@ -135,65 +135,104 @@
@Test
public void testUpdateCompatInfo() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, taskInfo);
// No diff
clearInvocations(mWindowManager);
- mWindowManager.updateCompatInfo(mTaskConfig, mTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
verify(mWindowManager, never()).updateSurfacePosition();
verify(mWindowManager, never()).release();
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean());
// Change task listener, recreate button.
clearInvocations(mWindowManager);
final ShellTaskOrganizer.TaskListener newTaskListener = mock(
ShellTaskOrganizer.TaskListener.class);
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).release();
- verify(mWindowManager).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager).createLayout(true);
+
+ // Change in Size Compat to false, hides restart button.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ false);
+
+ // Change in Size Compat to true, shows restart button.
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
+
+ verify(mCompatUILayout).setRestartButtonVisibility(/* show */ true);
// Change Camera Compat state, show a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ clearInvocations(mWindowManager);
+ clearInvocations(mCompatUILayout);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_APPLIED);
+ // Change Camera Compat state, update a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
- // Change Camera Compat state, update a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener, true /* show */,
- true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ true);
verify(mCompatUILayout).updateCameraTreatmentButton(
CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ // Change Camera Compat state to hidden, hide a control.
clearInvocations(mWindowManager);
clearInvocations(mCompatUILayout);
- // Change Camera Compat state to hidden, hide a control.
- mWindowManager.updateCompatInfo(mTaskConfig, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mCompatUILayout).setCameraControlVisibility(/* show */ false);
// Change task bounds, update position.
clearInvocations(mWindowManager);
- final Configuration newTaskConfiguration = new Configuration();
- newTaskConfiguration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
- mWindowManager.updateCompatInfo(newTaskConfiguration, newTaskListener,
- true /* show */, true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ taskInfo.configuration.windowConfiguration.setBounds(new Rect(0, 1000, 0, 2000));
+ mWindowManager.updateCompatInfo(taskInfo, newTaskListener, true /* canShow */);
verify(mWindowManager).updateSurfacePosition();
}
@Test
+ public void testUpdateCompatInfoLayoutNotInflatedYet() {
+ TaskInfo taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(false /* canShow */, taskInfo);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to false and pass canShow true, layout shouldn't be
+ // inflated
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(false /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager, never()).inflateLayout();
+
+ // Change topActivityInSizeCompat to true and pass canShow true, layout should be inflated.
+ clearInvocations(mWindowManager);
+ taskInfo = createTaskInfo(true /* hasSizeCompat */, CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.updateCompatInfo(taskInfo, mTaskListener, true /* canShow */);
+
+ verify(mWindowManager).inflateLayout();
+ }
+
+ @Test
public void testUpdateDisplayLayout() {
final DisplayInfo displayInfo = new DisplayInfo();
displayInfo.logicalWidth = 1000;
@@ -237,26 +276,25 @@
@Test
public void testUpdateVisibility() {
// Create button if it is not created.
- mWindowManager.mCompatUILayout = null;
+ mWindowManager.mLayout = null;
mWindowManager.mHasSizeCompat = true;
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager).createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ verify(mWindowManager).createLayout(true /* canShow */);
// Hide button.
clearInvocations(mWindowManager);
doReturn(View.VISIBLE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(false /* show */);
+ mWindowManager.updateVisibility(false /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.GONE);
// Show button.
doReturn(View.GONE).when(mCompatUILayout).getVisibility();
- mWindowManager.updateVisibility(true /* show */);
+ mWindowManager.updateVisibility(true /* canShow */);
- verify(mWindowManager, never()).createLayout(anyBoolean(), anyBoolean(), anyInt());
+ verify(mWindowManager, never()).createLayout(anyBoolean(), any());
verify(mCompatUILayout).setVisibility(View.VISIBLE);
}
@@ -270,8 +308,8 @@
@Test
public void testOnCameraDismissButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.onCameraDismissButtonClicked();
@@ -281,8 +319,8 @@
@Test
public void testOnCameraTreatmentButtonClicked() {
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
clearInvocations(mCompatUILayout);
mWindowManager.onCameraTreatmentButtonClicked();
@@ -310,10 +348,10 @@
public void testOnRestartButtonLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mShouldShowSizeCompatHint = false;
- mWindowManager.createLayout(true /* show */, true /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_HIDDEN);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(true /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_HIDDEN));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setSizeCompatHintVisibility(true /* show */);
mWindowManager.onRestartButtonLongClicked();
@@ -325,10 +363,10 @@
public void testOnCamerControlLongClicked_showHint() {
// Not create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
mWindowManager.onCameraButtonLongClicked();
@@ -339,30 +377,37 @@
@Test
public void testCreateCameraCompatControl() {
// Not create layout if show is false.
- mWindowManager.createLayout(false /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(false /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager, never()).inflateCompatUILayout();
+ verify(mWindowManager, never()).inflateLayout();
// Not create hint popup.
mWindowManager.mShouldShowCameraCompatHint = false;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager).inflateCompatUILayout();
+ verify(mWindowManager).inflateLayout();
verify(mCompatUILayout, never()).setCameraCompatHintVisibility(true /* show */);
verify(mCompatUILayout).setCameraControlVisibility(true /* show */);
// Create hint popup.
mWindowManager.release();
mWindowManager.mShouldShowCameraCompatHint = true;
- mWindowManager.createLayout(true /* show */, false /* hasSizeCompat */,
- CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED);
+ mWindowManager.createLayout(true /* canShow */, createTaskInfo(false /* hasSizeCompat */,
+ CAMERA_COMPAT_CONTROL_TREATMENT_SUGGESTED));
- verify(mWindowManager, times(2)).inflateCompatUILayout();
+ verify(mWindowManager, times(2)).inflateLayout();
assertNotNull(mCompatUILayout);
verify(mCompatUILayout, times(2)).setCameraControlVisibility(true /* show */);
assertFalse(mWindowManager.mShouldShowCameraCompatHint);
}
+ private static TaskInfo createTaskInfo(boolean hasSizeCompat,
+ @TaskInfo.CameraCompatControlState int cameraCompatControlState) {
+ ActivityManager.RunningTaskInfo taskInfo = new ActivityManager.RunningTaskInfo();
+ taskInfo.topActivityInSizeCompat = hasSizeCompat;
+ taskInfo.cameraCompatControlState = cameraCompatControlState;
+ return taskInfo;
+ }
}
diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp
index dc31bdd..f6ad4c2 100644
--- a/libs/hwui/Android.bp
+++ b/libs/hwui/Android.bp
@@ -345,6 +345,7 @@
"jni/PathEffect.cpp",
"jni/PathMeasure.cpp",
"jni/Picture.cpp",
+ "jni/Region.cpp",
"jni/Shader.cpp",
"jni/RenderEffect.cpp",
"jni/Typeface.cpp",
@@ -394,7 +395,6 @@
"jni/GraphicsStatsService.cpp",
"jni/Movie.cpp",
"jni/MovieImpl.cpp",
- "jni/Region.cpp", // requires libbinder_ndk
"jni/pdf/PdfDocument.cpp",
"jni/pdf/PdfEditor.cpp",
"jni/pdf/PdfRenderer.cpp",
diff --git a/libs/hwui/apex/LayoutlibLoader.cpp b/libs/hwui/apex/LayoutlibLoader.cpp
index dca10e2..942c050 100644
--- a/libs/hwui/apex/LayoutlibLoader.cpp
+++ b/libs/hwui/apex/LayoutlibLoader.cpp
@@ -14,31 +14,22 @@
* limitations under the License.
*/
-#include "graphics_jni_helpers.h"
-
#include <GraphicsJNI.h>
#include <SkGraphics.h>
-#include <sstream>
-#include <iostream>
-#include <unicode/putil.h>
#include <unordered_map>
#include <vector>
+#include "Properties.h"
+#include "android/graphics/jni_runtime.h"
+#include "graphics_jni_helpers.h"
+
using namespace std;
-/*
- * This is responsible for setting up the JNI environment for communication between
- * the Java and native parts of layoutlib, including registering native methods.
- * This is mostly achieved by copying the way it is done in the platform
- * (see AndroidRuntime.cpp).
- */
-
-static JavaVM* javaVM;
-
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_ByteBufferStreamAdaptor(JNIEnv* env);
+extern int register_android_graphics_Camera(JNIEnv* env);
extern int register_android_graphics_CreateJavaOutputStreamAdaptor(JNIEnv* env);
extern int register_android_graphics_Graphics(JNIEnv* env);
extern int register_android_graphics_ImageDecoder(JNIEnv*);
@@ -49,10 +40,12 @@
extern int register_android_graphics_Shader(JNIEnv* env);
extern int register_android_graphics_RenderEffect(JNIEnv* env);
extern int register_android_graphics_Typeface(JNIEnv* env);
+extern int register_android_graphics_YuvImage(JNIEnv* env);
namespace android {
extern int register_android_graphics_Canvas(JNIEnv* env);
+extern int register_android_graphics_CanvasProperty(JNIEnv* env);
extern int register_android_graphics_ColorFilter(JNIEnv* env);
extern int register_android_graphics_ColorSpace(JNIEnv* env);
extern int register_android_graphics_DrawFilter(JNIEnv* env);
@@ -62,7 +55,7 @@
extern int register_android_graphics_Path(JNIEnv* env);
extern int register_android_graphics_PathMeasure(JNIEnv* env);
extern int register_android_graphics_Picture(JNIEnv* env);
-//extern int register_android_graphics_Region(JNIEnv* env);
+extern int register_android_graphics_Region(JNIEnv* env);
extern int register_android_graphics_animation_NativeInterpolatorFactory(JNIEnv* env);
extern int register_android_graphics_animation_RenderNodeAnimator(JNIEnv* env);
extern int register_android_graphics_drawable_AnimatedVectorDrawable(JNIEnv* env);
@@ -71,9 +64,11 @@
extern int register_android_graphics_fonts_FontFamily(JNIEnv* env);
extern int register_android_graphics_text_LineBreaker(JNIEnv* env);
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
+extern int register_android_graphics_text_TextShaper(JNIEnv* env);
+
extern int register_android_util_PathParser(JNIEnv* env);
-extern int register_android_view_RenderNode(JNIEnv* env);
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
+extern int register_android_view_RenderNode(JNIEnv* env);
#define REG_JNI(name) { name }
struct RegJNIRec {
@@ -87,8 +82,9 @@
{"android.graphics.BitmapFactory", REG_JNI(register_android_graphics_BitmapFactory)},
{"android.graphics.ByteBufferStreamAdaptor",
REG_JNI(register_android_graphics_ByteBufferStreamAdaptor)},
+ {"android.graphics.Camera", REG_JNI(register_android_graphics_Camera)},
{"android.graphics.Canvas", REG_JNI(register_android_graphics_Canvas)},
- {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
+ {"android.graphics.CanvasProperty", REG_JNI(register_android_graphics_CanvasProperty)},
{"android.graphics.ColorFilter", REG_JNI(register_android_graphics_ColorFilter)},
{"android.graphics.ColorSpace", REG_JNI(register_android_graphics_ColorSpace)},
{"android.graphics.CreateJavaOutputStreamAdaptor",
@@ -107,10 +103,12 @@
{"android.graphics.PathMeasure", REG_JNI(register_android_graphics_PathMeasure)},
{"android.graphics.Picture", REG_JNI(register_android_graphics_Picture)},
{"android.graphics.RecordingCanvas", REG_JNI(register_android_view_DisplayListCanvas)},
-// {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.Region", REG_JNI(register_android_graphics_Region)},
+ {"android.graphics.RenderNode", REG_JNI(register_android_view_RenderNode)},
{"android.graphics.Shader", REG_JNI(register_android_graphics_Shader)},
{"android.graphics.RenderEffect", REG_JNI(register_android_graphics_RenderEffect)},
{"android.graphics.Typeface", REG_JNI(register_android_graphics_Typeface)},
+ {"android.graphics.YuvImage", REG_JNI(register_android_graphics_YuvImage)},
{"android.graphics.animation.NativeInterpolatorFactory",
REG_JNI(register_android_graphics_animation_NativeInterpolatorFactory)},
{"android.graphics.animation.RenderNodeAnimator",
@@ -124,6 +122,7 @@
{"android.graphics.text.LineBreaker", REG_JNI(register_android_graphics_text_LineBreaker)},
{"android.graphics.text.MeasuredText",
REG_JNI(register_android_graphics_text_MeasuredText)},
+ {"android.graphics.text.TextRunShaper", REG_JNI(register_android_graphics_text_TextShaper)},
{"android.util.PathParser", REG_JNI(register_android_util_PathParser)},
};
@@ -177,10 +176,9 @@
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
// Get the names of classes that need to register their native methods
- auto nativesClassesJString =
- (jstring) env->CallStaticObjectMethod(system,
- getPropertyMethod, env->NewStringUTF("native_classes"),
- env->NewStringUTF(""));
+ auto nativesClassesJString = (jstring)env->CallStaticObjectMethod(
+ system, getPropertyMethod, env->NewStringUTF("graphics_native_classes"),
+ env->NewStringUTF(""));
vector<string> classesToRegister = parseCsv(env, nativesClassesJString);
if (register_jni_procs(gRegJNIMap, classesToRegister, env) < 0) {
diff --git a/libs/hwui/jni/Shader.cpp b/libs/hwui/jni/Shader.cpp
index c505b53..899c7d4 100644
--- a/libs/hwui/jni/Shader.cpp
+++ b/libs/hwui/jni/Shader.cpp
@@ -261,11 +261,10 @@
return static_cast<jlong>(reinterpret_cast<uintptr_t>(&SkRuntimeShaderBuilder_delete));
}
-static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr,
- jboolean isOpaque) {
+static jlong RuntimeShader_create(JNIEnv* env, jobject, jlong shaderBuilder, jlong matrixPtr) {
SkRuntimeShaderBuilder* builder = reinterpret_cast<SkRuntimeShaderBuilder*>(shaderBuilder);
const SkMatrix* matrix = reinterpret_cast<const SkMatrix*>(matrixPtr);
- sk_sp<SkShader> shader = builder->makeShader(matrix, isOpaque == JNI_TRUE);
+ sk_sp<SkShader> shader = builder->makeShader(matrix, false);
ThrowIAE_IfNull(env, shader);
return reinterpret_cast<jlong>(shader.release());
}
@@ -419,7 +418,7 @@
static const JNINativeMethod gRuntimeShaderMethods[] = {
{"nativeGetFinalizer", "()J", (void*)RuntimeShader_getNativeFinalizer},
- {"nativeCreateShader", "(JJZ)J", (void*)RuntimeShader_create},
+ {"nativeCreateShader", "(JJ)J", (void*)RuntimeShader_create},
{"nativeCreateBuilder", "(Ljava/lang/String;)J", (void*)RuntimeShader_createShaderBuilder},
{"nativeUpdateUniforms", "(JLjava/lang/String;[FZ)V",
(void*)RuntimeShader_updateFloatArrayUniforms},
diff --git a/media/Android.bp b/media/Android.bp
index fcdfd72..5aedcfb 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -108,6 +108,11 @@
vndk: {
enabled: true,
},
+ min_sdk_version: "29",
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.bluetooth",
+ ],
},
},
}
diff --git a/media/java/android/media/AudioDescriptor.java b/media/java/android/media/AudioDescriptor.java
index 11371b1..df648be 100644
--- a/media/java/android/media/AudioDescriptor.java
+++ b/media/java/android/media/AudioDescriptor.java
@@ -18,16 +18,21 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
+import java.util.Objects;
/**
* The AudioDescriptor contains the information to describe the audio playback/capture
* capabilities. The capabilities are described by a byte array, which is defined by a
* particular standard. This is used when the format is unrecognized to the platform.
*/
-public class AudioDescriptor {
+public class AudioDescriptor implements Parcelable {
/**
* The audio standard is not specified.
*/
@@ -49,7 +54,15 @@
private final byte[] mDescriptor;
private final int mEncapsulationType;
- AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) {
+ /**
+ * @hide
+ * Constructor from standard, encapsulation type and descriptor
+ * @param standard the standard of the audio descriptor
+ * @param encapsulationType the encapsulation type of the audio descriptor
+ * @param descriptor the audio descriptor
+ */
+ @SystemApi
+ public AudioDescriptor(int standard, int encapsulationType, @NonNull byte[] descriptor) {
mStandard = standard;
mEncapsulationType = encapsulationType;
mDescriptor = descriptor;
@@ -87,4 +100,66 @@
public @AudioProfile.EncapsulationType int getEncapsulationType() {
return mEncapsulationType;
}
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mStandard, mEncapsulationType, Arrays.hashCode(mDescriptor));
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AudioDescriptor that = (AudioDescriptor) o;
+ return ((mStandard == that.mStandard)
+ && (mEncapsulationType == that.mEncapsulationType)
+ && (Arrays.equals(mDescriptor, that.mDescriptor)));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("{");
+ sb.append("standard=" + mStandard);
+ sb.append(", encapsulation type=" + mEncapsulationType);
+ if (mDescriptor != null && mDescriptor.length > 0) {
+ sb.append(", descriptor=").append(Arrays.toString(mDescriptor));
+ }
+ sb.append("}");
+ return sb.toString();
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mStandard);
+ dest.writeInt(mEncapsulationType);
+ dest.writeByteArray(mDescriptor);
+ }
+
+ private AudioDescriptor(@NonNull Parcel in) {
+ mStandard = in.readInt();
+ mEncapsulationType = in.readInt();
+ mDescriptor = in.createByteArray();
+ }
+
+ public static final @NonNull Parcelable.Creator<AudioDescriptor> CREATOR =
+ new Parcelable.Creator<AudioDescriptor>() {
+ /**
+ * Rebuilds an AudioDescriptor previously stored with writeToParcel().
+ * @param p Parcel object to read the AudioDescriptor from
+ * @return a new AudioDescriptor created from the data in the parcel
+ */
+ public AudioDescriptor createFromParcel(Parcel p) {
+ return new AudioDescriptor(p);
+ }
+
+ public AudioDescriptor[] newArray(int size) {
+ return new AudioDescriptor[size];
+ }
+ };
}
diff --git a/media/java/android/media/AudioDeviceAttributes.java b/media/java/android/media/AudioDeviceAttributes.java
index 1448c49..af3c295 100644
--- a/media/java/android/media/AudioDeviceAttributes.java
+++ b/media/java/android/media/AudioDeviceAttributes.java
@@ -18,12 +18,16 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.Objects;
/**
@@ -65,16 +69,27 @@
* The unique address of the device. Some devices don't have addresses, only an empty string.
*/
private final @NonNull String mAddress;
-
+ /**
+ * The non-unique name of the device. Some devices don't have names, only an empty string.
+ * Should not be used as a unique identifier for a device.
+ */
+ private final @NonNull String mName;
/**
* Is input or output device
*/
private final @Role int mRole;
-
/**
* The internal audio device type
*/
private final int mNativeType;
+ /**
+ * List of AudioProfiles supported by the device
+ */
+ private final @NonNull List<AudioProfile> mAudioProfiles;
+ /**
+ * List of AudioDescriptors supported by the device
+ */
+ private final @NonNull List<AudioDescriptor> mAudioDescriptors;
/**
* @hide
@@ -88,7 +103,10 @@
mRole = deviceInfo.isSink() ? ROLE_OUTPUT : ROLE_INPUT;
mType = deviceInfo.getType();
mAddress = deviceInfo.getAddress();
+ mName = String.valueOf(deviceInfo.getProductName());
mNativeType = deviceInfo.getInternalType();
+ mAudioProfiles = deviceInfo.getAudioProfiles();
+ mAudioDescriptors = deviceInfo.getAudioDescriptors();
}
/**
@@ -100,7 +118,24 @@
*/
@SystemApi
public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
- @NonNull String address) {
+ @NonNull String address) {
+ this(role, type, address, "", new ArrayList<>(), new ArrayList<>());
+ }
+
+ /**
+ * @hide
+ * Constructor with specification of all attributes
+ * @param role indicates input or output role
+ * @param type the device type, as defined in {@link AudioDeviceInfo}
+ * @param address the address of the device, or an empty string for devices without one
+ * @param name the name of the device, or an empty string for devices without one
+ * @param profiles the list of AudioProfiles supported by the device
+ * @param descriptors the list of AudioDescriptors supported by the device
+ */
+ @SystemApi
+ public AudioDeviceAttributes(@Role int role, @AudioDeviceInfo.AudioDeviceType int type,
+ @NonNull String address, @NonNull String name, @NonNull List<AudioProfile> profiles,
+ @NonNull List<AudioDescriptor> descriptors) {
Objects.requireNonNull(address);
if (role != ROLE_OUTPUT && role != ROLE_INPUT) {
throw new IllegalArgumentException("Invalid role " + role);
@@ -118,19 +153,37 @@
mRole = role;
mType = type;
mAddress = address;
+ mName = name;
+ mAudioProfiles = profiles;
+ mAudioDescriptors = descriptors;
}
/**
* @hide
- * Constructor from internal device type and address
- * @param type the internal device type, as defined in {@link AudioSystem}
+ * Constructor called from AudioSystem JNI when creating an AudioDeviceAttributes from a native
+ * AudioDeviceTypeAddr instance.
+ * @param nativeType the internal device type, as defined in {@link AudioSystem}
* @param address the address of the device, or an empty string for devices without one
*/
public AudioDeviceAttributes(int nativeType, @NonNull String address) {
+ this(nativeType, address, "");
+ }
+
+ /**
+ * @hide
+ * Constructor called from BtHelper to connect or disconnect a Bluetooth device.
+ * @param nativeType the internal device type, as defined in {@link AudioSystem}
+ * @param address the address of the device, or an empty string for devices without one
+ * @param name the name of the device, or an empty string for devices without one
+ */
+ public AudioDeviceAttributes(int nativeType, @NonNull String address, @NonNull String name) {
mRole = (nativeType & AudioSystem.DEVICE_BIT_IN) != 0 ? ROLE_INPUT : ROLE_OUTPUT;
mType = AudioDeviceInfo.convertInternalDeviceToDeviceType(nativeType);
mAddress = address;
+ mName = name;
mNativeType = nativeType;
+ mAudioProfiles = new ArrayList<>();
+ mAudioDescriptors = new ArrayList<>();
}
/**
@@ -165,6 +218,16 @@
/**
* @hide
+ * Returns the name of the audio device, or an empty string for devices without one
+ * @return the device name
+ */
+ @SystemApi
+ public @NonNull String getName() {
+ return mName;
+ }
+
+ /**
+ * @hide
* Returns the internal device type of a device
* @return the internal device type
*/
@@ -172,9 +235,29 @@
return mNativeType;
}
+ /**
+ * @hide
+ * Returns the list of AudioProfiles supported by the device
+ * @return the list of AudioProfiles
+ */
+ @SystemApi
+ public @NonNull List<AudioProfile> getAudioProfiles() {
+ return mAudioProfiles;
+ }
+
+ /**
+ * @hide
+ * Returns the list of AudioDescriptors supported by the device
+ * @return the list of AudioDescriptors
+ */
+ @SystemApi
+ public @NonNull List<AudioDescriptor> getAudioDescriptors() {
+ return mAudioDescriptors;
+ }
+
@Override
public int hashCode() {
- return Objects.hash(mRole, mType, mAddress);
+ return Objects.hash(mRole, mType, mAddress, mName, mAudioProfiles, mAudioDescriptors);
}
@Override
@@ -185,6 +268,25 @@
AudioDeviceAttributes that = (AudioDeviceAttributes) o;
return ((mRole == that.mRole)
&& (mType == that.mType)
+ && mAddress.equals(that.mAddress)
+ && mName.equals(that.mName)
+ && mAudioProfiles.equals(that.mAudioProfiles)
+ && mAudioDescriptors.equals(that.mAudioDescriptors));
+ }
+
+ /**
+ * Returns true if the role, type and address are equal. Called to compare with an
+ * AudioDeviceAttributes that was created from a native AudioDeviceTypeAddr instance.
+ * @param o object to compare with
+ * @return whether role, type and address are equal
+ */
+ public boolean equalTypeAddress(@Nullable Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AudioDeviceAttributes that = (AudioDeviceAttributes) o;
+ return ((mRole == that.mRole)
+ && (mType == that.mType)
&& mAddress.equals(that.mAddress));
}
@@ -199,7 +301,10 @@
+ " role:" + roleToString(mRole)
+ " type:" + (mRole == ROLE_OUTPUT ? AudioSystem.getOutputDeviceName(mNativeType)
: AudioSystem.getInputDeviceName(mNativeType))
- + " addr:" + mAddress);
+ + " addr:" + mAddress
+ + " name:" + mName
+ + " profiles:" + mAudioProfiles.toString()
+ + " descriptors:" + mAudioDescriptors.toString());
}
@Override
@@ -212,14 +317,26 @@
dest.writeInt(mRole);
dest.writeInt(mType);
dest.writeString(mAddress);
+ dest.writeString(mName);
dest.writeInt(mNativeType);
+ dest.writeParcelableArray(
+ mAudioProfiles.toArray(new AudioProfile[mAudioProfiles.size()]), flags);
+ dest.writeParcelableArray(
+ mAudioDescriptors.toArray(new AudioDescriptor[mAudioDescriptors.size()]), flags);
}
private AudioDeviceAttributes(@NonNull Parcel in) {
mRole = in.readInt();
mType = in.readInt();
mAddress = in.readString();
+ mName = in.readString();
mNativeType = in.readInt();
+ AudioProfile[] audioProfilesArray =
+ in.readParcelableArray(AudioProfile.class.getClassLoader(), AudioProfile.class);
+ mAudioProfiles = new ArrayList<AudioProfile>(Arrays.asList(audioProfilesArray));
+ AudioDescriptor[] audioDescriptorsArray = in.readParcelableArray(
+ AudioDescriptor.class.getClassLoader(), AudioDescriptor.class);
+ mAudioDescriptors = new ArrayList<AudioDescriptor>(Arrays.asList(audioDescriptorsArray));
}
public static final @NonNull Parcelable.Creator<AudioDeviceAttributes> CREATOR =
diff --git a/media/java/android/media/AudioDeviceInfo.java b/media/java/android/media/AudioDeviceInfo.java
index dd17dc6..3d08959 100644
--- a/media/java/android/media/AudioDeviceInfo.java
+++ b/media/java/android/media/AudioDeviceInfo.java
@@ -421,7 +421,7 @@
*/
public CharSequence getProductName() {
String portName = mPort.name();
- return portName.length() != 0 ? portName : android.os.Build.MODEL;
+ return (portName != null && portName.length() != 0) ? portName : android.os.Build.MODEL;
}
/**
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index 15a398d..cdc3163 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -5874,7 +5874,7 @@
return false;
}
- /**
+ /**
* Indicate wired accessory connection state change.
* @param device type of device connected/disconnected (AudioManager.DEVICE_OUT_xxx)
* @param state new connection state: 1 connected, 0 disconnected
@@ -5883,10 +5883,29 @@
*/
@UnsupportedAppUsage
@RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
- public void setWiredDeviceConnectionState(int type, int state, String address, String name) {
+ public void setWiredDeviceConnectionState(int device, int state, String address,
+ String name) {
+ final IAudioService service = getService();
+ int role = isOutputDevice(device)
+ ? AudioDeviceAttributes.ROLE_OUTPUT : AudioDeviceAttributes.ROLE_INPUT;
+ AudioDeviceAttributes attributes = new AudioDeviceAttributes(
+ role, AudioDeviceInfo.convertInternalDeviceToDeviceType(device), address,
+ name, new ArrayList<>()/*mAudioProfiles*/, new ArrayList<>()/*mAudioDescriptors*/);
+ setWiredDeviceConnectionState(attributes, state);
+ }
+
+ /**
+ * Indicate wired accessory connection state change and attributes.
+ * @param state new connection state: 1 connected, 0 disconnected
+ * @param attributes attributes of the connected device
+ * {@hide}
+ */
+ @UnsupportedAppUsage
+ @RequiresPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING)
+ public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes, int state) {
final IAudioService service = getService();
try {
- service.setWiredDeviceConnectionState(type, state, address, name,
+ service.setWiredDeviceConnectionState(attributes, state,
mApplicationContext.getOpPackageName());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
@@ -5919,13 +5938,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/AudioProfile.java b/media/java/android/media/AudioProfile.java
index ae8d0a5..5c5f837 100644
--- a/media/java/android/media/AudioProfile.java
+++ b/media/java/android/media/AudioProfile.java
@@ -18,10 +18,14 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
+import java.util.Objects;
import java.util.stream.Collectors;
/**
@@ -33,7 +37,7 @@
* be reported in different audio profiles. The application can choose any of the encapsulation
* types.
*/
-public class AudioProfile {
+public class AudioProfile implements Parcelable {
/**
* No encapsulation type is specified.
*/
@@ -57,9 +61,19 @@
private final int[] mChannelIndexMasks;
private final int mEncapsulationType;
- AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks,
- @NonNull int[] channelIndexMasks,
- int encapsulationType) {
+ /**
+ * @hide
+ * Constructor from format, sampling rates, channel masks, channel index masks and
+ * encapsulation type.
+ * @param format the audio format
+ * @param samplingRates the supported sampling rates
+ * @param channelMasks the supported channel masks
+ * @param channelIndexMasks the supported channel index masks
+ * @param encapsulationType the encapsulation type of the encoding format
+ */
+ @SystemApi
+ public AudioProfile(int format, @NonNull int[] samplingRates, @NonNull int[] channelMasks,
+ @NonNull int[] channelIndexMasks, int encapsulationType) {
mFormat = format;
mSamplingRates = samplingRates;
mChannelMasks = channelMasks;
@@ -114,6 +128,26 @@
}
@Override
+ public int hashCode() {
+ return Objects.hash(mFormat, Arrays.hashCode(mSamplingRates),
+ Arrays.hashCode(mChannelMasks), Arrays.hashCode(mChannelIndexMasks),
+ mEncapsulationType);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ AudioProfile that = (AudioProfile) o;
+ return ((mFormat == that.mFormat)
+ && (hasIdenticalElements(mSamplingRates, that.mSamplingRates))
+ && (hasIdenticalElements(mChannelMasks, that.mChannelMasks))
+ && (hasIdenticalElements(mChannelIndexMasks, that.mChannelIndexMasks))
+ && (mEncapsulationType == that.mEncapsulationType));
+ }
+
+ @Override
public String toString() {
StringBuilder sb = new StringBuilder("{");
sb.append(AudioFormat.toLogFriendlyEncoding(mFormat));
@@ -126,6 +160,7 @@
if (mChannelIndexMasks != null && mChannelIndexMasks.length > 0) {
sb.append(", channel index masks=").append(Arrays.toString(mChannelIndexMasks));
}
+ sb.append(", encapsulation type=" + mEncapsulationType);
sb.append("}");
return sb.toString();
}
@@ -137,4 +172,50 @@
return Arrays.stream(ints).mapToObj(anInt -> String.format("0x%02X", anInt))
.collect(Collectors.joining(", "));
}
+
+ private static boolean hasIdenticalElements(int[] array1, int[] array2) {
+ int[] sortedArray1 = Arrays.copyOf(array1, array1.length);
+ Arrays.sort(sortedArray1);
+ int[] sortedArray2 = Arrays.copyOf(array2, array2.length);
+ Arrays.sort(sortedArray2);
+ return Arrays.equals(sortedArray1, sortedArray2);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mFormat);
+ dest.writeIntArray(mSamplingRates);
+ dest.writeIntArray(mChannelMasks);
+ dest.writeIntArray(mChannelIndexMasks);
+ dest.writeInt(mEncapsulationType);
+ }
+
+ private AudioProfile(@NonNull Parcel in) {
+ mFormat = in.readInt();
+ mSamplingRates = in.createIntArray();
+ mChannelMasks = in.createIntArray();
+ mChannelIndexMasks = in.createIntArray();
+ mEncapsulationType = in.readInt();
+ }
+
+ public static final @NonNull Parcelable.Creator<AudioProfile> CREATOR =
+ new Parcelable.Creator<AudioProfile>() {
+ /**
+ * Rebuilds an AudioProfile previously stored with writeToParcel().
+ * @param p Parcel object to read the AudioProfile from
+ * @return a new AudioProfile created from the data in the parcel
+ */
+ public AudioProfile createFromParcel(Parcel p) {
+ return new AudioProfile(p);
+ }
+
+ public AudioProfile[] newArray(int size) {
+ return new AudioProfile[size];
+ }
+ };
}
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 1b46a50..536b4ad 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -26,10 +26,12 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.media.audio.common.AidlConversion;
import android.media.audiofx.AudioEffect;
import android.media.audiopolicy.AudioMix;
import android.os.Build;
import android.os.IBinder;
+import android.os.Parcel;
import android.os.Vibrator;
import android.telephony.TelephonyManager;
import android.util.Log;
@@ -1555,9 +1557,24 @@
* {@link #AUDIO_STATUS_ERROR} or {@link #AUDIO_STATUS_SERVER_DIED}
*/
@UnsupportedAppUsage
- public static native int setDeviceConnectionState(int device, int state,
- String device_address, String device_name,
- int codecFormat);
+ public static int setDeviceConnectionState(AudioDeviceAttributes attributes, int state,
+ int codecFormat) {
+ android.media.audio.common.AudioPort port =
+ AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes);
+ Parcel parcel = Parcel.obtain();
+ port.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ try {
+ return setDeviceConnectionState(state, parcel, codecFormat);
+ } finally {
+ parcel.recycle();
+ }
+ }
+ /**
+ * @hide
+ */
+ @UnsupportedAppUsage
+ public static native int setDeviceConnectionState(int state, Parcel parcel, int codecFormat);
/** @hide */
@UnsupportedAppUsage
public static native int getDeviceConnectionState(int device, String device_address);
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 86dc6e0..c148846 100644
--- a/media/java/android/media/BtProfileConnectionInfo.java
+++ b/media/java/android/media/BluetoothProfileConnectionInfo.java
@@ -26,14 +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;
@@ -44,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];
}
};
@@ -83,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);
}
/**
@@ -95,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);
}
/**
@@ -105,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);
}
/**
@@ -118,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);
}
/**
@@ -135,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;
}
@@ -152,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..fec14de 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;
@@ -215,8 +215,7 @@
IRingtonePlayer getRingtonePlayer();
int getUiSoundsStreamType();
- void setWiredDeviceConnectionState(int type, int state, String address, String name,
- String caller);
+ void setWiredDeviceConnectionState(in AudioDeviceAttributes aa, int state, String caller);
@UnsupportedAppUsage
AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
@@ -276,7 +275,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/RouteDiscoveryPreference.java b/media/java/android/media/RouteDiscoveryPreference.java
index 0045018..e609226 100644
--- a/media/java/android/media/RouteDiscoveryPreference.java
+++ b/media/java/android/media/RouteDiscoveryPreference.java
@@ -67,7 +67,7 @@
@NonNull
private final List<String> mRequiredFeatures;
@NonNull
- private final List<String> mPackagesOrder;
+ private final List<String> mPackageOrder;
@NonNull
private final List<String> mAllowedPackages;
@@ -86,7 +86,7 @@
RouteDiscoveryPreference(@NonNull Builder builder) {
mPreferredFeatures = builder.mPreferredFeatures;
mRequiredFeatures = builder.mRequiredFeatures;
- mPackagesOrder = builder.mPackageOrder;
+ mPackageOrder = builder.mPackageOrder;
mAllowedPackages = builder.mAllowedPackages;
mShouldPerformActiveScan = builder.mActiveScan;
mExtras = builder.mExtras;
@@ -95,7 +95,7 @@
RouteDiscoveryPreference(@NonNull Parcel in) {
mPreferredFeatures = in.createStringArrayList();
mRequiredFeatures = in.createStringArrayList();
- mPackagesOrder = in.createStringArrayList();
+ mPackageOrder = in.createStringArrayList();
mAllowedPackages = in.createStringArrayList();
mShouldPerformActiveScan = in.readBoolean();
mExtras = in.readBundle();
@@ -144,7 +144,7 @@
*/
@NonNull
public List<String> getDeduplicationPackageOrder() {
- return mPackagesOrder;
+ return mPackageOrder;
}
/**
@@ -175,7 +175,7 @@
* @see #getDeduplicationPackageOrder()
*/
public boolean shouldRemoveDuplicates() {
- return !mPackagesOrder.isEmpty();
+ return !mPackageOrder.isEmpty();
}
/**
@@ -194,7 +194,7 @@
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeStringList(mPreferredFeatures);
dest.writeStringList(mRequiredFeatures);
- dest.writeStringList(mPackagesOrder);
+ dest.writeStringList(mPackageOrder);
dest.writeStringList(mAllowedPackages);
dest.writeBoolean(mShouldPerformActiveScan);
dest.writeBundle(mExtras);
@@ -225,14 +225,14 @@
RouteDiscoveryPreference other = (RouteDiscoveryPreference) o;
return Objects.equals(mPreferredFeatures, other.mPreferredFeatures)
&& Objects.equals(mRequiredFeatures, other.mRequiredFeatures)
- && Objects.equals(mPackagesOrder, other.mPackagesOrder)
+ && Objects.equals(mPackageOrder, other.mPackageOrder)
&& Objects.equals(mAllowedPackages, other.mAllowedPackages)
&& mShouldPerformActiveScan == other.mShouldPerformActiveScan;
}
@Override
public int hashCode() {
- return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackagesOrder, mAllowedPackages,
+ return Objects.hash(mPreferredFeatures, mRequiredFeatures, mPackageOrder, mAllowedPackages,
mShouldPerformActiveScan);
}
@@ -271,21 +271,31 @@
}
/**
- * A constructor to combine all the preferences into a single preference.
- * It ignores extras of preferences.
+ * A constructor to combine multiple preferences into a single preference. The combined
+ * preference will discover a superset of the union of the routes discoverable by each of
+ * the individual preferences.
+ * <p>
+ * When routes need to be discovered for multiple preferences, the combined preference can
+ * be used to query route providers once and obtain all routes of interest. The obtained
+ * routes can then be filtered for each of the individual preferences. This is typically
+ * more efficient than querying route providers with each of the individual preferences.
*
* @hide
*/
public Builder(@NonNull Collection<RouteDiscoveryPreference> preferences) {
Objects.requireNonNull(preferences, "preferences must not be null");
- Set<String> routeFeatureSet = new HashSet<>();
- mActiveScan = false;
+ Set<String> preferredFeatures = new HashSet<>();
+ boolean activeScan = false;
for (RouteDiscoveryPreference preference : preferences) {
- routeFeatureSet.addAll(preference.mPreferredFeatures);
- mActiveScan |= preference.mShouldPerformActiveScan;
+ preferredFeatures.addAll(preference.mPreferredFeatures);
+ activeScan |= preference.mShouldPerformActiveScan;
}
- mPreferredFeatures = new ArrayList<>(routeFeatureSet);
+ mPreferredFeatures = new ArrayList<>(preferredFeatures);
+ mRequiredFeatures = List.of();
+ mPackageOrder = List.of();
+ mAllowedPackages = List.of();
+ mActiveScan = activeScan;
}
/**
diff --git a/media/java/android/media/audio/common/AidlConversion.java b/media/java/android/media/audio/common/AidlConversion.java
index 1053fb7..f17189d 100644
--- a/media/java/android/media/audio/common/AidlConversion.java
+++ b/media/java/android/media/audio/common/AidlConversion.java
@@ -17,12 +17,17 @@
package android.media.audio.common;
import android.annotation.NonNull;
+import android.media.AudioDescriptor;
+import android.media.AudioDeviceAttributes;
import android.media.AudioFormat;
+import android.media.AudioSystem;
import android.media.MediaFormat;
import android.os.Parcel;
import com.android.internal.annotations.VisibleForTesting;
+import java.util.stream.Collectors;
+
/**
* This class provides utility functions for converting between
* the AIDL types defined in 'android.media.audio.common' and:
@@ -525,6 +530,351 @@
}
}
+ /**
+ * Convert from SDK AudioDeviceAttributes to AIDL AudioPort.
+ */
+ public static AudioPort api2aidl_AudioDeviceAttributes_AudioPort(
+ @NonNull AudioDeviceAttributes attributes) {
+ AudioPort port = new AudioPort();
+ port.name = attributes.getName();
+ // TO DO: b/211611504 Convert attributes.getAudioProfiles() to AIDL as well.
+ port.profiles = new AudioProfile[]{};
+ port.extraAudioDescriptors = attributes.getAudioDescriptors().stream()
+ .map(descriptor -> api2aidl_AudioDescriptor_ExtraAudioDescriptor(descriptor))
+ .collect(Collectors.toList()).toArray(ExtraAudioDescriptor[]::new);
+ port.flags = new AudioIoFlags();
+ port.gains = new AudioGain[]{};
+ AudioPortDeviceExt deviceExt = new AudioPortDeviceExt();
+ deviceExt.device = new AudioDevice();
+ deviceExt.encodedFormats = new AudioFormatDescription[]{};
+ deviceExt.device.type =
+ api2aidl_NativeType_AudioDeviceDescription(attributes.getInternalType());
+ deviceExt.device.address = AudioDeviceAddress.id(attributes.getAddress());
+ port.ext = AudioPortExt.device(deviceExt);
+ return port;
+ }
+
+ /**
+ * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor.
+ */
+ public static ExtraAudioDescriptor api2aidl_AudioDescriptor_ExtraAudioDescriptor(
+ @NonNull AudioDescriptor descriptor) {
+ ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor();
+ extraDescriptor.standard =
+ api2aidl_AudioDescriptorStandard_AudioStandard(descriptor.getStandard());
+ extraDescriptor.audioDescriptor = descriptor.getDescriptor();
+ extraDescriptor.encapsulationType =
+ api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType(
+ descriptor.getEncapsulationType());
+ return extraDescriptor;
+ }
+
+ /**
+ * Convert from SDK AudioDescriptor to AIDL ExtraAudioDescriptor.
+ */
+ public static @NonNull AudioDescriptor aidl2api_ExtraAudioDescriptor_AudioDescriptor(
+ @NonNull ExtraAudioDescriptor extraDescriptor) {
+ AudioDescriptor descriptor = new AudioDescriptor(
+ aidl2api_AudioStandard_AudioDescriptorStandard(extraDescriptor.standard),
+ aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType(
+ extraDescriptor.encapsulationType),
+ extraDescriptor.audioDescriptor);
+ return descriptor;
+ }
+
+ /**
+ * Convert from SDK AudioDescriptor#mStandard to AIDL AudioStandard
+ */
+ @AudioStandard
+ public static int api2aidl_AudioDescriptorStandard_AudioStandard(
+ @AudioDescriptor.AudioDescriptorStandard int standard) {
+ switch (standard) {
+ case AudioDescriptor.STANDARD_EDID:
+ return AudioStandard.EDID;
+ case AudioDescriptor.STANDARD_NONE:
+ default:
+ return AudioStandard.NONE;
+ }
+ }
+
+ /**
+ * Convert from AIDL AudioStandard to SDK AudioDescriptor#mStandard
+ */
+ @AudioDescriptor.AudioDescriptorStandard
+ public static int aidl2api_AudioStandard_AudioDescriptorStandard(@AudioStandard int standard) {
+ switch (standard) {
+ case AudioStandard.EDID:
+ return AudioDescriptor.STANDARD_EDID;
+ case AudioStandard.NONE:
+ default:
+ return AudioDescriptor.STANDARD_NONE;
+ }
+ }
+
+ /**
+ * Convert from SDK AudioProfile.EncapsulationType to AIDL AudioEncapsulationType
+ */
+ @AudioEncapsulationType
+ public static int api2aidl_AudioProfileEncapsulationType_AudioEncapsulationType(
+ @android.media.AudioProfile.EncapsulationType int type) {
+ switch (type) {
+ case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937:
+ return AudioEncapsulationType.IEC61937;
+ case android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE:
+ default:
+ return AudioEncapsulationType.NONE;
+ }
+ }
+
+ /**
+ * Convert from AIDL AudioEncapsulationType to SDK AudioProfile.EncapsulationType
+ */
+ @android.media.AudioProfile.EncapsulationType
+ public static int aidl2api_AudioEncapsulationType_AudioProfileEncapsulationType(
+ @AudioEncapsulationType int type) {
+ switch (type) {
+ case AudioEncapsulationType.IEC61937:
+ return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937;
+ case AudioEncapsulationType.NONE:
+ default:
+ return android.media.AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE;
+ }
+ }
+
+ /**
+ * Convert from SDK native type to AIDL AudioDeviceDescription
+ */
+ public static AudioDeviceDescription api2aidl_NativeType_AudioDeviceDescription(
+ int nativeType) {
+ AudioDeviceDescription aidl = new AudioDeviceDescription();
+ aidl.connection = "";
+ switch (nativeType) {
+ case AudioSystem.DEVICE_OUT_EARPIECE:
+ aidl.type = AudioDeviceType.OUT_SPEAKER_EARPIECE;
+ break;
+ case AudioSystem.DEVICE_OUT_SPEAKER:
+ aidl.type = AudioDeviceType.OUT_SPEAKER;
+ break;
+ case AudioSystem.DEVICE_OUT_WIRED_HEADPHONE:
+ aidl.type = AudioDeviceType.OUT_HEADPHONE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+ aidl.type = AudioDeviceType.OUT_CARKIT;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
+ aidl.type = AudioDeviceType.OUT_HEADPHONE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+ aidl.type = AudioDeviceType.OUT_SPEAKER;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP;
+ break;
+ case AudioSystem.DEVICE_OUT_TELEPHONY_TX:
+ aidl.type = AudioDeviceType.OUT_TELEPHONY_TX;
+ break;
+ case AudioSystem.DEVICE_OUT_AUX_LINE:
+ aidl.type = AudioDeviceType.OUT_LINE_AUX;
+ break;
+ case AudioSystem.DEVICE_OUT_SPEAKER_SAFE:
+ aidl.type = AudioDeviceType.OUT_SPEAKER_SAFE;
+ break;
+ case AudioSystem.DEVICE_OUT_HEARING_AID:
+ aidl.type = AudioDeviceType.OUT_HEARING_AID;
+ aidl.connection = AudioDeviceDescription.CONNECTION_WIRELESS;
+ break;
+ case AudioSystem.DEVICE_OUT_ECHO_CANCELLER:
+ aidl.type = AudioDeviceType.OUT_ECHO_CANCELLER;
+ break;
+ case AudioSystem.DEVICE_OUT_BLE_SPEAKER:
+ aidl.type = AudioDeviceType.OUT_SPEAKER;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE;
+ break;
+ case AudioSystem.DEVICE_IN_BUILTIN_MIC:
+ aidl.type = AudioDeviceType.IN_MICROPHONE;
+ break;
+ case AudioSystem.DEVICE_IN_BACK_MIC:
+ aidl.type = AudioDeviceType.IN_MICROPHONE_BACK;
+ break;
+ case AudioSystem.DEVICE_IN_TELEPHONY_RX:
+ aidl.type = AudioDeviceType.IN_TELEPHONY_RX;
+ break;
+ case AudioSystem.DEVICE_IN_TV_TUNER:
+ aidl.type = AudioDeviceType.IN_TV_TUNER;
+ break;
+ case AudioSystem.DEVICE_IN_LOOPBACK:
+ aidl.type = AudioDeviceType.IN_LOOPBACK;
+ break;
+ case AudioSystem.DEVICE_IN_BLUETOOTH_BLE:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE;
+ break;
+ case AudioSystem.DEVICE_IN_ECHO_REFERENCE:
+ aidl.type = AudioDeviceType.IN_ECHO_REFERENCE;
+ break;
+ case AudioSystem.DEVICE_IN_WIRED_HEADSET:
+ aidl.type = AudioDeviceType.IN_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_OUT_WIRED_HEADSET:
+ aidl.type = AudioDeviceType.OUT_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_IN_BLUETOOTH_SCO_HEADSET:
+ aidl.type = AudioDeviceType.IN_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+ aidl.type = AudioDeviceType.OUT_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_SCO;
+ break;
+ case AudioSystem.DEVICE_IN_HDMI:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI;
+ break;
+ case AudioSystem.DEVICE_OUT_HDMI:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI;
+ break;
+ case AudioSystem.DEVICE_IN_REMOTE_SUBMIX:
+ aidl.type = AudioDeviceType.IN_SUBMIX;
+ break;
+ case AudioSystem.DEVICE_OUT_REMOTE_SUBMIX:
+ aidl.type = AudioDeviceType.OUT_SUBMIX;
+ break;
+ case AudioSystem.DEVICE_IN_ANLG_DOCK_HEADSET:
+ aidl.type = AudioDeviceType.IN_DOCK;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET:
+ aidl.type = AudioDeviceType.OUT_DOCK;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_IN_DGTL_DOCK_HEADSET:
+ aidl.type = AudioDeviceType.IN_DOCK;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET:
+ aidl.type = AudioDeviceType.OUT_DOCK;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_IN_USB_ACCESSORY:
+ aidl.type = AudioDeviceType.IN_ACCESSORY;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_OUT_USB_ACCESSORY:
+ aidl.type = AudioDeviceType.OUT_ACCESSORY;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_IN_USB_DEVICE:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_OUT_USB_DEVICE:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_IN_FM_TUNER:
+ aidl.type = AudioDeviceType.IN_FM_TUNER;
+ break;
+ case AudioSystem.DEVICE_OUT_FM:
+ aidl.type = AudioDeviceType.OUT_FM;
+ break;
+ case AudioSystem.DEVICE_IN_LINE:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_OUT_LINE:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_ANALOG;
+ break;
+ case AudioSystem.DEVICE_IN_SPDIF:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF;
+ break;
+ case AudioSystem.DEVICE_OUT_SPDIF:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_SPDIF;
+ break;
+ case AudioSystem.DEVICE_IN_BLUETOOTH_A2DP:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP;
+ break;
+ case AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_A2DP;
+ break;
+ case AudioSystem.DEVICE_IN_IP:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4;
+ break;
+ case AudioSystem.DEVICE_OUT_IP:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_IP_V4;
+ break;
+ case AudioSystem.DEVICE_IN_BUS:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BUS;
+ break;
+ case AudioSystem.DEVICE_OUT_BUS:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BUS;
+ break;
+ case AudioSystem.DEVICE_IN_PROXY:
+ aidl.type = AudioDeviceType.IN_AFE_PROXY;
+ break;
+ case AudioSystem.DEVICE_OUT_PROXY:
+ aidl.type = AudioDeviceType.OUT_AFE_PROXY;
+ break;
+ case AudioSystem.DEVICE_IN_USB_HEADSET:
+ aidl.type = AudioDeviceType.IN_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_OUT_USB_HEADSET:
+ aidl.type = AudioDeviceType.OUT_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_USB;
+ break;
+ case AudioSystem.DEVICE_IN_HDMI_ARC:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC;
+ break;
+ case AudioSystem.DEVICE_OUT_HDMI_ARC:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_ARC;
+ break;
+ case AudioSystem.DEVICE_IN_HDMI_EARC:
+ aidl.type = AudioDeviceType.IN_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC;
+ break;
+ case AudioSystem.DEVICE_OUT_HDMI_EARC:
+ aidl.type = AudioDeviceType.OUT_DEVICE;
+ aidl.connection = AudioDeviceDescription.CONNECTION_HDMI_EARC;
+ break;
+ case AudioSystem.DEVICE_IN_BLE_HEADSET:
+ aidl.type = AudioDeviceType.IN_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE;
+ break;
+ case AudioSystem.DEVICE_OUT_BLE_HEADSET:
+ aidl.type = AudioDeviceType.OUT_HEADSET;
+ aidl.connection = AudioDeviceDescription.CONNECTION_BT_LE;
+ break;
+ case AudioSystem.DEVICE_IN_DEFAULT:
+ aidl.type = AudioDeviceType.IN_DEFAULT;
+ break;
+ case AudioSystem.DEVICE_OUT_DEFAULT:
+ aidl.type = AudioDeviceType.OUT_DEFAULT;
+ break;
+ default:
+ aidl.type = AudioDeviceType.NONE;
+ }
+ return aidl;
+ }
+
private static native int aidl2legacy_AudioChannelLayout_Parcel_audio_channel_mask_t(
Parcel aidl, boolean isInput);
private static native Parcel legacy2aidl_audio_channel_mask_t_AudioChannelLayout_Parcel(
diff --git a/media/java/android/media/tv/BroadcastInfoRequest.java b/media/java/android/media/tv/BroadcastInfoRequest.java
index f7a52f2..cbd8c1f 100644
--- a/media/java/android/media/tv/BroadcastInfoRequest.java
+++ b/media/java/android/media/tv/BroadcastInfoRequest.java
@@ -56,6 +56,8 @@
switch (type) {
case TvInputManager.BROADCAST_INFO_TYPE_TS:
return TsRequest.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TABLE:
+ return TableRequest.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_SECTION:
return SectionRequest.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_PES:
diff --git a/media/java/android/media/tv/BroadcastInfoResponse.java b/media/java/android/media/tv/BroadcastInfoResponse.java
index ff4ec15..4ba2b2c 100644
--- a/media/java/android/media/tv/BroadcastInfoResponse.java
+++ b/media/java/android/media/tv/BroadcastInfoResponse.java
@@ -57,6 +57,8 @@
switch (type) {
case TvInputManager.BROADCAST_INFO_TYPE_TS:
return TsResponse.createFromParcelBody(source);
+ case TvInputManager.BROADCAST_INFO_TYPE_TABLE:
+ return TableResponse.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_SECTION:
return SectionResponse.createFromParcelBody(source);
case TvInputManager.BROADCAST_INFO_TYPE_PES:
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..a3e731b 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 {}
@@ -625,21 +623,21 @@
* <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise
* be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
*
- * @param delayInMs specifies the duration of the delay in milliseconds.
+ * @param durationInMs specifies the duration of the delay in milliseconds.
* @return one of the following results: {@link Tuner#RESULT_SUCCESS},
* {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
* {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
* {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
*/
- public int delayCallbackUntilMillisElapsed(long delayInMs) {
+ public int delayCallbackForDurationMillis(long durationInMs) {
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
return Tuner.RESULT_UNAVAILABLE;
}
- if (delayInMs >= 0 && delayInMs <= Integer.MAX_VALUE) {
+ if (durationInMs >= 0 && durationInMs <= Integer.MAX_VALUE) {
synchronized (mLock) {
- return nativeSetTimeDelayHint((int) delayInMs);
+ return nativeSetTimeDelayHint((int) durationInMs);
}
}
return Tuner.RESULT_INVALID_ARGUMENT;
@@ -655,20 +653,20 @@
* <p>This functionality is only available in Tuner version 2.0 and higher and will otherwise
* be a no-op. Use {@link TunerVersionChecker#getTunerVersion()} to get the version information.
*
- * @param delayInBytes specifies the duration of the delay in bytes.
+ * @param bytesAccumulated specifies the delay condition in bytes.
* @return one of the following results: {@link Tuner#RESULT_SUCCESS},
* {@link Tuner#RESULT_UNAVAILABLE}, {@link Tuner#RESULT_NOT_INITIALIZED},
* {@link Tuner#RESULT_INVALID_STATE}, {@link Tuner#RESULT_INVALID_ARGUMENT},
* {@link Tuner#RESULT_OUT_OF_MEMORY}, or {@link Tuner#RESULT_UNKNOWN_ERROR}.
*/
- public int delayCallbackUntilBytesAccumulated(int delayInBytes) {
+ public int delayCallbackUntilBytesAccumulated(int bytesAccumulated) {
if (!TunerVersionChecker.checkHigherOrEqualVersionTo(
TunerVersionChecker.TUNER_VERSION_2_0, "setTimeDelayHint")) {
return Tuner.RESULT_UNAVAILABLE;
}
synchronized (mLock) {
- return nativeSetDataSizeDelayHint(delayInBytes);
+ return nativeSetDataSizeDelayHint(bytesAccumulated);
}
}
}
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 c1e9b38..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/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index d8f48c2..20d711c 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -103,6 +103,7 @@
private int mDeviceType;
private String mHostType;
private boolean mSkipThumbForHost = false;
+ private volatile boolean mHostIsWindows = false;
private MtpServer mServer;
private MtpStorageManager mManager;
@@ -358,7 +359,7 @@
}
public void addStorage(StorageVolume storage) {
- MtpStorage mtpStorage = mManager.addMtpStorage(storage);
+ MtpStorage mtpStorage = mManager.addMtpStorage(storage, () -> mHostIsWindows);
mStorageMap.put(storage.getPath(), mtpStorage);
if (mServer != null) {
mServer.addStorage(mtpStorage);
@@ -413,6 +414,7 @@
}
mHostType = "";
mSkipThumbForHost = false;
+ mHostIsWindows = false;
}
@VisibleForNative
@@ -736,10 +738,12 @@
: MtpConstants.RESPONSE_GENERAL_ERROR);
case MtpConstants.DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO:
mHostType = stringValue;
+ Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
+ + "=" + stringValue);
if (stringValue.startsWith("Android/")) {
- Log.d(TAG, "setDeviceProperty." + Integer.toHexString(property)
- + "=" + stringValue);
mSkipThumbForHost = true;
+ } else if (stringValue.startsWith("Windows/")) {
+ mHostIsWindows = true;
}
return MtpConstants.RESPONSE_OK;
}
diff --git a/media/java/android/mtp/MtpStorage.java b/media/java/android/mtp/MtpStorage.java
index 88c32a3..a3754e90 100644
--- a/media/java/android/mtp/MtpStorage.java
+++ b/media/java/android/mtp/MtpStorage.java
@@ -19,6 +19,8 @@
import android.compat.annotation.UnsupportedAppUsage;
import android.os.storage.StorageVolume;
+import java.util.function.Supplier;
+
/**
* This class represents a storage unit on an MTP device.
* Used only for MTP support in USB responder mode.
@@ -33,14 +35,16 @@
private final boolean mRemovable;
private final long mMaxFileSize;
private final String mVolumeName;
+ private final Supplier<Boolean> mIsHostWindows;
- public MtpStorage(StorageVolume volume, int storageId) {
+ public MtpStorage(StorageVolume volume, int storageId, Supplier<Boolean> isHostWindows) {
mStorageId = storageId;
mPath = volume.getPath();
mDescription = volume.getDescription(null);
mRemovable = volume.isRemovable();
mMaxFileSize = volume.getMaxFileSize();
mVolumeName = volume.getMediaStoreVolumeName();
+ mIsHostWindows = isHostWindows;
}
/**
@@ -93,4 +97,13 @@
public String getVolumeName() {
return mVolumeName;
}
+
+ /**
+ * Returns true if the mtp host of this storage is Windows.
+ *
+ * @return is host Windows
+ */
+ public boolean isHostWindows() {
+ return mIsHostWindows.get();
+ }
}
diff --git a/media/java/android/mtp/MtpStorageManager.java b/media/java/android/mtp/MtpStorageManager.java
index 0bede0d..e9426cf 100644
--- a/media/java/android/mtp/MtpStorageManager.java
+++ b/media/java/android/mtp/MtpStorageManager.java
@@ -18,7 +18,11 @@
import android.media.MediaFile;
import android.os.FileObserver;
+import android.os.SystemProperties;
import android.os.storage.StorageVolume;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.system.StructStat;
import android.util.Log;
import com.android.internal.util.Preconditions;
@@ -35,6 +39,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.function.Supplier;
/**
* MtpStorageManager provides functionality for listing, tracking, and notifying MtpServer of
@@ -199,7 +204,38 @@
}
public long getSize() {
- return mIsDir ? 0 : getPath().toFile().length();
+ return mIsDir ? 0 : maybeApplyTranscodeLengthWorkaround(getPath().toFile().length());
+ }
+
+ private long maybeApplyTranscodeLengthWorkaround(long length) {
+ // Windows truncates transferred files to the size advertised in the object property.
+ if (mStorage.isHostWindows() && isTranscodeMtpEnabled() && isFileTranscodeSupported()) {
+ // If the file supports transcoding, we double the returned size to accommodate
+ // the increase in size from transcoding to AVC. This is the same heuristic
+ // applied in the FUSE daemon (MediaProvider).
+ return length * 2;
+ }
+ return length;
+ }
+
+ private boolean isTranscodeMtpEnabled() {
+ return SystemProperties.getBoolean("sys.fuse.transcode_mtp", false);
+ }
+
+ private boolean isFileTranscodeSupported() {
+ // Check if the file supports transcoding by reading the |st_nlinks| struct stat
+ // field. This will be > 1 if the file supports transcoding. The FUSE daemon
+ // sets the field accordingly to enable the MTP stack workaround some Windows OS
+ // MTP client bug where they ignore the size returned as part of getting the MTP
+ // object, see MtpServer#doGetObject.
+ final Path path = getPath();
+ try {
+ StructStat stat = Os.stat(path.toString());
+ return stat.st_nlink > 1;
+ } catch (ErrnoException e) {
+ Log.w(TAG, "Failed to stat path: " + getPath() + ". Ignoring transcoding.");
+ return false;
+ }
}
public Path getPath() {
@@ -420,10 +456,12 @@
* @param volume Storage to add.
* @return the associated MtpStorage
*/
- public synchronized MtpStorage addMtpStorage(StorageVolume volume) {
+ public synchronized MtpStorage addMtpStorage(StorageVolume volume,
+ Supplier<Boolean> isHostWindows) {
int storageId = ((getNextStorageId() & 0x0000FFFF) << 16) + 1;
- MtpStorage storage = new MtpStorage(volume, storageId);
- MtpObject root = new MtpObject(storage.getPath(), storageId, storage, null, true);
+ MtpStorage storage = new MtpStorage(volume, storageId, isHostWindows);
+ MtpObject root = new MtpObject(storage.getPath(), storageId, storage, /* parent= */ null,
+ /* isDir= */ true);
mRoots.put(storageId, root);
return storage;
}
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/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
index abdc7e5..a6a5568 100644
--- a/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
+++ b/media/tests/MtpTests/src/android/mtp/MtpStorageManagerTest.java
@@ -160,10 +160,11 @@
Log.d(TAG, "sendObjectInfoChanged: " + id);
objectsInfoChanged.add(id);
}
- }, null);
+ }, /* subdirectories= */ null);
- mainMtpStorage = manager.addMtpStorage(mainStorage);
- secondaryMtpStorage = manager.addMtpStorage(secondaryStorage);
+ mainMtpStorage = manager.addMtpStorage(mainStorage, /* isHostWindows= */ () -> false);
+ secondaryMtpStorage = manager.addMtpStorage(secondaryStorage,
+ /* isHostWindows= */ () -> false);
}
@After
diff --git a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
index 414de89..0957390 100644
--- a/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
+++ b/media/tests/aidltests/src/com/android/media/AidlConversionUnitTests.java
@@ -16,8 +16,18 @@
package android.media.audio.common;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assert.assertTrue;
+
import android.media.AudioAttributes;
+import android.media.AudioDescriptor;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
import android.media.AudioFormat;
+import android.media.AudioProfile;
import android.media.AudioSystem;
import android.media.AudioTrack;
import android.media.MediaFormat;
@@ -25,11 +35,12 @@
import androidx.test.runner.AndroidJUnit4;
-import static org.junit.Assert.*;
-
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.ArrayList;
+import java.util.Arrays;
+
/**
* Unit tests for AidlConversion utilities.
*
@@ -417,10 +428,102 @@
() -> AidlConversion.legacy2aidl_audio_usage_t_AudioUsage(sInvalidValue));
}
+ @Test
+ public void testAudioDescriptorConversion_Default() {
+ ExtraAudioDescriptor aidl = createDefaultDescriptor();
+ AudioDescriptor audioDescriptor =
+ AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl);
+ assertEquals(AudioDescriptor.STANDARD_NONE, audioDescriptor.getStandard());
+ assertEquals(
+ AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, audioDescriptor.getEncapsulationType());
+ assertTrue(Arrays.equals(new byte[]{}, audioDescriptor.getDescriptor()));
+
+ ExtraAudioDescriptor reconstructedExtraDescriptor =
+ AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor);
+ assertEquals(aidl, reconstructedExtraDescriptor);
+ }
+
+ @Test
+ public void testAudioDescriptorConversion() {
+ ExtraAudioDescriptor aidl = createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A});
+ AudioDescriptor audioDescriptor =
+ AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(aidl);
+ assertEquals(AudioDescriptor.STANDARD_EDID, audioDescriptor.getStandard());
+ assertEquals(AudioProfile.AUDIO_ENCAPSULATION_TYPE_IEC61937,
+ audioDescriptor.getEncapsulationType());
+ assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A}, audioDescriptor.getDescriptor()));
+
+ ExtraAudioDescriptor reconstructedExtraDescriptor =
+ AidlConversion.api2aidl_AudioDescriptor_ExtraAudioDescriptor(audioDescriptor);
+ assertEquals(aidl, reconstructedExtraDescriptor);
+ }
+
+ @Test
+ public void testAudioDeviceAttributesConversion_Default() {
+ AudioDeviceAttributes attributes =
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_DEFAULT, "myAddress");
+ AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(attributes);
+ assertEquals("", port.name);
+ assertEquals(0, port.extraAudioDescriptors.length);
+ assertEquals("myAddress", port.ext.getDevice().device.address.getId());
+ assertEquals("", port.ext.getDevice().device.type.connection);
+ assertEquals(AudioDeviceType.OUT_DEFAULT, port.ext.getDevice().device.type.type);
+ }
+
+ @Test
+ public void testAudioDeviceAttributesConversion() {
+ AudioDescriptor audioDescriptor1 =
+ AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(
+ createEncapsulationDescriptor(new byte[]{0x05, 0x18, 0x4A}));
+
+ AudioDescriptor audioDescriptor2 =
+ AidlConversion.aidl2api_ExtraAudioDescriptor_AudioDescriptor(
+ createDefaultDescriptor());
+
+ AudioDeviceAttributes attributes =
+ new AudioDeviceAttributes(AudioDeviceAttributes.ROLE_OUTPUT,
+ AudioDeviceInfo.TYPE_HDMI_ARC, "myAddress", "myName", new ArrayList<>(),
+ new ArrayList<>(Arrays.asList(audioDescriptor1, audioDescriptor2)));
+ AudioPort port = AidlConversion.api2aidl_AudioDeviceAttributes_AudioPort(
+ attributes);
+ assertEquals("myName", port.name);
+ assertEquals(2, port.extraAudioDescriptors.length);
+ assertEquals(AudioStandard.EDID, port.extraAudioDescriptors[0].standard);
+ assertEquals(AudioEncapsulationType.IEC61937,
+ port.extraAudioDescriptors[0].encapsulationType);
+ assertTrue(Arrays.equals(new byte[]{0x05, 0x18, 0x4A},
+ port.extraAudioDescriptors[0].audioDescriptor));
+ assertEquals(AudioStandard.NONE, port.extraAudioDescriptors[1].standard);
+ assertEquals(AudioEncapsulationType.NONE,
+ port.extraAudioDescriptors[1].encapsulationType);
+ assertTrue(Arrays.equals(new byte[]{},
+ port.extraAudioDescriptors[1].audioDescriptor));
+ assertEquals("myAddress", port.ext.getDevice().device.address.getId());
+ assertEquals(AudioDeviceDescription.CONNECTION_HDMI_ARC,
+ port.ext.getDevice().device.type.connection);
+ assertEquals(AudioDeviceType.OUT_DEVICE, port.ext.getDevice().device.type.type);
+ }
+
private static AudioFormatDescription createPcm16FormatAidl() {
final AudioFormatDescription aidl = new AudioFormatDescription();
aidl.type = AudioFormatType.PCM;
aidl.pcm = PcmType.INT_16_BIT;
return aidl;
}
+
+ private static ExtraAudioDescriptor createDefaultDescriptor() {
+ ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor();
+ extraDescriptor.standard = AudioStandard.NONE;
+ extraDescriptor.encapsulationType = AudioEncapsulationType.NONE;
+ extraDescriptor.audioDescriptor = new byte[]{};
+ return extraDescriptor;
+ }
+
+ private static ExtraAudioDescriptor createEncapsulationDescriptor(byte[] audioDescriptor) {
+ ExtraAudioDescriptor extraDescriptor = new ExtraAudioDescriptor();
+ extraDescriptor.standard = AudioStandard.EDID;
+ extraDescriptor.encapsulationType = AudioEncapsulationType.IEC61937;
+ extraDescriptor.audioDescriptor = audioDescriptor;
+ return extraDescriptor;
+ }
}
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..6652780 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,8 +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 +165,8 @@
filegroup {
name: "framework-connectivity-tiramisu-updatable-sources",
srcs: [
+ ":framework-connectivity-ipsec-sources",
+ ":framework-connectivity-netstats-sources",
":framework-connectivity-nsd-sources",
":framework-connectivity-tiramisu-internal-sources",
],
@@ -194,15 +194,12 @@
"jni/onload.cpp",
],
shared_libs: [
+ "libandroid",
"liblog",
- ],
- static_libs: [
- "libnativehelper_compat_libc++",
+ "libnativehelper",
],
stl: "none",
apex_available: [
"com.android.tethering",
- // TODO: remove when ConnectivityT moves to APEX.
- "//apex_available:platform",
],
}
diff --git a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
index 5ce7e59..0414bb7 100644
--- a/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/app/usage/NetworkStatsManager.java
@@ -27,7 +27,6 @@
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.app.usage.NetworkStats.Bucket;
import android.compat.annotation.UnsupportedAppUsage;
@@ -192,9 +191,13 @@
}
}
- /** @hide */
+ /**
+ * Set poll force flag to indicate that calling any subsequent query method will force a stats
+ * poll.
+ * @hide
+ */
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
- @TestApi
+ @SystemApi(client = MODULE_LIBRARIES)
public void setPollForce(boolean pollForce) {
if (pollForce) {
mFlags |= FLAG_POLL_FORCE;
diff --git a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
index 630f902e..9bffbfb 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/ConnectivityFrameworkInitializerTiramisu.java
@@ -18,6 +18,7 @@
import android.annotation.SystemApi;
import android.app.SystemServiceRegistry;
+import android.app.usage.NetworkStatsManager;
import android.content.Context;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
@@ -48,5 +49,24 @@
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);
+ }
+ );
+
+ SystemServiceRegistry.registerContextAwareService(
+ Context.NETWORK_STATS_SERVICE,
+ NetworkStatsManager.class,
+ (context, serviceBinder) -> {
+ INetworkStatsService service =
+ INetworkStatsService.Stub.asInterface(serviceBinder);
+ return new NetworkStatsManager(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 ece54df..1f67f6d 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -19,12 +19,14 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
import android.annotation.SystemApi;
import android.annotation.SystemService;
import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.Build;
import android.os.RemoteException;
@@ -320,15 +322,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 +340,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");
}
@@ -358,11 +360,43 @@
return proxy;
}
- private void updateConfiguration(
+ /**
+ * Updates the configuration of an automotive device's ethernet network.
+ *
+ * The {@link EthernetNetworkUpdateRequest} {@code request} argument describes how to update the
+ * configuration for this network.
+ * Use {@link StaticIpConfiguration.Builder} to build a {@code StaticIpConfiguration} object for
+ * this network to put inside the {@code request}.
+ * Similarly, use {@link NetworkCapabilities.Builder} to build a {@code NetworkCapabilities}
+ * object for this network to put inside the {@code request}.
+ *
+ * If non-null, the listener will be called exactly once after this is called, unless
+ * a synchronous exception was thrown.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param request the {@link EthernetNetworkUpdateRequest} used to set an ethernet network's
+ * {@link StaticIpConfiguration} and {@link NetworkCapabilities} values.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device or on an
+ * unsupported interface.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public 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) {
+ Objects.requireNonNull(iface, "iface must be non-null");
+ Objects.requireNonNull(request, "request must be non-null");
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -372,10 +406,34 @@
}
}
- private void connectNetwork(
+ /**
+ * Set an ethernet network's link state up.
+ *
+ * When the link is successfully turned up, the listener will be called with the resulting
+ * network. If any error or unexpected condition happens while the system tries to turn the
+ * interface up, the listener will be called with an appropriate exception.
+ * The listener is guaranteed to be called exactly once for each call to this method, but this
+ * may take an unbounded amount of time depending on the actual network conditions.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public void connectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
+ Objects.requireNonNull(iface, "iface must be non-null");
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -385,10 +443,33 @@
}
}
- private void disconnectNetwork(
+ /**
+ * Set an ethernet network's link state down.
+ *
+ * When the link is successfully turned down, the listener will be called with the network that
+ * was torn down, if any. If any error or unexpected condition happens while the system tries to
+ * turn the interface down, the listener will be called with an appropriate exception.
+ * The listener is guaranteed to be called exactly once for each call to this method.
+ *
+ * @param iface the name of the interface to act upon.
+ * @param executor an {@link Executor} to execute the listener on. Optional if listener is null.
+ * @param listener an optional {@link BiConsumer} to listen for completion of the operation.
+ * @throws SecurityException if the process doesn't hold
+ * {@link android.Manifest.permission.MANAGE_ETHERNET_NETWORKS}.
+ * @throws UnsupportedOperationException if called on a non-automotive device.
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {
+ NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK,
+ android.Manifest.permission.NETWORK_STACK,
+ android.Manifest.permission.MANAGE_ETHERNET_NETWORKS})
+ @RequiresFeature(PackageManager.FEATURE_AUTOMOTIVE)
+ public void disconnectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
+ Objects.requireNonNull(iface, "iface must be non-null");
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/EthernetNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
new file mode 100644
index 0000000..a69cc55
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
@@ -0,0 +1,73 @@
+/*
+ * 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 android.net;
+
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Objects;
+
+/** @hide */
+@SystemApi
+public final class EthernetNetworkManagementException
+ extends RuntimeException implements Parcelable {
+
+ /* @hide */
+ public EthernetNetworkManagementException(@NonNull final String errorMessage) {
+ super(errorMessage);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(getMessage());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null || getClass() != obj.getClass()) return false;
+ final EthernetNetworkManagementException that = (EthernetNetworkManagementException) obj;
+
+ return Objects.equals(getMessage(), that.getMessage());
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeString(getMessage());
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @NonNull
+ public static final Parcelable.Creator<EthernetNetworkManagementException> CREATOR =
+ new Parcelable.Creator<EthernetNetworkManagementException>() {
+ @Override
+ public EthernetNetworkManagementException[] newArray(int size) {
+ return new EthernetNetworkManagementException[size];
+ }
+
+ @Override
+ 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 79%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index f42c4b7..e879e40 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -17,13 +17,15 @@
package android.net;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.Parcelable;
import java.util.Objects;
/** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
+@SystemApi
+public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
@NonNull
@@ -39,8 +41,7 @@
return new NetworkCapabilities(mNetworkCapabilities);
}
- /** @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 +49,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 +57,7 @@
@Override
public String toString() {
- return "InternalNetworkUpdateRequest{"
+ return "EthernetNetworkUpdateRequest{"
+ "mIpConfig=" + mIpConfig
+ ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
}
@@ -65,7 +66,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 +89,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/framework-t/src/android/net/InternalNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
deleted file mode 100644
index 7f4e403..0000000
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.java
+++ /dev/null
@@ -1,59 +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 android.net;
-
-import android.annotation.NonNull;
-import android.os.Parcel;
-import android.os.Parcelable;
-
-/** @hide */
-public final class InternalNetworkManagementException
- extends RuntimeException implements Parcelable {
-
- /* @hide */
- public InternalNetworkManagementException(@NonNull final Throwable t) {
- super(t);
- }
-
- private InternalNetworkManagementException(@NonNull final Parcel source) {
- super(source.readString());
- }
-
- @Override
- public void writeToParcel(@NonNull Parcel dest, int flags) {
- dest.writeString(getCause().getMessage());
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @NonNull
- public static final Parcelable.Creator<InternalNetworkManagementException> CREATOR =
- new Parcelable.Creator<InternalNetworkManagementException>() {
- @Override
- public InternalNetworkManagementException[] newArray(int size) {
- return new InternalNetworkManagementException[size];
- }
-
- @Override
- public InternalNetworkManagementException createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkManagementException(source);
- }
- };
-}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
index 56faa52..4ebaf2b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentity.java
@@ -184,14 +184,14 @@
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
- proto.write(NetworkIdentityProto.TYPE, mType);
+ proto.write(NetworkIdentityProto.TYPE_FIELD_NUMBER, mType);
// TODO: dump mRatType as well.
- proto.write(NetworkIdentityProto.ROAMING, mRoaming);
- proto.write(NetworkIdentityProto.METERED, mMetered);
- proto.write(NetworkIdentityProto.DEFAULT_NETWORK, mDefaultNetwork);
- proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK, mOemManaged);
+ proto.write(NetworkIdentityProto.ROAMING_FIELD_NUMBER, mRoaming);
+ proto.write(NetworkIdentityProto.METERED_FIELD_NUMBER, mMetered);
+ proto.write(NetworkIdentityProto.DEFAULT_NETWORK_FIELD_NUMBER, mDefaultNetwork);
+ proto.write(NetworkIdentityProto.OEM_MANAGED_NETWORK_FIELD_NUMBER, mOemManaged);
proto.end(start);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
index dfa347f..2236d70 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkIdentitySet.java
@@ -212,7 +212,7 @@
final long start = proto.start(tag);
for (NetworkIdentity ident : this) {
- ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES);
+ ident.dumpDebug(proto, NetworkIdentitySetProto.IDENTITIES_FIELD_NUMBER);
}
proto.end(start);
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
index 735c44d..67d48f0 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsCollection.java
@@ -732,19 +732,19 @@
final long start = proto.start(tag);
for (Key key : getSortedKeys()) {
- final long startStats = proto.start(NetworkStatsCollectionProto.STATS);
+ final long startStats = proto.start(NetworkStatsCollectionProto.STATS_FIELD_NUMBER);
// Key
- final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY);
- key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY);
- proto.write(NetworkStatsCollectionKeyProto.UID, key.uid);
- proto.write(NetworkStatsCollectionKeyProto.SET, key.set);
- proto.write(NetworkStatsCollectionKeyProto.TAG, key.tag);
+ final long startKey = proto.start(NetworkStatsCollectionStatsProto.KEY_FIELD_NUMBER);
+ key.ident.dumpDebug(proto, NetworkStatsCollectionKeyProto.IDENTITY_FIELD_NUMBER);
+ proto.write(NetworkStatsCollectionKeyProto.UID_FIELD_NUMBER, key.uid);
+ proto.write(NetworkStatsCollectionKeyProto.SET_FIELD_NUMBER, key.set);
+ proto.write(NetworkStatsCollectionKeyProto.TAG_FIELD_NUMBER, key.tag);
proto.end(startKey);
// Value
final NetworkStatsHistory history = mStats.get(key);
- history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY);
+ history.dumpDebug(proto, NetworkStatsCollectionStatsProto.HISTORY_FIELD_NUMBER);
proto.end(startStats);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
index 78c1370..822a16e 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkStatsHistory.java
@@ -915,17 +915,18 @@
public void dumpDebug(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
- proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS, bucketDuration);
+ proto.write(NetworkStatsHistoryProto.BUCKET_DURATION_MS_FIELD_NUMBER, bucketDuration);
for (int i = 0; i < bucketCount; i++) {
- final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS);
+ final long startBucket = proto.start(NetworkStatsHistoryProto.BUCKETS_FIELD_NUMBER);
- proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS, bucketStart[i]);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES, rxBytes, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS, rxPackets, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES, txBytes, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS, txPackets, i);
- dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS, operations, i);
+ proto.write(NetworkStatsHistoryBucketProto.BUCKET_START_MS_FIELD_NUMBER,
+ bucketStart[i]);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_BYTES_FIELD_NUMBER, rxBytes, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.RX_PACKETS_FIELD_NUMBER, rxPackets, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_BYTES_FIELD_NUMBER, txBytes, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.TX_PACKETS_FIELD_NUMBER, txPackets, i);
+ dumpDebug(proto, NetworkStatsHistoryBucketProto.OPERATIONS_FIELD_NUMBER, operations, i);
proto.end(startBucket);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
index 9b58b01..7b5afd7 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/NetworkTemplate.java
@@ -79,7 +79,8 @@
MATCH_WIFI,
MATCH_ETHERNET,
MATCH_BLUETOOTH,
- MATCH_CARRIER
+ MATCH_PROXY,
+ MATCH_CARRIER,
})
public @interface TemplateMatchRule{}
@@ -104,9 +105,8 @@
/** Match rule to match bluetooth networks. */
public static final int MATCH_BLUETOOTH = 8;
/**
- * Match rule to match networks with {@link Connectivity#TYPE_PROXY} as the legacy network type.
- *
- * @hide
+ * Match rule to match networks with {@link ConnectivityManager#TYPE_PROXY} as the legacy
+ * network type.
*/
public static final int MATCH_PROXY = 9;
/**
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
index 0f21e55..512fbce 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdManager.java
@@ -16,6 +16,9 @@
package android.net.nsd;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.SdkConstant;
import android.annotation.SdkConstant.SdkConstantType;
import android.annotation.SystemService;
@@ -23,15 +26,22 @@
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.content.Context;
+import android.net.ConnectivityManager;
+import android.net.ConnectivityManager.NetworkCallback;
+import android.net.Network;
+import android.net.NetworkRequest;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.ArraySet;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Objects;
@@ -278,9 +288,180 @@
private final SparseArray mListenerMap = new SparseArray();
private final SparseArray<NsdServiceInfo> mServiceMap = new SparseArray<>();
private final Object mMapLock = new Object();
+ // Map of listener key sent by client -> per-network discovery tracker
+ @GuardedBy("mPerNetworkDiscoveryMap")
+ private final ArrayMap<Integer, PerNetworkDiscoveryTracker>
+ mPerNetworkDiscoveryMap = new ArrayMap<>();
private final ServiceHandler mHandler;
+ private class PerNetworkDiscoveryTracker {
+ final String mServiceType;
+ final int mProtocolType;
+ final DiscoveryListener mBaseListener;
+ final ArrayMap<Network, DelegatingDiscoveryListener> mPerNetworkListeners =
+ new ArrayMap<>();
+
+ final NetworkCallback mNetworkCb = new NetworkCallback() {
+ @Override
+ public void onAvailable(@NonNull Network network) {
+ final DelegatingDiscoveryListener wrappedListener = new DelegatingDiscoveryListener(
+ network, mBaseListener);
+ mPerNetworkListeners.put(network, wrappedListener);
+ discoverServices(mServiceType, mProtocolType, network, wrappedListener);
+ }
+
+ @Override
+ public void onLost(@NonNull Network network) {
+ final DelegatingDiscoveryListener listener = mPerNetworkListeners.get(network);
+ if (listener == null) return;
+ listener.notifyAllServicesLost();
+ // Listener will be removed from map in discovery stopped callback
+ stopServiceDiscovery(listener);
+ }
+ };
+
+ // Accessed from mHandler
+ private boolean mStopRequested;
+
+ public void start(@NonNull NetworkRequest request) {
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ cm.registerNetworkCallback(request, mNetworkCb, mHandler);
+ mHandler.post(() -> mBaseListener.onDiscoveryStarted(mServiceType));
+ }
+
+ /**
+ * Stop discovery on all networks tracked by this class.
+ *
+ * This will request all underlying listeners to stop, and the last one to stop will call
+ * onDiscoveryStopped or onStopDiscoveryFailed.
+ *
+ * Must be called on the handler thread.
+ */
+ public void requestStop() {
+ mHandler.post(() -> {
+ mStopRequested = true;
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ cm.unregisterNetworkCallback(mNetworkCb);
+ if (mPerNetworkListeners.size() == 0) {
+ mBaseListener.onDiscoveryStopped(mServiceType);
+ return;
+ }
+ for (int i = 0; i < mPerNetworkListeners.size(); i++) {
+ final DelegatingDiscoveryListener listener = mPerNetworkListeners.valueAt(i);
+ stopServiceDiscovery(listener);
+ }
+ });
+ }
+
+ private PerNetworkDiscoveryTracker(String serviceType, int protocolType,
+ DiscoveryListener baseListener) {
+ mServiceType = serviceType;
+ mProtocolType = protocolType;
+ mBaseListener = baseListener;
+ }
+
+ /**
+ * Subset of NsdServiceInfo that is tracked to generate service lost notifications when a
+ * network is lost.
+ *
+ * Service lost notifications only contain service name, type and network, so only track
+ * that information (Network is known from the listener). This also implements
+ * equals/hashCode for usage in maps.
+ */
+ private class TrackedNsdInfo {
+ private final String mServiceName;
+ private final String mServiceType;
+ TrackedNsdInfo(NsdServiceInfo info) {
+ mServiceName = info.getServiceName();
+ mServiceType = info.getServiceType();
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(mServiceName, mServiceType);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof TrackedNsdInfo)) return false;
+ final TrackedNsdInfo other = (TrackedNsdInfo) obj;
+ return Objects.equals(mServiceName, other.mServiceName)
+ && Objects.equals(mServiceType, other.mServiceType);
+ }
+ }
+
+ private class DelegatingDiscoveryListener implements DiscoveryListener {
+ private final Network mNetwork;
+ private final DiscoveryListener mWrapped;
+ private final ArraySet<TrackedNsdInfo> mFoundInfo = new ArraySet<>();
+
+ private DelegatingDiscoveryListener(Network network, DiscoveryListener listener) {
+ mNetwork = network;
+ mWrapped = listener;
+ }
+
+ void notifyAllServicesLost() {
+ for (int i = 0; i < mFoundInfo.size(); i++) {
+ final TrackedNsdInfo trackedInfo = mFoundInfo.valueAt(i);
+ final NsdServiceInfo serviceInfo = new NsdServiceInfo(
+ trackedInfo.mServiceName, trackedInfo.mServiceType);
+ serviceInfo.setNetwork(mNetwork);
+ mWrapped.onServiceLost(serviceInfo);
+ }
+ }
+
+ @Override
+ public void onStartDiscoveryFailed(String serviceType, int errorCode) {
+ // The delegated listener is used when NsdManager takes care of starting/stopping
+ // discovery on multiple networks. Failure to start on one network is not a global
+ // failure to be reported up, as other networks may succeed: just log.
+ Log.e(TAG, "Failed to start discovery for " + serviceType + " on " + mNetwork
+ + " with code " + errorCode);
+ mPerNetworkListeners.remove(mNetwork);
+ }
+
+ @Override
+ public void onDiscoveryStarted(String serviceType) {
+ // Wrapped listener was called upon registration, it is not called for discovery
+ // on each network
+ }
+
+ @Override
+ public void onStopDiscoveryFailed(String serviceType, int errorCode) {
+ Log.e(TAG, "Failed to stop discovery for " + serviceType + " on " + mNetwork
+ + " with code " + errorCode);
+ mPerNetworkListeners.remove(mNetwork);
+ if (mStopRequested && mPerNetworkListeners.size() == 0) {
+ // Do not report onStopDiscoveryFailed when some underlying listeners failed:
+ // this does not mean that all listeners did, and onStopDiscoveryFailed is not
+ // actionable anyway. Just report that discovery stopped.
+ mWrapped.onDiscoveryStopped(serviceType);
+ }
+ }
+
+ @Override
+ public void onDiscoveryStopped(String serviceType) {
+ mPerNetworkListeners.remove(mNetwork);
+ if (mStopRequested && mPerNetworkListeners.size() == 0) {
+ mWrapped.onDiscoveryStopped(serviceType);
+ }
+ }
+
+ @Override
+ public void onServiceFound(NsdServiceInfo serviceInfo) {
+ mFoundInfo.add(new TrackedNsdInfo(serviceInfo));
+ mWrapped.onServiceFound(serviceInfo);
+ }
+
+ @Override
+ public void onServiceLost(NsdServiceInfo serviceInfo) {
+ mFoundInfo.remove(new TrackedNsdInfo(serviceInfo));
+ mWrapped.onServiceLost(serviceInfo);
+ }
+ }
+ }
+
/**
* Create a new Nsd instance. Applications use
* {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve
@@ -634,6 +815,14 @@
}
/**
+ * Same as {@link #discoverServices(String, int, Network, DiscoveryListener)} with a null
+ * {@link Network}.
+ */
+ public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
+ discoverServices(serviceType, protocolType, (Network) null, listener);
+ }
+
+ /**
* Initiate service discovery to browse for instances of a service type. Service discovery
* consumes network bandwidth and will continue until the application calls
* {@link #stopServiceDiscovery}.
@@ -657,11 +846,13 @@
* @param serviceType The service type being discovered. Examples include "_http._tcp" for
* http services or "_ipp._tcp" for printers
* @param protocolType The service discovery protocol
+ * @param network Network to discover services on, or null to discover on all available networks
* @param listener The listener notifies of a successful discovery and is used
* to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
* Cannot be null. Cannot be in use for an active service discovery.
*/
- public void discoverServices(String serviceType, int protocolType, DiscoveryListener listener) {
+ public void discoverServices(@NonNull String serviceType, int protocolType,
+ @Nullable Network network, @NonNull DiscoveryListener listener) {
if (TextUtils.isEmpty(serviceType)) {
throw new IllegalArgumentException("Service type cannot be empty");
}
@@ -669,6 +860,7 @@
NsdServiceInfo s = new NsdServiceInfo();
s.setServiceType(serviceType);
+ s.setNetwork(network);
int key = putListener(listener, s);
try {
@@ -679,6 +871,67 @@
}
/**
+ * Initiate service discovery to browse for instances of a service type. Service discovery
+ * consumes network bandwidth and will continue until the application calls
+ * {@link #stopServiceDiscovery}.
+ *
+ * <p> The function call immediately returns after sending a request to start service
+ * discovery to the framework. The application is notified of a success to initiate
+ * discovery through the callback {@link DiscoveryListener#onDiscoveryStarted} or a failure
+ * through {@link DiscoveryListener#onStartDiscoveryFailed}.
+ *
+ * <p> Upon successful start, application is notified when a service is found with
+ * {@link DiscoveryListener#onServiceFound} or when a service is lost with
+ * {@link DiscoveryListener#onServiceLost}.
+ *
+ * <p> Upon failure to start, service discovery is not active and application does
+ * not need to invoke {@link #stopServiceDiscovery}
+ *
+ * <p> The application should call {@link #stopServiceDiscovery} when discovery of this
+ * service type is no longer required, and/or whenever the application is paused or
+ * stopped.
+ *
+ * <p> During discovery, new networks may connect or existing networks may disconnect - for
+ * example if wifi is reconnected. When a service was found on a network that disconnects,
+ * {@link DiscoveryListener#onServiceLost} will be called. If a new network connects that
+ * matches the {@link NetworkRequest}, {@link DiscoveryListener#onServiceFound} will be called
+ * for services found on that network. Applications that do not want to track networks
+ * themselves are encouraged to use this method instead of other overloads of
+ * {@code discoverServices}, as they will receive proper notifications when a service becomes
+ * available or unavailable due to network changes.
+ *
+ * @param serviceType The service type being discovered. Examples include "_http._tcp" for
+ * http services or "_ipp._tcp" for printers
+ * @param protocolType The service discovery protocol
+ * @param networkRequest Request specifying networks that should be considered when discovering
+ * @param listener The listener notifies of a successful discovery and is used
+ * to stop discovery on this serviceType through a call on {@link #stopServiceDiscovery}.
+ * Cannot be null. Cannot be in use for an active service discovery.
+ */
+ @RequiresPermission(android.Manifest.permission.ACCESS_NETWORK_STATE)
+ public void discoverServices(@NonNull String serviceType, int protocolType,
+ @NonNull NetworkRequest networkRequest, @NonNull DiscoveryListener listener) {
+ if (TextUtils.isEmpty(serviceType)) {
+ throw new IllegalArgumentException("Service type cannot be empty");
+ }
+ Objects.requireNonNull(networkRequest, "NetworkRequest cannot be null");
+ checkProtocol(protocolType);
+
+ NsdServiceInfo s = new NsdServiceInfo();
+ s.setServiceType(serviceType);
+
+ final int baseListenerKey = putListener(listener, s);
+
+ final PerNetworkDiscoveryTracker discoveryInfo = new PerNetworkDiscoveryTracker(
+ serviceType, protocolType, listener);
+
+ synchronized (mPerNetworkDiscoveryMap) {
+ mPerNetworkDiscoveryMap.put(baseListenerKey, discoveryInfo);
+ discoveryInfo.start(networkRequest);
+ }
+ }
+
+ /**
* Stop service discovery initiated with {@link #discoverServices}. An active service
* discovery is notified to the application with {@link DiscoveryListener#onDiscoveryStarted}
* and it stays active until the application invokes a stop service discovery. A successful
@@ -696,6 +949,14 @@
*/
public void stopServiceDiscovery(DiscoveryListener listener) {
int id = getListenerKey(listener);
+ // If this is a PerNetworkDiscovery request, handle it as such
+ synchronized (mPerNetworkDiscoveryMap) {
+ final PerNetworkDiscoveryTracker info = mPerNetworkDiscoveryMap.get(id);
+ if (info != null) {
+ info.requestStop();
+ return;
+ }
+ }
try {
mService.stopDiscovery(id);
} catch (RemoteException e) {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
index 0946499..8506db1 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/nsd/NsdServiceInfo.java
@@ -17,7 +17,9 @@
package android.net.nsd;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
+import android.net.Network;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
@@ -49,6 +51,9 @@
private int mPort;
+ @Nullable
+ private Network mNetwork;
+
public NsdServiceInfo() {
}
@@ -307,18 +312,37 @@
return txtRecord;
}
- public String toString() {
- StringBuffer sb = new StringBuffer();
+ /**
+ * Get the network where the service can be found.
+ *
+ * This is never null if this {@link NsdServiceInfo} was obtained from
+ * {@link NsdManager#discoverServices} or {@link NsdManager#resolveService}.
+ */
+ @Nullable
+ public Network getNetwork() {
+ return mNetwork;
+ }
+ /**
+ * Set the network where the service can be found.
+ * @param network The network, or null to search for, or to announce, the service on all
+ * connected networks.
+ */
+ public void setNetwork(@Nullable Network network) {
+ mNetwork = network;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
sb.append("name: ").append(mServiceName)
.append(", type: ").append(mServiceType)
.append(", host: ").append(mHost)
- .append(", port: ").append(mPort);
+ .append(", port: ").append(mPort)
+ .append(", network: ").append(mNetwork);
byte[] txtRecord = getTxtRecord();
- if (txtRecord != null) {
- sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
- }
+ sb.append(", txtRecord: ").append(new String(txtRecord, StandardCharsets.UTF_8));
return sb.toString();
}
@@ -352,6 +376,8 @@
}
dest.writeString(key);
}
+
+ dest.writeParcelable(mNetwork, 0);
}
/** Implement the Parcelable interface */
@@ -381,6 +407,7 @@
}
info.mTxtRecord.put(in.readString(), valueArray);
}
+ info.mNetwork = in.readParcelable(null, Network.class);
return info;
}
diff --git a/packages/ConnectivityT/service/Android.bp b/packages/ConnectivityT/service/Android.bp
index 24bc91d..5100e7c 100644
--- a/packages/ConnectivityT/service/Android.bp
+++ b/packages/ConnectivityT/service/Android.bp
@@ -28,6 +28,11 @@
"src/com/android/server/net/NetworkStats*.java",
"src/com/android/server/net/BpfInterfaceMapUpdater.java",
"src/com/android/server/net/InterfaceMapValue.java",
+ "src/com/android/server/net/CookieTagMapKey.java",
+ "src/com/android/server/net/CookieTagMapValue.java",
+ "src/com/android/server/net/StatsMapKey.java",
+ "src/com/android/server/net/StatsMapValue.java",
+ "src/com/android/server/net/UidStatsMapKey.java",
],
path: "src",
visibility: [
@@ -35,6 +40,30 @@
],
}
+// For test code only.
+filegroup {
+ name: "lib_networkStatsFactory_native",
+ srcs: [
+ "jni/com_android_server_net_NetworkStatsFactory.cpp",
+ ],
+ path: "jni",
+ visibility: [
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
+
+filegroup {
+ name: "services.connectivity-netstats-jni-sources",
+ srcs: [
+ "jni/com_android_server_net_NetworkStatsFactory.cpp",
+ "jni/com_android_server_net_NetworkStatsService.cpp",
+ ],
+ path: "jni",
+ visibility: [
+ "//packages/modules/Connectivity:__subpackages__",
+ ],
+}
+
// Nsd related libraries.
filegroup {
@@ -83,8 +112,6 @@
name: "services.connectivity-tiramisu-sources",
srcs: [
":services.connectivity-ethernet-sources",
- ":services.connectivity-ipsec-sources",
- ":services.connectivity-netstats-sources",
],
path: "src",
visibility: ["//frameworks/base/services/core"],
@@ -93,6 +120,8 @@
filegroup {
name: "services.connectivity-tiramisu-updatable-sources",
srcs: [
+ ":services.connectivity-ipsec-sources",
+ ":services.connectivity-netstats-sources",
":services.connectivity-nsd-sources",
],
path: "src",
diff --git a/services/core/jni/com_android_server_net_NetworkStatsFactory.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp
similarity index 100%
rename from services/core/jni/com_android_server_net_NetworkStatsFactory.cpp
rename to packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsFactory.cpp
diff --git a/services/core/jni/com_android_server_net_NetworkStatsService.cpp b/packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
similarity index 100%
rename from services/core/jni/com_android_server_net_NetworkStatsService.cpp
rename to packages/ConnectivityT/service/jni/com_android_server_net_NetworkStatsService.cpp
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/ConnectivityT/service/src/com/android/server/NsdService.java b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
index 497107d..ddf6d2c 100644
--- a/packages/ConnectivityT/service/src/com/android/server/NsdService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/NsdService.java
@@ -19,6 +19,9 @@
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
+import android.net.ConnectivityManager;
+import android.net.LinkProperties;
+import android.net.Network;
import android.net.nsd.INsdManager;
import android.net.nsd.INsdManagerCallback;
import android.net.nsd.INsdServiceConnector;
@@ -44,6 +47,9 @@
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.SocketException;
+import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
@@ -60,6 +66,7 @@
private static final boolean DBG = true;
private static final long CLEANUP_DELAY_MS = 10000;
+ private static final int IFACE_IDX_ANY = 0;
private final Context mContext;
private final NsdStateMachine mNsdStateMachine;
@@ -297,7 +304,7 @@
maybeStartDaemon();
id = getUniqueId();
- if (discoverServices(id, args.serviceInfo.getServiceType())) {
+ if (discoverServices(id, args.serviceInfo)) {
if (DBG) {
Log.d(TAG, "Discover " + msg.arg2 + " " + id
+ args.serviceInfo.getServiceType());
@@ -430,13 +437,38 @@
}
switch (code) {
case NativeResponseCode.SERVICE_FOUND:
- /* NNN uniqueId serviceName regType domain */
+ /* NNN uniqueId serviceName regType domain interfaceIdx netId */
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
+ final int foundNetId;
+ try {
+ foundNetId = Integer.parseInt(cooked[6]);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
+ break;
+ }
+ if (foundNetId == 0L) {
+ // Ignore services that do not have a Network: they are not usable
+ // by apps, as they would need privileged permissions to use
+ // interfaces that do not have an associated Network.
+ break;
+ }
+ servInfo.setNetwork(new Network(foundNetId));
clientInfo.onServiceFound(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_LOST:
- /* NNN uniqueId serviceName regType domain */
+ /* NNN uniqueId serviceName regType domain interfaceIdx netId */
+ final int lostNetId;
+ try {
+ lostNetId = Integer.parseInt(cooked[6]);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network received from mdnsd: " + cooked[6]);
+ break;
+ }
servInfo = new NsdServiceInfo(cooked[2], cooked[3]);
+ // The network could be null if it was torn down when the service is lost
+ // TODO: avoid returning null in that case, possibly by remembering found
+ // services on the same interface index and their network at the time
+ servInfo.setNetwork(lostNetId == 0 ? null : new Network(lostNetId));
clientInfo.onServiceLost(clientId, servInfo);
break;
case NativeResponseCode.SERVICE_DISCOVERY_FAILED:
@@ -461,7 +493,7 @@
/* NNN regId errorCode */
break;
case NativeResponseCode.SERVICE_RESOLVED:
- /* NNN resolveId fullName hostName port txtlen txtdata */
+ /* NNN resolveId fullName hostName port txtlen txtdata interfaceIdx */
int index = 0;
while (index < cooked[2].length() && cooked[2].charAt(index) != '.') {
if (cooked[2].charAt(index) == '\\') {
@@ -473,6 +505,7 @@
Log.e(TAG, "Invalid service found " + raw);
break;
}
+
String name = cooked[2].substring(0, index);
String rest = cooked[2].substring(index);
String type = rest.replace(".local.", "");
@@ -483,12 +516,13 @@
clientInfo.mResolvedService.setServiceType(type);
clientInfo.mResolvedService.setPort(Integer.parseInt(cooked[4]));
clientInfo.mResolvedService.setTxtRecords(cooked[6]);
+ // Network will be added after SERVICE_GET_ADDR_SUCCESS
stopResolveService(id);
removeRequestMap(clientId, id, clientInfo);
int id2 = getUniqueId();
- if (getAddrInfo(id2, cooked[3])) {
+ if (getAddrInfo(id2, cooked[3], cooked[7] /* interfaceIdx */)) {
storeRequestMap(clientId, id2, clientInfo, NsdManager.RESOLVE_SERVICE);
} else {
clientInfo.onResolveServiceFailed(
@@ -513,12 +547,31 @@
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
break;
case NativeResponseCode.SERVICE_GET_ADDR_SUCCESS:
- /* NNN resolveId hostname ttl addr */
+ /* NNN resolveId hostname ttl addr interfaceIdx netId */
+ Network network = null;
try {
- clientInfo.mResolvedService.setHost(InetAddress.getByName(cooked[4]));
+ final int netId = Integer.parseInt(cooked[6]);
+ network = netId == 0L ? null : new Network(netId);
+ } catch (NumberFormatException e) {
+ Log.wtf(TAG, "Invalid network in GET_ADDR_SUCCESS: " + cooked[6], e);
+ }
+
+ InetAddress serviceHost = null;
+ try {
+ serviceHost = InetAddress.getByName(cooked[4]);
+ } catch (UnknownHostException e) {
+ Log.wtf(TAG, "Invalid host in GET_ADDR_SUCCESS", e);
+ }
+
+ // If the resolved service is on an interface without a network, consider it
+ // as a failure: it would not be usable by apps as they would need
+ // privileged permissions.
+ if (network != null && serviceHost != null) {
+ clientInfo.mResolvedService.setHost(serviceHost);
+ clientInfo.mResolvedService.setNetwork(network);
clientInfo.onResolveServiceSucceeded(
clientId, clientInfo.mResolvedService);
- } catch (java.net.UnknownHostException e) {
+ } else {
clientInfo.onResolveServiceFailed(
clientId, NsdManager.FAILURE_INTERNAL_ERROR);
}
@@ -815,8 +868,15 @@
return mDaemon.execute("update", regId, t.size(), t.getRawData());
}
- private boolean discoverServices(int discoveryId, String serviceType) {
- return mDaemon.execute("discover", discoveryId, serviceType);
+ private boolean discoverServices(int discoveryId, NsdServiceInfo serviceInfo) {
+ final Network network = serviceInfo.getNetwork();
+ final int discoverInterface = getNetworkInterfaceIndex(network);
+ if (network != null && discoverInterface == IFACE_IDX_ANY) {
+ Log.e(TAG, "Interface to discover service on not found");
+ return false;
+ }
+ return mDaemon.execute("discover", discoveryId, serviceInfo.getServiceType(),
+ discoverInterface);
}
private boolean stopServiceDiscovery(int discoveryId) {
@@ -824,17 +884,61 @@
}
private boolean resolveService(int resolveId, NsdServiceInfo service) {
- String name = service.getServiceName();
- String type = service.getServiceType();
- return mDaemon.execute("resolve", resolveId, name, type, "local.");
+ final String name = service.getServiceName();
+ final String type = service.getServiceType();
+ final Network network = service.getNetwork();
+ final int resolveInterface = getNetworkInterfaceIndex(network);
+ if (network != null && resolveInterface == IFACE_IDX_ANY) {
+ Log.e(TAG, "Interface to resolve service on not found");
+ return false;
+ }
+ return mDaemon.execute("resolve", resolveId, name, type, "local.", resolveInterface);
+ }
+
+ /**
+ * Guess the interface to use to resolve or discover a service on a specific network.
+ *
+ * This is an imperfect guess, as for example the network may be gone or not yet fully
+ * registered. This is fine as failing is correct if the network is gone, and a client
+ * attempting to resolve/discover on a network not yet setup would have a bad time anyway; also
+ * this is to support the legacy mdnsresponder implementation, which historically resolved
+ * services on an unspecified network.
+ */
+ private int getNetworkInterfaceIndex(Network network) {
+ if (network == null) return IFACE_IDX_ANY;
+
+ final ConnectivityManager cm = mContext.getSystemService(ConnectivityManager.class);
+ if (cm == null) {
+ Log.wtf(TAG, "No ConnectivityManager for resolveService");
+ return IFACE_IDX_ANY;
+ }
+ final LinkProperties lp = cm.getLinkProperties(network);
+ if (lp == null) return IFACE_IDX_ANY;
+
+ // Only resolve on non-stacked interfaces
+ final NetworkInterface iface;
+ try {
+ iface = NetworkInterface.getByName(lp.getInterfaceName());
+ } catch (SocketException e) {
+ Log.e(TAG, "Error querying interface", e);
+ return IFACE_IDX_ANY;
+ }
+
+ if (iface == null) {
+ Log.e(TAG, "Interface not found: " + lp.getInterfaceName());
+ return IFACE_IDX_ANY;
+ }
+
+ return iface.getIndex();
}
private boolean stopResolveService(int resolveId) {
return mDaemon.execute("stop-resolve", resolveId);
}
- private boolean getAddrInfo(int resolveId, String hostname) {
- return mDaemon.execute("getaddrinfo", resolveId, hostname);
+ private boolean getAddrInfo(int resolveId, String hostname, String interfaceIdx) {
+ // interfaceIdx is always obtained (as string) from the service resolved callback
+ return mDaemon.execute("getaddrinfo", resolveId, hostname, interfaceIdx);
}
private boolean stopGetAddrInfo(int resolveId) {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
new file mode 100644
index 0000000..443e5b3
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapKey.java
@@ -0,0 +1,33 @@
+/*
+ * 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for cookie tag map.
+ */
+public class CookieTagMapKey extends Struct {
+ @Field(order = 0, type = Type.S64)
+ public final long socketCookie;
+
+ public CookieTagMapKey(final long socketCookie) {
+ this.socketCookie = socketCookie;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
new file mode 100644
index 0000000..93b9195
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/CookieTagMapValue.java
@@ -0,0 +1,37 @@
+/*
+ * 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value for cookie tag map.
+ */
+public class CookieTagMapValue extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ public CookieTagMapValue(final long uid, final long tag) {
+ this.uid = uid;
+ this.tag = tag;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
index 668d1cb..151c90d 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsFactory.java
@@ -54,6 +54,10 @@
* @hide
*/
public class NetworkStatsFactory {
+ static {
+ System.loadLibrary("service-connectivity");
+ }
+
private static final String TAG = "NetworkStatsFactory";
private static final boolean USE_NATIVE_PARSING = true;
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
index c371f08..a006cd5 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsRecorder.java
@@ -471,9 +471,11 @@
public void dumpDebugLocked(ProtoOutputStream proto, long tag) {
final long start = proto.start(tag);
if (mPending != null) {
- proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES, mPending.getTotalBytes());
+ proto.write(NetworkStatsRecorderProto.PENDING_TOTAL_BYTES_FIELD_NUMBER,
+ mPending.getTotalBytes());
}
- getOrLoadCompleteLocked().dumpDebug(proto, NetworkStatsRecorderProto.COMPLETE_HISTORY);
+ getOrLoadCompleteLocked().dumpDebug(proto,
+ NetworkStatsRecorderProto.COMPLETE_HISTORY_FIELD_NUMBER);
proto.end(start);
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
index 9f3371b..7a5ba09 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsService.java
@@ -62,6 +62,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.TargetApi;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.usage.NetworkStatsManager;
@@ -102,6 +103,7 @@
import android.net.netstats.provider.INetworkStatsProviderCallback;
import android.net.netstats.provider.NetworkStatsProvider;
import android.os.Binder;
+import android.os.Build;
import android.os.DropBoxManager;
import android.os.Environment;
import android.os.Handler;
@@ -169,7 +171,12 @@
* Collect and persist detailed network statistics, and provide this data to
* other system services.
*/
+@TargetApi(Build.VERSION_CODES.TIRAMISU)
public class NetworkStatsService extends INetworkStatsService.Stub {
+ static {
+ System.loadLibrary("service-connectivity");
+ }
+
static final String TAG = "NetworkStats";
static final boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
static final boolean LOGV = Log.isLoggable(TAG, Log.VERBOSE);
@@ -2004,12 +2011,15 @@
// TODO Right now it writes all history. Should it limit to the "since-boot" log?
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES, mActiveIfaces);
- dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES, mActiveUidIfaces);
- mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS);
- mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS);
- mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS);
- mUidTagRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_TAG_STATS);
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_INTERFACES_FIELD_NUMBER,
+ mActiveIfaces);
+ dumpInterfaces(proto, NetworkStatsServiceDumpProto.ACTIVE_UID_INTERFACES_FIELD_NUMBER,
+ mActiveUidIfaces);
+ mDevRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.DEV_STATS_FIELD_NUMBER);
+ mXtRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.XT_STATS_FIELD_NUMBER);
+ mUidRecorder.dumpDebugLocked(proto, NetworkStatsServiceDumpProto.UID_STATS_FIELD_NUMBER);
+ mUidTagRecorder.dumpDebugLocked(proto,
+ NetworkStatsServiceDumpProto.UID_TAG_STATS_FIELD_NUMBER);
proto.flush();
}
@@ -2019,8 +2029,8 @@
for (int i = 0; i < ifaces.size(); i++) {
final long start = proto.start(tag);
- proto.write(NetworkInterfaceProto.INTERFACE, ifaces.keyAt(i));
- ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES);
+ proto.write(NetworkInterfaceProto.INTERFACE_FIELD_NUMBER, ifaces.keyAt(i));
+ ifaces.valueAt(i).dumpDebug(proto, NetworkInterfaceProto.IDENTITIES_FIELD_NUMBER);
proto.end(start);
}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
index 5bba0b1..65ccd20 100644
--- a/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
+++ b/packages/ConnectivityT/service/src/com/android/server/net/NetworkStatsSubscriptionsMonitor.java
@@ -23,7 +23,9 @@
import static android.telephony.TelephonyManager.NETWORK_TYPE_LTE;
import android.annotation.NonNull;
+import android.annotation.TargetApi;
import android.content.Context;
+import android.os.Build;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyDisplayInfo;
@@ -43,6 +45,7 @@
/**
* Helper class that watches for events that are triggered per subscription.
*/
+@TargetApi(Build.VERSION_CODES.TIRAMISU)
public class NetworkStatsSubscriptionsMonitor extends
SubscriptionManager.OnSubscriptionsChangedListener {
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
new file mode 100644
index 0000000..ea8d836
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapKey.java
@@ -0,0 +1,46 @@
+/*
+ * 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for both stats maps.
+ */
+public class StatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ @Field(order = 1, type = Type.U32)
+ public final long tag;
+
+ @Field(order = 2, type = Type.U32)
+ public final long counterSet;
+
+ @Field(order = 3, type = Type.U32)
+ public final long ifaceIndex;
+
+ public StatsMapKey(final long uid, final long tag, final long counterSet,
+ final long ifaceIndex) {
+ this.uid = uid;
+ this.tag = tag;
+ this.counterSet = counterSet;
+ this.ifaceIndex = ifaceIndex;
+ }
+}
diff --git a/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
new file mode 100644
index 0000000..48f26ce
--- /dev/null
+++ b/packages/ConnectivityT/service/src/com/android/server/net/StatsMapValue.java
@@ -0,0 +1,46 @@
+/*
+ * 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.net;
+
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Value used for both stats maps and uid stats map.
+ */
+public class StatsMapValue extends Struct {
+ @Field(order = 0, type = Type.U63)
+ public final long rxPackets;
+
+ @Field(order = 1, type = Type.U63)
+ public final long rxBytes;
+
+ @Field(order = 2, type = Type.U63)
+ public final long txPackets;
+
+ @Field(order = 3, type = Type.U63)
+ public final long txBytes;
+
+ public StatsMapValue(final long rxPackets, final long rxBytes, final long txPackets,
+ final long txBytes) {
+ this.rxPackets = rxPackets;
+ this.rxBytes = rxBytes;
+ this.txPackets = txPackets;
+ this.txBytes = txBytes;
+ }
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
similarity index 60%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
copy to packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
index 861a4ed..2849f94 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/packages/ConnectivityT/service/src/com/android/server/net/UidStatsMapKey.java
@@ -14,6 +14,20 @@
* limitations under the License.
*/
-package com.android.systemui.shared.mediattt;
+package com.android.server.net;
-parcelable DeviceInfo;
+import com.android.net.module.util.Struct;
+import com.android.net.module.util.Struct.Field;
+import com.android.net.module.util.Struct.Type;
+
+/**
+ * Key for uid stats map.
+ */
+public class UidStatsMapKey extends Struct {
+ @Field(order = 0, type = Type.U32)
+ public final long uid;
+
+ public UidStatsMapKey(final long uid) {
+ this.uid = uid;
+ }
+}
diff --git a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
index c8ddcc8..6940c39 100644
--- a/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
+++ b/packages/SettingsLib/res/layout/edit_user_info_dialog_content.xml
@@ -27,14 +27,14 @@
android:layout_gravity="center">
<ImageView
android:id="@+id/user_photo"
- android:layout_width="@dimen/user_photo_size_in_profile_info_dialog"
- android:layout_height="@dimen/user_photo_size_in_profile_info_dialog"
+ android:layout_width="@dimen/user_photo_size_in_user_info_dialog"
+ android:layout_height="@dimen/user_photo_size_in_user_info_dialog"
android:contentDescription="@string/user_image_photo_selector"
android:scaleType="fitCenter"/>
<ImageView
android:id="@+id/add_a_photo_icon"
- android:layout_width="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
- android:layout_height="@dimen/add_a_photo_icon_size_in_profile_info_dialog"
+ android:layout_width="@dimen/add_a_photo_icon_size_in_user_info_dialog"
+ android:layout_height="@dimen/add_a_photo_icon_size_in_user_info_dialog"
android:src="@drawable/add_a_photo_circled"
android:layout_gravity="bottom|right" />
</FrameLayout>
@@ -42,7 +42,7 @@
<EditText
android:id="@+id/user_name"
android:layout_width="match_parent"
- android:layout_height="wrap_content"
+ android:layout_height="@dimen/user_name_height_in_user_info_dialog"
android:layout_gravity="center"
android:minWidth="200dp"
android:layout_marginStart="6dp"
diff --git a/packages/SettingsLib/res/values/arrays.xml b/packages/SettingsLib/res/values/arrays.xml
index 93e3dee..29a1831 100644
--- a/packages/SettingsLib/res/values/arrays.xml
+++ b/packages/SettingsLib/res/values/arrays.xml
@@ -647,6 +647,11 @@
<item>disabled</item>
</array>
+ <!-- Images offered as options in the avatar picker. If populated, the avatar_image_descriptions
+ array must also be populated with a content description for each image. -->
<array name="avatar_images"/>
+ <!-- Content descriptions for each of the images in the avatar_images array. -->
+ <string-array name="avatar_image_descriptions"/>
+
</resources>
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/res/values/dimens.xml b/packages/SettingsLib/res/values/dimens.xml
index 120df76..e1bd9f7 100644
--- a/packages/SettingsLib/res/values/dimens.xml
+++ b/packages/SettingsLib/res/values/dimens.xml
@@ -99,8 +99,9 @@
<dimen name="update_user_photo_popup_min_width">300dp</dimen>
<dimen name="add_a_photo_circled_padding">6dp</dimen>
- <dimen name="user_photo_size_in_profile_info_dialog">112dp</dimen>
- <dimen name="add_a_photo_icon_size_in_profile_info_dialog">32dp</dimen>
+ <dimen name="user_photo_size_in_user_info_dialog">112dp</dimen>
+ <dimen name="add_a_photo_icon_size_in_user_info_dialog">32dp</dimen>
+ <dimen name="user_name_height_in_user_info_dialog">48sp</dimen>
<integer name="avatar_picker_columns">3</integer>
<dimen name="avatar_size_in_picker">96dp</dimen>
diff --git a/packages/SettingsLib/res/values/strings.xml b/packages/SettingsLib/res/values/strings.xml
index af6a658..0fe869f 100644
--- a/packages/SettingsLib/res/values/strings.xml
+++ b/packages/SettingsLib/res/values/strings.xml
@@ -1562,4 +1562,8 @@
<!-- Title for a screen allowing the user to choose a profile picture. [CHAR LIMIT=NONE] -->
<string name="avatar_picker_title">Choose a profile picture</string>
+
+ <!-- Content description for a default user icon. [CHAR LIMIT=NONE] -->
+ <string name="default_user_icon_description">Default user icon</string>
+
</resources>
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..e203cba 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;
@@ -144,20 +144,20 @@
* @hide
*/
public boolean connect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.connect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
}
/*
* @hide
*/
public boolean disconnect(BluetoothDevice device) {
- if (mService == null) {
- return false;
- }
- return mService.disconnect(device);
+ if (mService == null) {
+ return false;
+ }
+ return mService.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
}
public int getConnectionStatus(BluetoothDevice device) {
@@ -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/drawable/UserIconDrawable.java b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
index a781a62..035fafd 100644
--- a/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
+++ b/packages/SettingsLib/src/com/android/settingslib/drawable/UserIconDrawable.java
@@ -16,6 +16,10 @@
package com.android.settingslib.drawable;
+import static android.app.admin.DevicePolicyResources.Drawables.Style.SOLID_COLORED;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_ICON;
+import static android.app.admin.DevicePolicyResources.Drawables.WORK_PROFILE_USER_ICON;
+
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.NonNull;
@@ -38,9 +42,12 @@
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.os.Build;
import android.os.UserHandle;
+import androidx.annotation.RequiresApi;
import androidx.annotation.VisibleForTesting;
+import androidx.core.os.BuildCompat;
import com.android.settingslib.R;
@@ -82,8 +89,23 @@
* @return drawable containing just the badge
*/
public static Drawable getManagedUserDrawable(Context context) {
- return getDrawableForDisplayDensity
- (context, com.android.internal.R.drawable.ic_corp_user_badge);
+ if (BuildCompat.isAtLeastT()) {
+ return getUpdatableManagedUserDrawable(context);
+ } else {
+ return getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_user_badge);
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static Drawable getUpdatableManagedUserDrawable(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getDrawableForDensity(
+ WORK_PROFILE_USER_ICON,
+ SOLID_COLORED,
+ context.getResources().getDisplayMetrics().densityDpi,
+ /* default= */ () -> getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_user_badge));
}
private static Drawable getDrawableForDisplayDensity(
@@ -181,8 +203,7 @@
&& dpm.getProfileOwnerOrDeviceOwnerSupervisionComponent(UserHandle.of(userId))
== null; // and has no supervisor
if (isCorp) {
- badge = getDrawableForDisplayDensity(
- context, com.android.internal.R.drawable.ic_corp_badge_case);
+ badge = getManagementBadge(context);
}
}
return setBadge(badge);
@@ -192,16 +213,35 @@
* Sets the managed badge to this user icon if the device has a device owner.
*/
public UserIconDrawable setBadgeIfManagedDevice(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
Drawable badge = null;
- boolean deviceOwnerExists = context.getSystemService(DevicePolicyManager.class)
- .getDeviceOwnerComponentOnAnyUser() != null;
+ boolean deviceOwnerExists = dpm.getDeviceOwnerComponentOnAnyUser() != null;
if (deviceOwnerExists) {
- badge = getDrawableForDisplayDensity(
- context, com.android.internal.R.drawable.ic_corp_badge_case);
+ badge = getManagementBadge(context);
}
return setBadge(badge);
}
+ private static Drawable getManagementBadge(Context context) {
+ if (BuildCompat.isAtLeastT()) {
+ return getUpdatableManagementBadge(context);
+ } else {
+ return getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_user_badge);
+ }
+ }
+
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
+ private static Drawable getUpdatableManagementBadge(Context context) {
+ DevicePolicyManager dpm = context.getSystemService(DevicePolicyManager.class);
+ return dpm.getDrawableForDensity(
+ WORK_PROFILE_ICON,
+ SOLID_COLORED,
+ context.getResources().getDisplayMetrics().densityDpi,
+ /* default= */ () -> getDrawableForDisplayDensity(
+ context, com.android.internal.R.drawable.ic_corp_badge_case));
+ }
+
public void setBadgeRadius(float radius) {
mBadgeRadius = radius;
onBoundsChange(getBounds());
diff --git a/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java b/packages/SettingsLib/src/com/android/settingslib/dream/DreamBackend.java
index 6bf43e5..d179b82 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)
@@ -528,7 +528,18 @@
if (flattenedString.indexOf('/') < 0) {
flattenedString = serviceInfo.packageName + "/" + flattenedString;
}
- return ComponentName.unflattenFromString(flattenedString);
+
+ ComponentName cn = ComponentName.unflattenFromString(flattenedString);
+
+ if (cn == null) return null;
+ if (!cn.getPackageName().equals(serviceInfo.packageName)) {
+ Log.w(TAG,
+ "Inconsistent package name in component: " + cn.getPackageName()
+ + ", should be: " + serviceInfo.packageName);
+ return null;
+ }
+
+ return cn;
}
private static DreamMetadata getDreamMetadata(PackageManager pm, ResolveInfo resolveInfo) {
diff --git a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
index 50015e6..93be66a 100644
--- a/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
+++ b/packages/SettingsLib/src/com/android/settingslib/users/AvatarPickerActivity.java
@@ -45,6 +45,7 @@
import com.google.android.setupdesign.util.ThemeHelper;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
/**
@@ -180,6 +181,7 @@
private final int mPreselectedImageStartPosition;
private final List<Drawable> mImageDrawables;
+ private final List<String> mImageDescriptions;
private final TypedArray mPreselectedImages;
private final int[] mUserIconColors;
private int mSelectedPosition = NONE;
@@ -196,6 +198,7 @@
mPreselectedImages = getResources().obtainTypedArray(R.array.avatar_images);
mUserIconColors = UserIcons.getUserIconColors(getResources());
mImageDrawables = buildDrawableList();
+ mImageDescriptions = buildDescriptionsList();
}
@NonNull
@@ -210,15 +213,24 @@
public void onBindViewHolder(@NonNull AvatarViewHolder viewHolder, int position) {
if (position == mTakePhotoPosition) {
viewHolder.setDrawable(getDrawable(R.drawable.avatar_take_photo_circled));
+ viewHolder.setContentDescription(getString(R.string.user_image_take_photo));
viewHolder.setClickListener(view -> mAvatarPhotoController.takePhoto());
} else if (position == mChoosePhotoPosition) {
viewHolder.setDrawable(getDrawable(R.drawable.avatar_choose_photo_circled));
+ viewHolder.setContentDescription(getString(R.string.user_image_choose_photo));
viewHolder.setClickListener(view -> mAvatarPhotoController.choosePhoto());
} else if (position >= mPreselectedImageStartPosition) {
+ int index = indexFromPosition(position);
viewHolder.setSelected(position == mSelectedPosition);
- viewHolder.setDrawable(mImageDrawables.get(indexFromPosition(position)));
+ viewHolder.setDrawable(mImageDrawables.get(index));
+ if (mImageDescriptions != null) {
+ viewHolder.setContentDescription(mImageDescriptions.get(index));
+ } else {
+ viewHolder.setContentDescription(
+ getString(R.string.default_user_icon_description));
+ }
viewHolder.setClickListener(view -> {
if (mSelectedPosition == position) {
deselect(position);
@@ -256,6 +268,15 @@
return result;
}
+ private List<String> buildDescriptionsList() {
+ if (mPreselectedImages.length() > 0) {
+ return Arrays.asList(
+ getResources().getStringArray(R.array.avatar_image_descriptions));
+ }
+
+ return null;
+ }
+
private Drawable circularDrawableFrom(BitmapDrawable drawable) {
Bitmap bitmap = drawable.getBitmap();
@@ -323,6 +344,10 @@
mImageView.setImageDrawable(drawable);
}
+ public void setContentDescription(String desc) {
+ mImageView.setContentDescription(desc);
+ }
+
public void setClickListener(View.OnClickListener listener) {
mImageView.setOnClickListener(listener);
}
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/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
index b0a647e..95f7ef4 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/net/DataUsageUtilsTest.java
@@ -81,8 +81,8 @@
when(mSubscriptionManager.isActiveSubscriptionId(SUB_ID)).thenReturn(false);
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse();
}
@Test
@@ -94,8 +94,8 @@
.thenReturn(new String[] {SUBSCRIBER_ID});
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isFalse();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isFalse();
}
@Test
@@ -107,7 +107,7 @@
.thenReturn(new String[] {SUBSCRIBER_ID, SUBSCRIBER_ID_2});
final NetworkTemplate networkTemplate = DataUsageUtils.getMobileTemplate(mContext, SUB_ID);
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID)).isTrue();
- assertThat(networkTemplate.matchesSubscriberId(SUBSCRIBER_ID_2)).isTrue();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID)).isTrue();
+ assertThat(networkTemplate.getSubscriberIds().contains(SUBSCRIBER_ID_2)).isTrue();
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
index 00b5f50..a6bfc408b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SystemSettings.java
@@ -84,6 +84,7 @@
Settings.System.RING_VIBRATION_INTENSITY,
Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Settings.System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY,
+ Settings.System.HAPTIC_FEEDBACK_ENABLED,
Settings.System.DISPLAY_COLOR_MODE_VENDOR_HINT, // must precede DISPLAY_COLOR_MODE
Settings.System.DISPLAY_COLOR_MODE,
Settings.System.ALARM_ALERT,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
index 246466e..bbfab0b 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/GlobalSettingsValidators.java
@@ -314,6 +314,7 @@
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_HEIGHT, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.USER_PREFERRED_RESOLUTION_WIDTH, ANY_INTEGER_VALIDATOR);
VALIDATORS.put(Global.Wearable.WET_MODE_ON, BOOLEAN_VALIDATOR);
+ VALIDATORS.put(Global.Wearable.COOLDOWN_MODE_ON, BOOLEAN_VALIDATOR);
}
}
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
index 6bcb769..06712cc 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SystemSettingsValidators.java
@@ -124,6 +124,7 @@
VALIDATORS.put(System.RING_VIBRATION_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
VALIDATORS.put(System.HARDWARE_HAPTIC_FEEDBACK_INTENSITY, VIBRATION_INTENSITY_VALIDATOR);
+ VALIDATORS.put(System.HAPTIC_FEEDBACK_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(System.RINGTONE, URI_VALIDATOR);
VALIDATORS.put(System.NOTIFICATION_SOUND, URI_VALIDATOR);
VALIDATORS.put(System.ALARM_ALERT, URI_VALIDATOR);
diff --git a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
index 13ae870..be25b47 100644
--- a/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
+++ b/packages/SettingsProvider/test/src/android/provider/SettingsBackupTest.java
@@ -594,6 +594,7 @@
Settings.Global.APP_INTEGRITY_VERIFICATION_TIMEOUT,
Settings.Global.KEY_CHORD_POWER_VOLUME_UP,
Settings.Global.CLOCKWORK_HOME_READY,
+ Settings.Global.WATCHDOG_TIMEOUT_MILLIS,
Settings.Global.Wearable.BATTERY_SAVER_MODE,
Settings.Global.Wearable.COMBINED_LOCATION_ENABLED,
Settings.Global.Wearable.HAS_PAY_TOKENS,
@@ -659,7 +660,8 @@
Settings.Global.Wearable.CLOCKWORK_SYSUI_MAIN_ACTIVITY,
Settings.Global.Wearable.CLOCKWORK_LONG_PRESS_TO_ASSISTANT_ENABLED,
Settings.Global.Wearable.WEAR_ACTIVITY_AUTO_RESUME_TIMEOUT_SET_BY_USER,
- Settings.Global.Wearable.WET_MODE_ON);
+ Settings.Global.Wearable.WET_MODE_ON,
+ Settings.Global.Wearable.COOLDOWN_MODE_ON);
private static final Set<String> BACKUP_DENY_LIST_SECURE_SETTINGS =
newHashSet(
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 27fc6ba..ef5849c 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -192,6 +192,9 @@
<uses-permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS" />
<uses-permission android:name="android.permission.WHITELIST_RESTRICTED_PERMISSIONS" />
+ <!-- Permission required for processes that don't own the focused window to switch
+ touch mode state -->
+ <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
<!-- Permission required to test onPermissionsChangedListener -->
<uses-permission android:name="android.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS" />
<uses-permission android:name="android.permission.SET_KEYBOARD_LAYOUT" />
@@ -530,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. -->
@@ -558,12 +562,14 @@
<!-- Permissions required for CTS test - TrustTestCases -->
<uses-permission android:name="android.permission.PROVIDE_TRUST_AGENT" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
+ <uses-permission android:name="android.permission.TRUST_LISTENER" />
<!-- Permission required for CTS test - CtsGameManagerTestCases -->
<uses-permission android:name="android.permission.MANAGE_GAME_MODE" />
<!-- Permission required for CTS test - CtsGameServiceTestCases -->
<uses-permission android:name="android.permission.SET_GAME_SERVICE" />
+ <uses-permission android:name="android.permission.MANAGE_GAME_ACTIVITY" />
<!-- Permission required for CTS test - ClipboardManagerTest -->
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index 5989381..c9bd371 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -39,6 +39,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INJECT_EVENTS" />
+ <uses-permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE" />
<uses-permission android:name="android.permission.DUMP" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
@@ -308,6 +309,7 @@
<!-- To change system language (HDMI CEC) -->
<uses-permission android:name="android.permission.CHANGE_CONFIGURATION" />
+ <uses-permission android:name="android.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION" />
<protected-broadcast android:name="com.android.settingslib.action.REGISTER_SLICE_RECEIVER" />
<protected-broadcast android:name="com.android.settingslib.action.UNREGISTER_SLICE_RECEIVER" />
@@ -872,11 +874,11 @@
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 for external clients to notify us of nearby media devices -->
+ <!-- TODO(b/216313420): Export and guard with a permission. -->
<service
- android:name=".media.taptotransfer.sender.MediaTttSenderService"
- />
+ android:name=".media.nearby.NearbyMediaDevicesService"
+ />
<receiver
android:name=".tuner.TunerService$ClearReceiver"
diff --git a/packages/SystemUI/TEST_MAPPING b/packages/SystemUI/TEST_MAPPING
index dee4ff5..9722b1f 100644
--- a/packages/SystemUI/TEST_MAPPING
+++ b/packages/SystemUI/TEST_MAPPING
@@ -1,7 +1,7 @@
{
// Looking for unit test presubmit configuration?
// This currently lives in ATP config apct/system_ui/unit_test
- "presubmit": [
+ "presubmit-large": [
{
"name": "PlatformScenarioTests",
"options": [
@@ -24,7 +24,9 @@
"exclude-annotation": "android.platform.test.scenario.annotation.FoldableOnly"
}
]
- },
+ }
+ ],
+ "presubmit": [
{
"name": "SystemUIGoogleTests",
"options": [
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/values-w500dp/dimens.xml b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
similarity index 74%
copy from packages/SystemUI/res/values-w500dp/dimens.xml
copy to packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
index 5ce5cee..9891156 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/packages/SystemUI/res-keyguard/drawable/status_bar_user_chip_bg.xml
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -16,6 +14,7 @@
~ limitations under the License.
-->
-<resources>
- <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@android:color/system_neutral1_800" />
+ <corners android:radius="@dimen/ongoing_call_chip_corner_radius" />
+</shape>
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/packages/SystemUI/res/drawable/action_chip_background.xml b/packages/SystemUI/res/drawable/action_chip_background.xml
index eeff39b..745470f 100644
--- a/packages/SystemUI/res/drawable/action_chip_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_background.xml
@@ -17,11 +17,11 @@
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
- android:color="@color/screenshot_button_ripple">
+ android:color="@color/overlay_button_ripple">
<item android:id="@android:id/background">
<shape android:shape="rectangle">
<solid android:color="?androidprv:attr/colorAccentSecondary"/>
- <corners android:radius="@dimen/screenshot_button_corner_radius"/>
+ <corners android:radius="@dimen/overlay_button_corner_radius"/>
</shape>
</item>
</ripple>
diff --git a/packages/SystemUI/res/drawable/action_chip_container_background.xml b/packages/SystemUI/res/drawable/action_chip_container_background.xml
index 72767a1..36083f1 100644
--- a/packages/SystemUI/res/drawable/action_chip_container_background.xml
+++ b/packages/SystemUI/res/drawable/action_chip_container_background.xml
@@ -19,5 +19,5 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
<solid android:color="?androidprv:attr/colorSurface"/>
- <corners android:radius="@dimen/screenshot_action_container_corner_radius"/>
+ <corners android:radius="@dimen/overlay_action_container_corner_radius"/>
</shape>
diff --git a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
index c547c52..ec9465b 100644
--- a/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
+++ b/packages/SystemUI/res/drawable/auth_dialog_enterprise.xml
@@ -20,6 +20,6 @@
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
- android:pathData="M20,6h-4L16,4c0,-1.11 -0.89,-2 -2,-2h-4c-1.11,0 -2,0.89 -2,2v2L4,6c-1.11,0 -1.99,0.89 -1.99,2L2,19c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,8c0,-1.11 -0.89,-2 -2,-2zM12,15c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM14,6h-4L10,4h4v2z"
+ android:pathData="@*android:string/config_work_badge_path_24"
android:fillColor="?android:attr/colorAccent"/>
</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_next.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_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%
rename from libs/WindowManager/Shell/res/drawable/letterbox_education_ic_expand_more.xml
rename 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/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
similarity index 91%
rename from packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
rename to packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
index dd818a0..d8f5632 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/overlay_actions_background_protection.xml
@@ -17,6 +17,6 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<gradient
android:angle="90"
- android:startColor="@color/screenshot_background_protection_start"
+ android:startColor="@color/overlay_background_protection_start"
android:endColor="#00000000"/>
</shape>
\ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/screenshot_button_background.xml b/packages/SystemUI/res/drawable/overlay_button_background.xml
similarity index 100%
rename from packages/SystemUI/res/drawable/screenshot_button_background.xml
rename to packages/SystemUI/res/drawable/overlay_button_background.xml
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/screenshot_actions_background_protection.xml b/packages/SystemUI/res/drawable/qs_media_scrim.xml
similarity index 62%
copy from packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
copy to packages/SystemUI/res/drawable/qs_media_scrim.xml
index dd818a0..2ec319c 100644
--- a/packages/SystemUI/res/drawable/screenshot_actions_background_protection.xml
+++ b/packages/SystemUI/res/drawable/qs_media_scrim.xml
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
- ~ Copyright (C) 2020 The Android Open Source Project
+ ~ 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.
@@ -12,11 +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
-->
-<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
+<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:angle="90"
- android:startColor="@color/screenshot_background_protection_start"
- android:endColor="#00000000"/>
-</shape>
\ No newline at end of file
+ android:type="radial"
+ android:gradientRadius="100%p"
+ android:startColor="#40000000"
+ android:endColor="#FF000000" />
+</shape>
diff --git a/packages/SystemUI/res/layout/auth_credential_password_view.xml b/packages/SystemUI/res/layout/auth_credential_password_view.xml
index 1e0ce00..0ff1db2 100644
--- a/packages/SystemUI/res/layout/auth_credential_password_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_password_view.xml
@@ -18,64 +18,72 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
- android:orientation="vertical"
- android:gravity="center_horizontal"
- android:elevation="@dimen/biometric_dialog_elevation">
+ android:elevation="@dimen/biometric_dialog_elevation"
+ android:orientation="vertical">
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/title"
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Title"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Subtitle"/>
+ <LinearLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true">
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Description"/>
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:contentDescription="@null" />
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <ImeAwareEditText
- android:id="@+id/lockPassword"
- android:layout_width="208dp"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal"
- android:minHeight="48dp"
- android:gravity="center"
- android:inputType="textPassword"
- android:maxLength="500"
- android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
- style="@style/TextAppearance.AuthCredential.PasswordEntry"/>
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <TextView
- android:id="@+id/error"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Error"/>
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="5"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:layout_alignParentBottom="true">
+
+ <ImeAwareEditText
+ android:id="@+id/lockPassword"
+ style="@style/TextAppearance.AuthCredential.PasswordEntry"
+ android:layout_width="208dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:imeOptions="actionNext|flagNoFullscreen|flagForceAscii"
+ android:inputType="textPassword"
+ android:minHeight="48dp" />
+
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
</com.android.systemui.biometrics.AuthCredentialPasswordView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
index 4939ea2..dada981 100644
--- a/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
+++ b/packages/SystemUI/res/layout/auth_credential_pattern_view.xml
@@ -22,76 +22,81 @@
android:gravity="center_horizontal"
android:elevation="@dimen/biometric_dialog_elevation">
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-
- <TextView
- android:id="@+id/title"
+ <RelativeLayout
android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Title"/>
+ android:layout_height="match_parent"
+ android:orientation="vertical">
- <TextView
- android:id="@+id/subtitle"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Subtitle"/>
+ <LinearLayout
+ android:id="@+id/auth_credential_header"
+ style="@style/AuthCredentialHeaderStyle"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content">
- <TextView
- android:id="@+id/description"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Description"/>
+ <ImageView
+ android:id="@+id/icon"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:contentDescription="@null" />
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
+ <TextView
+ android:id="@+id/title"
+ style="@style/TextAppearance.AuthNonBioCredential.Title"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="vertical"
- android:gravity="center"
- android:paddingLeft="0dp"
- android:paddingRight="0dp"
- android:paddingTop="0dp"
- android:paddingBottom="16dp"
- android:clipToPadding="false">
+ <TextView
+ android:id="@+id/subtitle"
+ style="@style/TextAppearance.AuthNonBioCredential.Subtitle"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
- <FrameLayout
- android:layout_width="wrap_content"
- android:layout_height="0dp"
- android:layout_weight="1"
- style="@style/LockPatternContainerStyle">
+ <TextView
+ android:id="@+id/description"
+ style="@style/TextAppearance.AuthNonBioCredential.Description"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+ </LinearLayout>
- <com.android.internal.widget.LockPatternView
- android:id="@+id/lockPattern"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_gravity="center"
- style="@style/LockPatternStyleBiometricPrompt"/>
-
- </FrameLayout>
-
- <TextView
- android:id="@+id/error"
+ <LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
- style="@style/TextAppearance.AuthCredential.Error"/>
+ android:layout_below="@id/auth_credential_header"
+ android:gravity="center"
+ android:orientation="vertical"
+ android:paddingBottom="16dp"
+ android:paddingTop="60dp">
- </LinearLayout>
+ <FrameLayout
+ style="@style/LockPatternContainerStyle"
+ android:layout_width="wrap_content"
+ android:layout_height="0dp"
+ android:layout_weight="1">
- <Space
- android:layout_width="0dp"
- android:layout_height="0dp"
- android:layout_weight="1"/>
+ <com.android.internal.widget.LockPatternView
+ android:id="@+id/lockPattern"
+ style="@style/LockPatternStyle"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_gravity="center" />
+
+ </FrameLayout>
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_alignParentBottom="true">
+
+ <TextView
+ android:id="@+id/error"
+ style="@style/TextAppearance.AuthNonBioCredential.Error"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ </RelativeLayout>
</com.android.systemui.biometrics.AuthCredentialPatternView>
\ No newline at end of file
diff --git a/packages/SystemUI/res/layout/clipboard_overlay.xml b/packages/SystemUI/res/layout/clipboard_overlay.xml
index 7e31909..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/Screenshot"
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/screenshot_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/screenshot_action_container_margin_horizontal"
- android:paddingEnd="@dimen/screenshot_action_container_padding_right"
- android:paddingVertical="@dimen/screenshot_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/screenshot_action_chip"
- android:id="@+id/remote_copy_chip"/>
- <include layout="@layout/screenshot_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_date.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
index b6f516f..91d81a2 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
@@ -19,8 +19,6 @@
android:id="@+id/date_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="@dimen/dream_overlay_complication_clock_date_padding_left"
- android:paddingBottom="@dimen/dream_overlay_complication_clock_date_padding_bottom"
android:gravity="center_horizontal"
android:textColor="@android:color/white"
android:shadowColor="@color/keyguard_shadow_color"
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..4824f4c 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -19,8 +19,7 @@
android:id="@+id/time_view"
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/dream_overlay_complication_weather.xml b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
index 08f0d67..3900ea5 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
@@ -19,8 +19,6 @@
android:id="@+id/weather_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingLeft="@dimen/dream_overlay_complication_weather_padding_left"
- android:paddingBottom="@dimen/dream_overlay_complication_weather_padding_bottom"
android:textColor="@android:color/white"
android:shadowColor="@color/keyguard_shadow_color"
android:shadowRadius="?attr/shadowRadius"
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 3c2183d..330f515 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -25,8 +25,14 @@
android:id="@+id/dream_overlay_content"
android:layout_width="match_parent"
android:layout_height="0dp"
+ android:layout_marginTop="@dimen/dream_overlay_container_margin_top"
+ android:layout_marginEnd="@dimen/dream_overlay_container_margin_end"
+ android:layout_marginBottom="@dimen/dream_overlay_container_margin_bottom"
+ android:layout_marginStart="@dimen/dream_overlay_container_margin_start"
+
app:layout_constraintTop_toBottomOf="@id/dream_overlay_status_bar"
- app:layout_constraintBottom_toBottomOf="parent" />
+ app:layout_constraintBottom_toBottomOf="parent"
+ />
<com.android.systemui.dreams.DreamOverlayStatusBarView
android:id="@+id/dream_overlay_status_bar"
diff --git a/packages/SystemUI/res/layout/keyguard_status_bar.xml b/packages/SystemUI/res/layout/keyguard_status_bar.xml
index 850b017..e47eed9 100644
--- a/packages/SystemUI/res/layout/keyguard_status_bar.xml
+++ b/packages/SystemUI/res/layout/keyguard_status_bar.xml
@@ -30,14 +30,39 @@
android:id="@+id/status_icon_area"
android:layout_width="wrap_content"
android:layout_height="match_parent"
+ android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
android:paddingTop="@dimen/status_bar_padding_top"
android:layout_alignParentEnd="true"
android:gravity="center_vertical|end" >
+ <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+ android:id="@+id/user_switcher_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:background="@drawable/status_bar_user_chip_bg"
+ android:visibility="visible" >
+ <ImageView android:id="@+id/current_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:scaleType="centerInside"
+ android:paddingEnd="4dp" />
+
+ <TextView android:id="@+id/current_user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ />
+ </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
+
<FrameLayout android:id="@+id/system_icons_container"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
- android:layout_marginStart="@dimen/system_icons_super_container_margin_start"
android:layout_marginEnd="@dimen/status_bar_padding_end"
android:gravity="center_vertical|end">
<include layout="@layout/system_icons" />
diff --git a/packages/SystemUI/res/layout/long_screenshot.xml b/packages/SystemUI/res/layout/long_screenshot.xml
index 856697c..cdf6103 100644
--- a/packages/SystemUI/res/layout/long_screenshot.xml
+++ b/packages/SystemUI/res/layout/long_screenshot.xml
@@ -18,7 +18,6 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
- xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:background="?android:colorBackgroundFloating"
android:id="@+id/root"
android:layout_width="match_parent"
@@ -32,7 +31,7 @@
android:text="@string/save"
android:layout_marginStart="8dp"
android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
- android:background="@drawable/screenshot_button_background"
+ android:background="@drawable/overlay_button_background"
android:textColor="?android:textColorSecondary"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
@@ -46,7 +45,7 @@
android:text="@android:string/cancel"
android:layout_marginStart="6dp"
android:layout_marginTop="@dimen/long_screenshot_action_bar_top_margin"
- android:background="@drawable/screenshot_button_background"
+ android:background="@drawable/overlay_button_background"
android:textColor="?android:textColorSecondary"
app:layout_constraintStart_toEndOf="@id/save"
app:layout_constraintTop_toTopOf="parent"
diff --git a/packages/SystemUI/res/layout/media_session_view.xml b/packages/SystemUI/res/layout/media_session_view.xml
index cc02fea..978998d 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:id="@+id/album_art"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:translationZ="0dp"
+ 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"
@@ -138,10 +154,7 @@
android:layout_marginStart="@dimen/qs_media_padding"
android:layout_marginEnd="@dimen/qs_media_padding"
android:layout_marginTop="0dp"
- android:layout_marginBottom="0dp"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintTop_toBottomOf="@id/media_seamless"
- app:layout_constraintBottom_toTopOf="@id/actionEnd" />
+ android:layout_marginBottom="0dp" />
<ImageButton
android:id="@+id/actionPrev"
@@ -152,11 +165,8 @@
android:layout_marginEnd="0dp"
android:layout_marginBottom="0dp"
android:layout_marginTop="0dp"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintEnd_toStartOf="@id/media_progress_bar"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="packed" />
<!-- Seek Bar -->
<!-- As per Material Design on Bidirectionality, this is forced to LTR in code -->
@@ -172,11 +182,7 @@
android:layout_marginBottom="0dp"
android:layout_marginTop="0dp"
android:layout_marginStart="0dp"
- android:layout_marginEnd="0dp"
- app:layout_constraintStart_toEndOf="@id/actionPrev"
- app:layout_constraintEnd_toStartOf="@id/actionNext"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+ android:layout_marginEnd="0dp" />
<ImageButton
android:id="@+id/actionNext"
@@ -186,11 +192,7 @@
android:layout_marginStart="0dp"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:layout_marginBottom="0dp"
- android:layout_marginTop="0dp"
- app:layout_constraintStart_toEndOf="@id/media_progress_bar"
- app:layout_constraintEnd_toStartOf="@id/actionStart"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+ android:layout_marginTop="0dp" />
<ImageButton
android:id="@+id/actionStart"
@@ -200,11 +202,7 @@
android:layout_marginStart="@dimen/qs_media_action_spacing"
android:layout_marginEnd="@dimen/qs_media_action_spacing"
android:layout_marginBottom="0dp"
- android:layout_marginTop="0dp"
- app:layout_constraintStart_toEndOf="@id/actionNext"
- app:layout_constraintEnd_toStartOf="@id/actionEnd"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+ android:layout_marginTop="0dp" />
<ImageButton
android:id="@+id/actionEnd"
@@ -215,11 +213,7 @@
android:layout_marginEnd="4dp"
android:layout_marginBottom="0dp"
android:layout_marginTop="0dp"
- app:layout_constraintHorizontal_chainStyle="packed"
- app:layout_constraintStart_toEndOf="@id/actionStart"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+ app:layout_constraintHorizontal_chainStyle="packed" />
<!-- Long press menu -->
<TextView
@@ -281,6 +275,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 +299,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/screenshot_action_chip.xml b/packages/SystemUI/res/layout/overlay_action_chip.xml
similarity index 61%
rename from packages/SystemUI/res/layout/screenshot_action_chip.xml
rename to packages/SystemUI/res/layout/overlay_action_chip.xml
index b80469f..e0c20ff 100644
--- a/packages/SystemUI/res/layout/screenshot_action_chip.xml
+++ b/packages/SystemUI/res/layout/overlay_action_chip.xml
@@ -14,33 +14,34 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-<com.android.systemui.screenshot.ScreenshotActionChip
+<com.android.systemui.screenshot.OverlayActionChip
xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/screenshot_action_chip"
+ android:id="@+id/overlay_action_chip"
+ android:theme="@style/FloatingOverlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_marginStart="@dimen/screenshot_action_chip_margin_start"
- android:paddingVertical="@dimen/screenshot_action_chip_margin_vertical"
+ android:layout_marginStart="@dimen/overlay_action_chip_margin_start"
+ android:paddingVertical="@dimen/overlay_action_chip_margin_vertical"
android:layout_gravity="center"
android:gravity="center"
android:alpha="0.0">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:paddingVertical="@dimen/screenshot_action_chip_padding_vertical"
+ android:paddingVertical="@dimen/overlay_action_chip_padding_vertical"
android:background="@drawable/action_chip_background"
android:gravity="center">
<ImageView
- android:id="@+id/screenshot_action_chip_icon"
- android:tint="?android:attr/textColorPrimary"
- android:layout_width="@dimen/screenshot_action_chip_icon_size"
- android:layout_height="@dimen/screenshot_action_chip_icon_size"/>
+ android:id="@+id/overlay_action_chip_icon"
+ android:tint="?attr/overlayButtonTextColor"
+ android:layout_width="@dimen/overlay_action_chip_icon_size"
+ android:layout_height="@dimen/overlay_action_chip_icon_size"/>
<TextView
- android:id="@+id/screenshot_action_chip_text"
+ android:id="@+id/overlay_action_chip_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@*android:string/config_headlineFontFamilyMedium"
- android:textSize="@dimen/screenshot_action_chip_text_size"
- android:textColor="?android:attr/textColorPrimary"/>
+ android:textSize="@dimen/overlay_action_chip_text_size"
+ android:textColor="?attr/overlayButtonTextColor"/>
</LinearLayout>
-</com.android.systemui.screenshot.ScreenshotActionChip>
+</com.android.systemui.screenshot.OverlayActionChip>
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/qs_tile_label.xml b/packages/SystemUI/res/layout/qs_tile_label.xml
index 536b042..02c58e4 100644
--- a/packages/SystemUI/res/layout/qs_tile_label.xml
+++ b/packages/SystemUI/res/layout/qs_tile_label.xml
@@ -26,9 +26,9 @@
android:layout_marginEnd="0dp"
android:layout_gravity="center_vertical | start">
- <TextView
+ <com.android.systemui.util.SafeMarqueeTextView
android:id="@+id/tile_label"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textDirection="locale"
@@ -37,9 +37,9 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.QS.TileLabel"/>
- <TextView
+ <com.android.systemui.util.SafeMarqueeTextView
android:id="@+id/app_label"
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="start"
android:textDirection="locale"
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/layout/screenshot.xml b/packages/SystemUI/res/layout/screenshot.xml
index 227212b..890dbe5 100644
--- a/packages/SystemUI/res/layout/screenshot.xml
+++ b/packages/SystemUI/res/layout/screenshot.xml
@@ -17,7 +17,7 @@
<com.android.systemui.screenshot.ScreenshotView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/screenshot_frame"
- android:theme="@style/Screenshot"
+ android:theme="@style/FloatingOverlay"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:importantForAccessibility="no">
@@ -30,11 +30,11 @@
android:importantForAccessibility="no"/>
<ImageView
android:id="@+id/screenshot_actions_background"
- android:layout_height="@dimen/screenshot_bg_protection_height"
+ android:layout_height="@dimen/overlay_bg_protection_height"
android:layout_width="match_parent"
android:layout_gravity="bottom"
android:alpha="0.0"
- android:src="@drawable/screenshot_actions_background_protection"/>
+ android:src="@drawable/overlay_actions_background_protection"/>
<ImageView
android:id="@+id/screenshot_flash"
android:layout_width="match_parent"
diff --git a/packages/SystemUI/res/layout/screenshot_static.xml b/packages/SystemUI/res/layout/screenshot_static.xml
index 8f791c3..813bb60 100644
--- a/packages/SystemUI/res/layout/screenshot_static.xml
+++ b/packages/SystemUI/res/layout/screenshot_static.xml
@@ -26,7 +26,7 @@
android:layout_width="0dp"
android:elevation="1dp"
android:background="@drawable/action_chip_container_background"
- android:layout_marginStart="@dimen/screenshot_action_container_margin_horizontal"
+ android:layout_marginStart="@dimen/overlay_action_container_margin_horizontal"
app:layout_constraintBottom_toBottomOf="@+id/screenshot_actions_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/screenshot_actions_container"
@@ -35,9 +35,9 @@
android:id="@+id/screenshot_actions_container"
android:layout_width="0dp"
android:layout_height="wrap_content"
- android:layout_marginEnd="@dimen/screenshot_action_container_margin_horizontal"
- android:paddingEnd="@dimen/screenshot_action_container_padding_right"
- android:paddingVertical="@dimen/screenshot_action_container_padding_vertical"
+ 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"
@@ -50,11 +50,11 @@
android:id="@+id/screenshot_actions"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
- <include layout="@layout/screenshot_action_chip"
+ <include layout="@layout/overlay_action_chip"
android:id="@+id/screenshot_share_chip"/>
- <include layout="@layout/screenshot_action_chip"
+ <include layout="@layout/overlay_action_chip"
android:id="@+id/screenshot_edit_chip"/>
- <include layout="@layout/screenshot_action_chip"
+ <include layout="@layout/overlay_action_chip"
android:id="@+id/screenshot_scroll_chip"
android:visibility="gone" />
</LinearLayout>
@@ -89,7 +89,7 @@
<ImageView
android:id="@+id/screenshot_preview"
android:visibility="invisible"
- android:layout_width="@dimen/screenshot_x_scale"
+ android:layout_width="@dimen/overlay_x_scale"
android:layout_margin="@dimen/overlay_border_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
diff --git a/packages/SystemUI/res/layout/status_bar.xml b/packages/SystemUI/res/layout/status_bar.xml
index 8b244c7..af98019 100644
--- a/packages/SystemUI/res/layout/status_bar.xml
+++ b/packages/SystemUI/res/layout/status_bar.xml
@@ -119,6 +119,32 @@
android:gravity="center_vertical|end"
>
+ <com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer
+ android:id="@+id/user_switcher_container"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:orientation="horizontal"
+ android:paddingTop="4dp"
+ android:paddingBottom="4dp"
+ android:paddingStart="8dp"
+ android:paddingEnd="8dp"
+ android:layout_marginEnd="16dp"
+ android:background="@drawable/status_bar_user_chip_bg"
+ android:visibility="visible" >
+ <ImageView android:id="@+id/current_user_avatar"
+ android:layout_width="@dimen/multi_user_avatar_keyguard_size"
+ android:layout_height="@dimen/multi_user_avatar_keyguard_size"
+ android:scaleType="centerInside"
+ android:paddingEnd="4dp" />
+
+ <TextView android:id="@+id/current_user_name"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textAppearance="@style/TextAppearance.StatusBar.Clock"
+ />
+ </com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer>
+
<include layout="@layout/system_icons" />
</com.android.keyguard.AlphaOptimizedLinearLayout>
</LinearLayout>
diff --git a/packages/SystemUI/res/layout/system_icons.xml b/packages/SystemUI/res/layout/system_icons.xml
index 6d5c7d4..4f4bae4 100644
--- a/packages/SystemUI/res/layout/system_icons.xml
+++ b/packages/SystemUI/res/layout/system_icons.xml
@@ -17,7 +17,7 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:systemui="http://schemas.android.com/apk/res-auto"
android:id="@+id/system_icons"
- android:layout_width="match_parent"
+ android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
diff --git a/packages/SystemUI/res/values-night/colors.xml b/packages/SystemUI/res/values-night/colors.xml
index 3412722..b318bbc 100644
--- a/packages/SystemUI/res/values-night/colors.xml
+++ b/packages/SystemUI/res/values-night/colors.xml
@@ -58,9 +58,9 @@
<!-- The color of the text in the Global Actions menu -->
<color name="global_actions_alert_text">@color/GM2_red_300</color>
- <!-- Global screenshot actions -->
- <color name="screenshot_button_ripple">#42FFFFFF</color>
- <color name="screenshot_background_protection_start">#80000000</color> <!-- 50% black -->
+ <!-- Floating overlay actions -->
+ <color name="overlay_button_ripple">#42FFFFFF</color>
+ <color name="overlay_background_protection_start">#80000000</color> <!-- 50% black -->
<!-- Media -->
<color name="media_divider">#85ffffff</color>
diff --git a/packages/SystemUI/res/values-night/styles.xml b/packages/SystemUI/res/values-night/styles.xml
index 1f815b7..f7261e7 100644
--- a/packages/SystemUI/res/values-night/styles.xml
+++ b/packages/SystemUI/res/values-night/styles.xml
@@ -47,8 +47,8 @@
<item name="android:textColorSecondary">?android:attr/textColorPrimaryInverse</item>
</style>
- <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight">
- <item name="android:textColorPrimary">?android:attr/textColorPrimaryInverse</item>
+ <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="overlayButtonTextColor">?android:attr/textColorPrimaryInverse</item>
</style>
<style name="Theme.PeopleTileConfigActivity" parent="@style/Theme.SystemUI">
diff --git a/packages/SystemUI/res/values-sw600dp/config.xml b/packages/SystemUI/res/values-sw600dp/config.xml
index 1b8453a..abc69b0 100644
--- a/packages/SystemUI/res/values-sw600dp/config.xml
+++ b/packages/SystemUI/res/values-sw600dp/config.xml
@@ -23,6 +23,9 @@
<!-- The maximum number of rows in the QuickSettings -->
<integer name="quick_settings_max_rows">4</integer>
+ <!-- Use collapsed layout for media player in landscape QQS -->
+ <bool name="config_quickSettingsMediaLandscapeCollapsed">false</bool>
+
<!-- Nav bar button default ordering/layout -->
<string name="config_navBarLayout" translatable="false">left;back,home,recent;right</string>
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-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/attrs.xml b/packages/SystemUI/res/values/attrs.xml
index de136de..e6ab0ff 100644
--- a/packages/SystemUI/res/values/attrs.xml
+++ b/packages/SystemUI/res/values/attrs.xml
@@ -204,5 +204,7 @@
<attr name="singleLineVerticalPadding" format="dimension" />
<attr name="textViewId" format="reference" />
</declare-styleable>
+
+ <attr name="overlayButtonTextColor" format="color" />
</resources>
diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml
index 81e3e04..3ab569a 100644
--- a/packages/SystemUI/res/values/colors.xml
+++ b/packages/SystemUI/res/values/colors.xml
@@ -138,9 +138,9 @@
<color name="udfps_enroll_progress">#7DA7F1</color>
<color name="udfps_enroll_progress_help">#ffEE675C</color>
- <!-- Global screenshot actions -->
- <color name="screenshot_button_ripple">#1f000000</color>
- <color name="screenshot_background_protection_start">#40000000</color> <!-- 25% black -->
+ <!-- Floating overlay actions -->
+ <color name="overlay_button_ripple">#1f000000</color>
+ <color name="overlay_background_protection_start">#40000000</color> <!-- 25% black -->
<!-- GM2 colors -->
<color name="GM2_grey_100">#F1F3F4</color>
diff --git a/packages/SystemUI/res/values/config.xml b/packages/SystemUI/res/values/config.xml
index 079f5d0..47822b7 100644
--- a/packages/SystemUI/res/values/config.xml
+++ b/packages/SystemUI/res/values/config.xml
@@ -102,6 +102,9 @@
<item>one_handed_mode_enabled:onehanded</item>
</string-array>
+ <!-- Use collapsed layout for media player in landscape QQS -->
+ <bool name="config_quickSettingsMediaLandscapeCollapsed">true</bool>
+
<!-- Show indicator for Wifi on but not connected. -->
<bool name="config_showWifiIndicatorWhenEnabled">false</bool>
@@ -683,7 +686,7 @@
<integer name="config_connectionMinDuration">1000</integer>
<!-- Flag to activate notification to contents feature -->
- <bool name="config_notificationToContents">false</bool>
+ <bool name="config_notificationToContents">true</bool>
<!-- Respect drawable/rounded_secondary.xml intrinsic size for multiple radius corner path
customization for secondary display-->
diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml
index bba616f..7eb25db 100644
--- a/packages/SystemUI/res/values/dimens.xml
+++ b/packages/SystemUI/res/values/dimens.xml
@@ -258,36 +258,36 @@
<!-- Dimensions related to screenshots -->
- <!-- The padding on the global screenshot background image -->
- <dimen name="screenshot_x_scale">80dp</dimen>
- <dimen name="screenshot_bg_protection_height">242dp</dimen>
- <dimen name="screenshot_action_container_corner_radius">18dp</dimen>
- <dimen name="screenshot_action_container_padding_vertical">4dp</dimen>
- <dimen name="screenshot_action_container_margin_horizontal">8dp</dimen>
- <dimen name="screenshot_action_container_padding_right">8dp</dimen>
- <!-- Radius of the chip background on global screenshot actions -->
- <dimen name="screenshot_button_corner_radius">8dp</dimen>
- <!-- Margin between successive chips -->
- <dimen name="screenshot_action_chip_margin_start">8dp</dimen>
- <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
- <dimen name="screenshot_action_chip_margin_vertical">4dp</dimen>
- <dimen name="screenshot_action_chip_padding_vertical">11dp</dimen>
- <dimen name="screenshot_action_chip_icon_size">18sp</dimen>
- <!-- Padding on each side of the icon for icon-only chips -->
- <dimen name="screenshot_action_chip_icon_only_padding_horizontal">14dp</dimen>
- <!-- Padding at the edges of the chip for icon-and-text chips -->
- <dimen name="screenshot_action_chip_padding_horizontal">12dp</dimen>
- <!-- Spacing between chip icon and chip text -->
- <dimen name="screenshot_action_chip_spacing">8dp</dimen>
- <dimen name="screenshot_action_chip_text_size">14sp</dimen>
- <dimen name="screenshot_dismissal_height_delta">80dp</dimen>
+
<dimen name="screenshot_crop_handle_thickness">3dp</dimen>
<dimen name="long_screenshot_action_bar_top_margin">8dp</dimen>
<!-- Dimensions shared between "overlays" (clipboard and screenshot preview UIs) -->
+ <!-- Constrained size of the floating overlay preview -->
+ <dimen name="overlay_x_scale">80dp</dimen>
+ <!-- Radius of the chip background on floating overlay actions -->
+ <dimen name="overlay_button_corner_radius">8dp</dimen>
+ <!-- Margin between successive chips -->
+ <dimen name="overlay_action_chip_margin_start">8dp</dimen>
+ <!-- Padding to make tappable chip height 48dp (18+11+11+4+4) -->
+ <dimen name="overlay_action_chip_margin_vertical">4dp</dimen>
+ <dimen name="overlay_action_chip_padding_vertical">11dp</dimen>
+ <dimen name="overlay_action_chip_icon_size">18sp</dimen>
+ <!-- Padding on each side of the icon for icon-only chips -->
+ <dimen name="overlay_action_chip_icon_only_padding_horizontal">14dp</dimen>
+ <!-- Padding at the edges of the chip for icon-and-text chips -->
+ <dimen name="overlay_action_chip_padding_horizontal">12dp</dimen>
+ <!-- Spacing between chip icon and chip text -->
+ <dimen name="overlay_action_chip_spacing">8dp</dimen>
+ <dimen name="overlay_action_chip_text_size">14sp</dimen>
<dimen name="overlay_offset_y">8dp</dimen>
<dimen name="overlay_offset_x">16dp</dimen>
<dimen name="overlay_preview_elevation">4dp</dimen>
+ <dimen name="overlay_action_container_margin_horizontal">8dp</dimen>
+ <dimen name="overlay_bg_protection_height">242dp</dimen>
+ <dimen name="overlay_action_container_corner_radius">18dp</dimen>
+ <dimen name="overlay_action_container_padding_vertical">4dp</dimen>
+ <dimen name="overlay_action_container_padding_right">8dp</dimen>
<dimen name="overlay_dismiss_button_elevation">7dp</dimen>
<dimen name="overlay_dismiss_button_tappable_size">48dp</dimen>
<dimen name="overlay_dismiss_button_margin">8dp</dimen>
@@ -295,7 +295,7 @@
<!-- need a negative margin for some of the constraints. should be overlay_border_width * -1 -->
<dimen name="overlay_border_width_neg">-4dp</dimen>
- <dimen name="clipboard_preview_size">@dimen/screenshot_x_scale</dimen>
+ <dimen name="clipboard_preview_size">@dimen/overlay_x_scale</dimen>
<!-- The width of the view containing navigation buttons -->
@@ -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>
@@ -990,6 +990,8 @@
<!-- Sizes for alternate session-based layout -->
<dimen name="qs_media_session_enabled_seekbar_vertical_padding">15dp</dimen>
<dimen name="qs_media_session_disabled_seekbar_vertical_padding">16dp</dimen>
+ <dimen name="qs_media_session_height_expanded">184dp</dimen>
+ <dimen name="qs_media_session_height_collapsed">128dp</dimen>
<!-- Size of Smartspace media recommendations cards in the QSPanel carousel -->
<dimen name="qs_aa_media_rec_album_size_collapsed">72dp</dimen>
@@ -1347,13 +1349,8 @@
<dimen name="dream_overlay_notifications_drag_area_height">100dp</dimen>
<!-- Dream overlay complications related dimensions -->
- <dimen name="dream_overlay_complication_clock_time_padding_left">50dp</dimen>
<dimen name="dream_overlay_complication_clock_time_text_size">72sp</dimen>
- <dimen name="dream_overlay_complication_clock_date_padding_left">60dp</dimen>
- <dimen name="dream_overlay_complication_clock_date_padding_bottom">50dp</dimen>
<dimen name="dream_overlay_complication_clock_date_text_size">18sp</dimen>
- <dimen name="dream_overlay_complication_weather_padding_left">20dp</dimen>
- <dimen name="dream_overlay_complication_weather_padding_bottom">50dp</dimen>
<dimen name="dream_overlay_complication_weather_text_size">18sp</dimen>
<!-- The position of the end guide, which dream overlay complications can align their start with
@@ -1388,4 +1385,13 @@
<item name="dream_overlay_bouncer_start_region_screen_percentage" format="float" type="dimen">
.2
</item>
+
+ <!-- The margins applied to the dream overlay container -->
+ <dimen name="dream_overlay_container_margin_start">0dp</dimen>
+ <dimen name="dream_overlay_container_margin_end">0dp</dimen>
+ <dimen name="dream_overlay_container_margin_top">0dp</dimen>
+ <dimen name="dream_overlay_container_margin_bottom">0dp</dimen>
+
+ <!-- The margin applied between complications -->
+ <dimen name="dream_overlay_complication_margin">0dp</dimen>
</resources>
diff --git a/packages/SystemUI/res/values/flags.xml b/packages/SystemUI/res/values/flags.xml
index 2a70645..49dd574 100644
--- a/packages/SystemUI/res/values/flags.xml
+++ b/packages/SystemUI/res/values/flags.xml
@@ -32,4 +32,8 @@
<bool name="flag_smartspace">false</bool>
+ <!-- Whether the user switcher chip shows in the status bar. When true, the multi user
+ avatar will no longer show on the lockscreen -->
+ <bool name="flag_user_switcher_chip">false</bool>
+
</resources>
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/res/values/styles.xml b/packages/SystemUI/res/values/styles.xml
index 57f1f3f..590cc9b 100644
--- a/packages/SystemUI/res/values/styles.xml
+++ b/packages/SystemUI/res/values/styles.xml
@@ -236,6 +236,41 @@
<item name="android:textColor">?android:attr/colorError</item>
</style>
+ <style name="TextAppearance.AuthNonBioCredential"
+ parent="@android:style/TextAppearance.DeviceDefault">
+ <item name="android:accessibilityLiveRegion">polite</item>
+ <item name="android:textAlignment">gravity</item>
+ <item name="android:layout_gravity">top</item>
+ <item name="android:textColor">?android:attr/textColorPrimary</item>
+ </style>
+
+ <style name="TextAppearance.AuthNonBioCredential.Title">
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:textSize">36sp</item>
+ </style>
+
+ <style name="TextAppearance.AuthNonBioCredential.Subtitle">
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:textSize">18sp</item>
+ </style>
+
+ <style name="TextAppearance.AuthNonBioCredential.Description">
+ <item name="android:fontFamily">google-sans</item>
+ <item name="android:layout_marginTop">20dp</item>
+ <item name="android:textSize">16sp</item>
+ </style>
+
+ <style name="TextAppearance.AuthNonBioCredential.Error">
+ <item name="android:paddingTop">6dp</item>
+ <item name="android:paddingBottom">18dp</item>
+ <item name="android:paddingHorizontal">24dp</item>
+ <item name="android:textSize">14sp</item>
+ <item name="android:textColor">?android:attr/colorError</item>
+ <item name="android:gravity">center</item>
+ </style>
+
<style name="TextAppearance.AuthCredential.PasswordEntry" parent="@android:style/TextAppearance.DeviceDefault">
<item name="android:gravity">center</item>
<item name="android:singleLine">true</item>
@@ -243,6 +278,15 @@
<item name="android:textSize">24sp</item>
</style>
+ <style name="AuthCredentialHeaderStyle">
+ <item name="android:paddingStart">48dp</item>
+ <item name="android:paddingEnd">24dp</item>
+ <item name="android:paddingTop">28dp</item>
+ <item name="android:paddingBottom">20dp</item>
+ <item name="android:orientation">vertical</item>
+ <item name="android:layout_gravity">top</item>
+ </style>
+
<style name="DeviceManagementDialogTitle">
<item name="android:gravity">center</item>
<item name="android:textAppearance">@style/TextAppearance.DeviceManagementDialog.Title</item>
@@ -307,9 +351,8 @@
<item name="android:maxWidth">420dp</item>
<item name="android:minHeight">0dp</item>
<item name="android:minWidth">0dp</item>
- <item name="android:paddingBottom">0dp</item>
- <item name="android:paddingHorizontal">44dp</item>
- <item name="android:paddingTop">0dp</item>
+ <item name="android:paddingHorizontal">60dp</item>
+ <item name="android:paddingBottom">40dp</item>
</style>
<style name="LockPatternStyle">
@@ -664,7 +707,9 @@
<item name="android:windowActivityTransitions">true</item>
</style>
- <style name="Screenshot" parent="@android:style/Theme.DeviceDefault.DayNight"/>
+ <style name="FloatingOverlay" parent="@android:style/Theme.DeviceDefault.DayNight">
+ <item name="overlayButtonTextColor">?android:attr/textColorPrimary</item>
+ </style>
<!-- Clipboard overlay's edit text activity. -->
<style name="EditTextActivity" parent="@android:style/Theme.DeviceDefault.DayNight">
diff --git a/packages/SystemUI/res/xml/media_session_collapsed.xml b/packages/SystemUI/res/xml/media_session_collapsed.xml
new file mode 100644
index 0000000..c6e18a6
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_session_collapsed.xml
@@ -0,0 +1,90 @@
+<?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
+ -->
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <Constraint
+ android:id="@+id/album_art"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_collapsed"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <Constraint
+ android:id="@+id/actionPlayPause"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ app:layout_constraintStart_toEndOf="@id/actionEnd"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <Constraint
+ android:id="@+id/actionPrev"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintHorizontal_bias="1"
+ app:layout_constraintHorizontal_chainStyle="packed"
+ app:layout_constraintStart_toEndOf="@id/header_artist"
+ app:layout_constraintEnd_toStartOf="@id/media_progress_bar"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
+
+ <Constraint
+ android:id="@+id/media_progress_bar"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ android:visibility="gone"
+ app:layout_constraintStart_toEndOf="@id/actionPrev"
+ app:layout_constraintEnd_toStartOf="@id/actionNext"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
+
+ <Constraint
+ android:id="@+id/actionNext"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toEndOf="@id/media_progress_bar"
+ app:layout_constraintEnd_toStartOf="@id/actionStart"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
+
+ <Constraint
+ android:id="@+id/actionStart"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:visibility="gone"
+ app:layout_constraintStart_toEndOf="@id/actionNext"
+ app:layout_constraintEnd_toStartOf="@id/actionEnd"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
+
+ <Constraint
+ android:id="@+id/actionEnd"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:visibility="gone"
+ app:layout_constraintStart_toEndOf="@id/actionStart"
+ app:layout_constraintEnd_toStartOf="@id/actionPlayPause"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless" />
+
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/res/xml/media_session_expanded.xml b/packages/SystemUI/res/xml/media_session_expanded.xml
new file mode 100644
index 0000000..18ec7aa
--- /dev/null
+++ b/packages/SystemUI/res/xml/media_session_expanded.xml
@@ -0,0 +1,84 @@
+<?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
+ -->
+<ConstraintSet
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <Constraint
+ android:id="@+id/album_art"
+ android:layout_width="match_parent"
+ android:layout_height="@dimen/qs_media_session_height_expanded"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent" />
+
+ <Constraint
+ android:id="@+id/actionPlayPause"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:layout_marginEnd="@dimen/qs_media_padding"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/media_seamless"
+ app:layout_constraintBottom_toTopOf="@id/actionEnd" />
+
+ <Constraint
+ android:id="@+id/actionPrev"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/media_progress_bar"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+
+ <Constraint
+ android:id="@+id/media_progress_bar"
+ android:layout_width="0dp"
+ android:layout_height="wrap_content"
+ app:layout_constraintStart_toEndOf="@id/actionPrev"
+ app:layout_constraintEnd_toStartOf="@id/actionNext"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+
+ <Constraint
+ android:id="@+id/actionNext"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toEndOf="@id/media_progress_bar"
+ app:layout_constraintEnd_toStartOf="@id/actionStart"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+
+ <Constraint
+ android:id="@+id/actionStart"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toEndOf="@id/actionNext"
+ app:layout_constraintEnd_toStartOf="@id/actionEnd"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+
+ <Constraint
+ android:id="@+id/actionEnd"
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ app:layout_constraintStart_toEndOf="@id/actionStart"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/actionPlayPause" />
+
+</ConstraintSet>
\ No newline at end of file
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
index 9010d51..fc6bb50 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/animation/UnfoldMoveFromCenterAnimator.kt
@@ -42,7 +42,9 @@
* are different than actual bounds (e.g. view container may
* have larger width than width of the items in the container)
*/
- private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {}
+ private val viewCenterProvider: ViewCenterProvider = object : ViewCenterProvider {},
+ /** Allows to set the alpha based on the progress. */
+ private val alphaProvider: AlphaProvider? = null
) : UnfoldTransitionProgressProvider.TransitionProgressListener {
private val screenSize = Point()
@@ -99,17 +101,27 @@
override fun onTransitionProgress(progress: Float) {
animatedViews.forEach {
- it.view.get()?.let { view ->
- translationApplier.apply(
- view = view,
- x = it.startTranslationX * (1 - progress),
- y = it.startTranslationY * (1 - progress)
- )
- }
+ it.applyTransition(progress)
+ it.applyAlpha(progress)
}
lastAnimationProgress = progress
}
+ private fun AnimatedView.applyTransition(progress: Float) {
+ view.get()?.let { view ->
+ translationApplier.apply(
+ view = view,
+ x = startTranslationX * (1 - progress),
+ y = startTranslationY * (1 - progress)
+ )
+ }
+ }
+
+ private fun AnimatedView.applyAlpha(progress: Float) {
+ if (alphaProvider == null) return
+ view.get()?.alpha = alphaProvider.getAlpha(progress)
+ }
+
private fun createAnimatedView(view: View): AnimatedView =
AnimatedView(view = WeakReference(view)).updateAnimatedView(view)
@@ -146,6 +158,13 @@
}
}
+ /** Allows to set a custom alpha based on the progress. */
+ interface AlphaProvider {
+
+ /** Returns the alpha views should have at a given progress. */
+ fun getAlpha(progress: Float): Float
+ }
+
/**
* Interface that allows to use custom logic to get the center of the view
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl
new file mode 100644
index 0000000..6db06f0
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesProvider.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.media;
+
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback;
+import com.android.systemui.shared.media.NearbyDevice;
+
+/**
+ * An interface that provides information about nearby devices that are able to play media.
+ *
+ * External clients will implement this interface and System UI will invoke it if it's passed to
+ * SystemUI via {@link INearbyMediaDevicesService.registerProvider}.
+ */
+interface INearbyMediaDevicesProvider {
+ /**
+ * Returns a list of nearby devices that are able to play media.
+ */
+ List<NearbyDevice> getCurrentNearbyDevices() = 1;
+
+ /**
+ * Registers a callback that will be notified each time the status of a nearby device changes.
+ */
+ oneway void registerNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 2;
+
+ /**
+ * Unregisters a callback. See {@link registerNearbyDevicesCallback}.
+ */
+ oneway void unregisterNearbyDevicesCallback(in INearbyMediaDevicesUpdateCallback callback) = 3;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl
new file mode 100644
index 0000000..4f3e10d
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesService.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.media;
+
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider;
+
+/**
+ * An interface that can be invoked to notify System UI of nearby media devices.
+ *
+ * External clients wanting to notify System UI about the status of nearby media devices should
+ * implement {@link INearbyMediaDevicesProvider} and then register it with system UI using this
+ * service.
+ *
+ * System UI will implement this interface and external clients will invoke it.
+ */
+interface INearbyMediaDevicesService {
+ /** Registers a new provider. */
+ oneway void registerProvider(INearbyMediaDevicesProvider provider) = 1;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl
new file mode 100644
index 0000000..a835f52
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/INearbyMediaDevicesUpdateCallback.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.media;
+
+/**
+ * A callback used to notify implementors of changes in the status of nearby devices that are able
+ * to play media.
+ *
+ * External clients may allow registration of these callbacks and external clients will be
+ * responsible for notifying the callbacks appropriately. System UI is only a mediator between the
+ * external client and these callbacks.
+ */
+interface INearbyMediaDevicesUpdateCallback {
+ /** Unknown distance range. */
+ const int RANGE_UNKNOWN = 0;
+ /** Distance is very far away from the peer device. */
+ const int RANGE_FAR = 1;
+ /** Distance is relatively long from the peer device, typically a few meters. */
+ const int RANGE_LONG = 2;
+ /** Distance is close to the peer device, typically with one or two meter. */
+ const int RANGE_CLOSE = 3;
+ /** Distance is very close to the peer device, typically within one meter or less. */
+ const int RANGE_WITHIN_REACH = 4;
+
+ /** Invoked by external clients when media device changes are detected. */
+ oneway void nearbyDeviceUpdate(in String routeId, in int rangeZone) = 1;
+}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
similarity index 89%
rename from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
rename to packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
index 861a4ed..62b50ed 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.aidl
@@ -14,6 +14,6 @@
* limitations under the License.
*/
-package com.android.systemui.shared.mediattt;
+package com.android.systemui.shared.media;
-parcelable DeviceInfo;
+parcelable NearbyDevice;
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
similarity index 86%
rename from packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt
rename to packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
index 96b853f..9cab3ab 100644
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyDevice.kt
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/NearbyDevice.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.media.nearby
+package com.android.systemui.shared.media
import android.os.Parcel
import android.os.Parcelable
@@ -26,14 +26,15 @@
* - [routeId] identifying the media route
* - [rangeZone] specifying how far away the device with the media route is from this device.
*/
-class NearbyDevice(parcel: Parcel) : Parcelable {
- var routeId: String? = null
+class NearbyDevice(
+ val routeId: String?,
@RangeZone val rangeZone: Int
+) : Parcelable {
- init {
- routeId = parcel.readString() ?: "unknown"
+ private constructor(parcel: Parcel) : this(
+ routeId = parcel.readString() ?: null,
rangeZone = parcel.readInt()
- }
+ )
override fun describeContents() = 0
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt
new file mode 100644
index 0000000..b5eaff6
--- /dev/null
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/media/RangeZone.kt
@@ -0,0 +1,31 @@
+/*
+ * 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.media
+
+import androidx.annotation.IntDef
+import kotlin.annotation.AnnotationRetention
+
+@IntDef(
+ INearbyMediaDevicesUpdateCallback.RANGE_UNKNOWN,
+ INearbyMediaDevicesUpdateCallback.RANGE_FAR,
+ INearbyMediaDevicesUpdateCallback.RANGE_LONG,
+ INearbyMediaDevicesUpdateCallback.RANGE_CLOSE,
+ INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
+)
+@Retention(AnnotationRetention.SOURCE)
+/** The various range zones a device can be in, in relation to the current device. */
+annotation class RangeZone
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/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
index 8d98a75..56326e3 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/utilities/Utilities.java
@@ -18,6 +18,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import android.annotation.TargetApi;
import android.content.Context;
@@ -110,11 +111,16 @@
hints &= ~NAVIGATION_HINT_BACK_ALT;
break;
}
- if (showImeSwitcher) {
+ if (imeShown) {
hints |= NAVIGATION_HINT_IME_SHOWN;
} else {
hints &= ~NAVIGATION_HINT_IME_SHOWN;
}
+ if (showImeSwitcher) {
+ hints |= NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ } else {
+ hints &= ~NAVIGATION_HINT_IME_SWITCHER_SHOWN;
+ }
return hints;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
index eebc791..08b4d3f 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/QuickStepContract.java
@@ -59,6 +59,7 @@
public static final String KEY_EXTRA_UNLOCK_ANIMATION_CONTROLLER = "unlock_animation";
// See IRecentTasks.aidl
public static final String KEY_EXTRA_RECENT_TASKS = "recent_tasks";
+ public static final String KEY_EXTRA_SHELL_BACK_ANIMATION = "extra_shell_back_animation";
public static final String NAV_BAR_MODE_3BUTTON_OVERLAY =
WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
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/keyguard/dagger/KeyguardStatusBarViewModule.java b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
index fc14b6a..8fc8600 100644
--- a/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
+++ b/packages/SystemUI/src/com/android/keyguard/dagger/KeyguardStatusBarViewModule.java
@@ -20,7 +20,11 @@
import com.android.systemui.R;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.statusbar.phone.KeyguardStatusBarView;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -39,4 +43,17 @@
static BatteryMeterView getBatteryMeterView(KeyguardStatusBarView view) {
return view.findViewById(R.id.battery);
}
+
+ /** */
+ @Provides
+ @KeyguardStatusBarViewScope
+ static StatusBarUserSwitcherContainer getUserSwitcherContainer(KeyguardStatusBarView view) {
+ return view.findViewById(R.id.user_switcher_container);
+ }
+
+ /** */
+ @Binds
+ @KeyguardStatusBarViewScope
+ abstract StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
+ StatusBarUserSwitcherControllerImpl controller);
}
diff --git a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
index 4a0c30c..3d0c08b 100644
--- a/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/SwipeHelper.java
@@ -39,6 +39,8 @@
import android.view.accessibility.AccessibilityEvent;
import com.android.systemui.animation.Interpolators;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -82,6 +84,7 @@
private final int mSwipeDirection;
private final VelocityTracker mVelocityTracker;
private final FalsingManager mFalsingManager;
+ private final FeatureFlags mFeatureFlags;
private float mInitialTouchPos;
private float mPerpendicularInitialTouchPos;
@@ -128,7 +131,8 @@
public SwipeHelper(
int swipeDirection, Callback callback, Resources resources,
- ViewConfiguration viewConfiguration, FalsingManager falsingManager) {
+ ViewConfiguration viewConfiguration, FalsingManager falsingManager,
+ FeatureFlags featureFlags) {
mCallback = callback;
mHandler = new Handler();
mSwipeDirection = swipeDirection;
@@ -146,6 +150,7 @@
mFadeDependingOnAmountSwiped = resources.getBoolean(
R.bool.config_fadeDependingOnAmountSwiped);
mFalsingManager = falsingManager;
+ mFeatureFlags = featureFlags;
mFlingAnimationUtils = new FlingAnimationUtils(resources.getDisplayMetrics(),
getMaxEscapeAnimDuration() / 1000f);
}
@@ -795,7 +800,7 @@
}
private boolean isAvailableToDragAndDrop(View v) {
- if (v.getResources().getBoolean(R.bool.config_notificationToContents)) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
if (v instanceof ExpandableNotificationRow) {
ExpandableNotificationRow enr = (ExpandableNotificationRow) v;
boolean canBubble = enr.getEntry().canBubble();
diff --git a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
index 20d6e32..881e6a9 100644
--- a/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
+++ b/packages/SystemUI/src/com/android/systemui/accessibility/SystemActions.java
@@ -120,6 +120,13 @@
AccessibilityService.GLOBAL_ACTION_TAKE_SCREENSHOT; // = 9
/**
+ * Action ID to send the KEYCODE_HEADSETHOOK KeyEvent, which is used to answer/hang up calls and
+ * play/stop media
+ */
+ private static final int SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK =
+ AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK; // = 10
+
+ /**
* Action ID to trigger the accessibility button
*/
public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON =
@@ -137,6 +144,36 @@
public static final int SYSTEM_ACTION_ID_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
AccessibilityService.GLOBAL_ACTION_DISMISS_NOTIFICATION_SHADE; // 15
+ /**
+ * Action ID to trigger the dpad up button
+ */
+ private static final int SYSTEM_ACTION_ID_DPAD_UP =
+ AccessibilityService.GLOBAL_ACTION_DPAD_UP; // 16
+
+ /**
+ * Action ID to trigger the dpad down button
+ */
+ private static final int SYSTEM_ACTION_ID_DPAD_DOWN =
+ AccessibilityService.GLOBAL_ACTION_DPAD_DOWN; // 17
+
+ /**
+ * Action ID to trigger the dpad left button
+ */
+ private static final int SYSTEM_ACTION_ID_DPAD_LEFT =
+ AccessibilityService.GLOBAL_ACTION_DPAD_LEFT; // 18
+
+ /**
+ * Action ID to trigger the dpad right button
+ */
+ private static final int SYSTEM_ACTION_ID_DPAD_RIGHT =
+ AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT; // 19
+
+ /**
+ * Action ID to trigger dpad center keyevent
+ */
+ private static final int SYSTEM_ACTION_ID_DPAD_CENTER =
+ AccessibilityService.GLOBAL_ACTION_DPAD_CENTER; // 20
+
private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
private final SystemActionsBroadcastReceiver mReceiver;
@@ -222,10 +259,34 @@
R.string.accessibility_system_action_screenshot_label,
SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT);
+ RemoteAction actionHeadsetHook = createRemoteAction(
+ R.string.accessibility_system_action_headset_hook_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK);
+
RemoteAction actionAccessibilityShortcut = createRemoteAction(
R.string.accessibility_system_action_hardware_a11y_shortcut_label,
SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
+ RemoteAction actionDpadUp = createRemoteAction(
+ R.string.accessibility_system_action_dpad_up_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP);
+
+ RemoteAction actionDpadDown = createRemoteAction(
+ R.string.accessibility_system_action_dpad_down_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN);
+
+ RemoteAction actionDpadLeft = createRemoteAction(
+ R.string.accessibility_system_action_dpad_left_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT);
+
+ RemoteAction actionDpadRight = createRemoteAction(
+ R.string.accessibility_system_action_dpad_right_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT);
+
+ RemoteAction actionDpadCenter = createRemoteAction(
+ R.string.accessibility_system_action_dpad_center_label,
+ SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER);
+
mA11yManager.registerSystemAction(actionBack, SYSTEM_ACTION_ID_BACK);
mA11yManager.registerSystemAction(actionHome, SYSTEM_ACTION_ID_HOME);
mA11yManager.registerSystemAction(actionRecents, SYSTEM_ACTION_ID_RECENTS);
@@ -234,8 +295,14 @@
mA11yManager.registerSystemAction(actionPowerDialog, SYSTEM_ACTION_ID_POWER_DIALOG);
mA11yManager.registerSystemAction(actionLockScreen, SYSTEM_ACTION_ID_LOCK_SCREEN);
mA11yManager.registerSystemAction(actionTakeScreenshot, SYSTEM_ACTION_ID_TAKE_SCREENSHOT);
+ mA11yManager.registerSystemAction(actionHeadsetHook, SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK);
mA11yManager.registerSystemAction(
actionAccessibilityShortcut, SYSTEM_ACTION_ID_ACCESSIBILITY_SHORTCUT);
+ mA11yManager.registerSystemAction(actionDpadUp, SYSTEM_ACTION_ID_DPAD_UP);
+ mA11yManager.registerSystemAction(actionDpadDown, SYSTEM_ACTION_ID_DPAD_DOWN);
+ mA11yManager.registerSystemAction(actionDpadLeft, SYSTEM_ACTION_ID_DPAD_LEFT);
+ mA11yManager.registerSystemAction(actionDpadRight, SYSTEM_ACTION_ID_DPAD_RIGHT);
+ mA11yManager.registerSystemAction(actionDpadCenter, SYSTEM_ACTION_ID_DPAD_CENTER);
registerOrUnregisterDismissNotificationShadeAction();
}
@@ -305,6 +372,10 @@
labelId = R.string.accessibility_system_action_screenshot_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_TAKE_SCREENSHOT;
break;
+ case SYSTEM_ACTION_ID_KEYCODE_HEADSETHOOK:
+ labelId = R.string.accessibility_system_action_headset_hook_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_HEADSET_HOOK;
+ break;
case SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON:
labelId = R.string.accessibility_system_action_on_screen_a11y_shortcut_label;
intent = SystemActionsBroadcastReceiver.INTENT_ACTION_ACCESSIBILITY_BUTTON;
@@ -323,6 +394,26 @@
intent = SystemActionsBroadcastReceiver
.INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE;
break;
+ case SYSTEM_ACTION_ID_DPAD_UP:
+ labelId = R.string.accessibility_system_action_dpad_up_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_UP;
+ break;
+ case SYSTEM_ACTION_ID_DPAD_DOWN:
+ labelId = R.string.accessibility_system_action_dpad_down_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_DOWN;
+ break;
+ case SYSTEM_ACTION_ID_DPAD_LEFT:
+ labelId = R.string.accessibility_system_action_dpad_left_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_LEFT;
+ break;
+ case SYSTEM_ACTION_ID_DPAD_RIGHT:
+ labelId = R.string.accessibility_system_action_dpad_right_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_RIGHT;
+ break;
+ case SYSTEM_ACTION_ID_DPAD_CENTER:
+ labelId = R.string.accessibility_system_action_dpad_center_label;
+ intent = SystemActionsBroadcastReceiver.INTENT_ACTION_DPAD_CENTER;
+ break;
default:
return;
}
@@ -411,6 +502,10 @@
SCREENSHOT_ACCESSIBILITY_ACTIONS, new Handler(Looper.getMainLooper()), null);
}
+ private void handleHeadsetHook() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
+ }
+
private void handleAccessibilityButton() {
AccessibilityManager.getInstance(mContext).notifyAccessibilityButtonClicked(
Display.DEFAULT_DISPLAY);
@@ -434,6 +529,26 @@
CommandQueue.FLAG_EXCLUDE_NONE, false /* force */));
}
+ private void handleDpadUp() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
+ }
+
+ private void handleDpadDown() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
+ }
+
+ private void handleDpadLeft() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
+ }
+
+ private void handleDpadRight() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
+ }
+
+ private void handleDpadCenter() {
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
+ }
+
private class SystemActionsBroadcastReceiver extends BroadcastReceiver {
private static final String INTENT_ACTION_BACK = "SYSTEM_ACTION_BACK";
private static final String INTENT_ACTION_HOME = "SYSTEM_ACTION_HOME";
@@ -443,6 +558,7 @@
private static final String INTENT_ACTION_POWER_DIALOG = "SYSTEM_ACTION_POWER_DIALOG";
private static final String INTENT_ACTION_LOCK_SCREEN = "SYSTEM_ACTION_LOCK_SCREEN";
private static final String INTENT_ACTION_TAKE_SCREENSHOT = "SYSTEM_ACTION_TAKE_SCREENSHOT";
+ private static final String INTENT_ACTION_HEADSET_HOOK = "SYSTEM_ACTION_HEADSET_HOOK";
private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON =
"SYSTEM_ACTION_ACCESSIBILITY_BUTTON";
private static final String INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER =
@@ -451,6 +567,11 @@
"SYSTEM_ACTION_ACCESSIBILITY_SHORTCUT";
private static final String INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE =
"SYSTEM_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE";
+ private static final String INTENT_ACTION_DPAD_UP = "SYSTEM_ACTION_DPAD_UP";
+ private static final String INTENT_ACTION_DPAD_DOWN = "SYSTEM_ACTION_DPAD_DOWN";
+ private static final String INTENT_ACTION_DPAD_LEFT = "SYSTEM_ACTION_DPAD_LEFT";
+ private static final String INTENT_ACTION_DPAD_RIGHT = "SYSTEM_ACTION_DPAD_RIGHT";
+ private static final String INTENT_ACTION_DPAD_CENTER = "SYSTEM_ACTION_DPAD_CENTER";
private PendingIntent createPendingIntent(Context context, String intentAction) {
switch (intentAction) {
@@ -462,10 +583,16 @@
case INTENT_ACTION_POWER_DIALOG:
case INTENT_ACTION_LOCK_SCREEN:
case INTENT_ACTION_TAKE_SCREENSHOT:
+ case INTENT_ACTION_HEADSET_HOOK:
case INTENT_ACTION_ACCESSIBILITY_BUTTON:
case INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER:
case INTENT_ACTION_ACCESSIBILITY_SHORTCUT:
- case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE: {
+ case INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE:
+ case INTENT_ACTION_DPAD_UP:
+ case INTENT_ACTION_DPAD_DOWN:
+ case INTENT_ACTION_DPAD_LEFT:
+ case INTENT_ACTION_DPAD_RIGHT:
+ case INTENT_ACTION_DPAD_CENTER: {
Intent intent = new Intent(intentAction);
intent.setPackage(context.getPackageName());
return PendingIntent.getBroadcast(context, 0, intent,
@@ -487,10 +614,16 @@
intentFilter.addAction(INTENT_ACTION_POWER_DIALOG);
intentFilter.addAction(INTENT_ACTION_LOCK_SCREEN);
intentFilter.addAction(INTENT_ACTION_TAKE_SCREENSHOT);
+ intentFilter.addAction(INTENT_ACTION_HEADSET_HOOK);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_BUTTON_CHOOSER);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_SHORTCUT);
intentFilter.addAction(INTENT_ACTION_ACCESSIBILITY_DISMISS_NOTIFICATION_SHADE);
+ intentFilter.addAction(INTENT_ACTION_DPAD_UP);
+ intentFilter.addAction(INTENT_ACTION_DPAD_DOWN);
+ intentFilter.addAction(INTENT_ACTION_DPAD_LEFT);
+ intentFilter.addAction(INTENT_ACTION_DPAD_RIGHT);
+ intentFilter.addAction(INTENT_ACTION_DPAD_CENTER);
return intentFilter;
}
@@ -530,6 +663,10 @@
handleTakeScreenshot();
break;
}
+ case INTENT_ACTION_HEADSET_HOOK: {
+ handleHeadsetHook();
+ break;
+ }
case INTENT_ACTION_ACCESSIBILITY_BUTTON: {
handleAccessibilityButton();
break;
@@ -546,6 +683,26 @@
handleAccessibilityDismissNotificationShade();
break;
}
+ case INTENT_ACTION_DPAD_UP: {
+ handleDpadUp();
+ break;
+ }
+ case INTENT_ACTION_DPAD_DOWN: {
+ handleDpadDown();
+ break;
+ }
+ case INTENT_ACTION_DPAD_LEFT: {
+ handleDpadLeft();
+ break;
+ }
+ case INTENT_ACTION_DPAD_RIGHT: {
+ handleDpadRight();
+ break;
+ }
+ case INTENT_ACTION_DPAD_CENTER: {
+ handleDpadCenter();
+ break;
+ }
default:
break;
}
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 12759f48..f6d6464 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/ClipboardOverlayController.java
@@ -74,7 +74,7 @@
import com.android.internal.policy.PhoneWindow;
import com.android.systemui.R;
import com.android.systemui.screenshot.FloatingWindowUtil;
-import com.android.systemui.screenshot.ScreenshotActionChip;
+import com.android.systemui.screenshot.OverlayActionChip;
import com.android.systemui.screenshot.TimeoutHandler;
import java.io.IOException;
@@ -103,15 +103,16 @@
private final AccessibilityManager mAccessibilityManager;
private final TextClassifier mTextClassifier;
+ private final FrameLayout mContainer;
private final DraggableConstraintLayout mView;
private final ImageView mImagePreview;
private final TextView mTextPreview;
- private final ScreenshotActionChip mEditChip;
- private final ScreenshotActionChip mRemoteCopyChip;
+ private final OverlayActionChip mEditChip;
+ private final OverlayActionChip mRemoteCopyChip;
private final View mActionContainerBackground;
private final View mDismissButton;
private final LinearLayout mActionContainer;
- private final ArrayList<ScreenshotActionChip> mActionChips = new ArrayList<>();
+ private final ArrayList<OverlayActionChip> mActionChips = new ArrayList<>();
private Runnable mOnSessionCompleteListener;
@@ -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);
@@ -251,7 +253,7 @@
for (RemoteAction action : actions) {
Intent targetIntent = action.getActionIntent().getIntent();
if (!TextUtils.equals(source, targetIntent.getComponent().getPackageName())) {
- ScreenshotActionChip chip = constructActionChip(action);
+ OverlayActionChip chip = constructActionChip(action);
mActionContainer.addView(chip);
mActionChips.add(chip);
}
@@ -259,9 +261,9 @@
});
}
- private ScreenshotActionChip constructActionChip(RemoteAction action) {
- ScreenshotActionChip chip = (ScreenshotActionChip) LayoutInflater.from(mContext).inflate(
- R.layout.screenshot_action_chip, mActionContainer, false);
+ private OverlayActionChip constructActionChip(RemoteAction action) {
+ OverlayActionChip chip = (OverlayActionChip) LayoutInflater.from(mContext).inflate(
+ R.layout.overlay_action_chip, mActionContainer, false);
chip.setText(action.getTitle());
chip.setIcon(action.getIcon(), false);
chip.setPendingIntent(action.getActionIntent(), this::animateOut);
@@ -341,7 +343,7 @@
mEditChip.setAlpha(1f);
ContentResolver resolver = mContext.getContentResolver();
try {
- int size = mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale);
+ int size = mContext.getResources().getDimensionPixelSize(R.dimen.overlay_x_scale);
// The width of the view is capped, height maintains aspect ratio, so allow it to be
// taller if needed.
Bitmap thumbnail = resolver.loadThumbnail(uri, new Size(size, size * 4), null);
@@ -365,13 +367,13 @@
}
private void animateOut() {
- getExitAnimation().start();
+ mView.dismiss();
}
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,35 +396,13 @@
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
- mView.setAlpha(1);
+ mContainer.setAlpha(1);
mTimeoutHandler.resetTimeout();
}
});
return anim;
}
- private ValueAnimator getExitAnimation() {
- ValueAnimator anim = ValueAnimator.ofFloat(0, 1);
-
- anim.addUpdateListener(animation -> {
- mView.setAlpha(1 - animation.getAnimatedFraction());
- final View actionBackground = requireNonNull(
- mView.findViewById(R.id.actions_container_background));
- mView.setTranslationX(
- -animation.getAnimatedFraction() * actionBackground.getWidth() / 2);
- });
-
- anim.addListener(new AnimatorListenerAdapter() {
- @Override
- public void onAnimationEnd(Animator animation) {
- super.onAnimationEnd(animation);
- hideImmediate();
- }
- });
-
- return anim;
- }
-
private void hideImmediate() {
// Note this may be called multiple times if multiple dismissal events happen at the same
// time.
@@ -453,7 +433,7 @@
}
private void resetActionChips() {
- for (ScreenshotActionChip chip : mActionChips) {
+ for (OverlayActionChip chip : mActionChips) {
mActionContainer.removeView(chip);
}
mActionChips.clear();
@@ -461,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/DraggableConstraintLayout.java b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
index 6a4be6e..8843462 100644
--- a/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/clipboardoverlay/DraggableConstraintLayout.java
@@ -98,10 +98,23 @@
return mSwipeDetector.onTouchEvent(ev);
}
+ /**
+ * Dismiss the view, with animation controlled by SwipeDismissHandler
+ */
+ public void dismiss() {
+ mSwipeDismissHandler.dismiss();
+ }
+
+ /**
+ * Set the callback to be run after view is dismissed
+ */
public void setOnDismissCallback(Runnable callback) {
mOnDismiss = callback;
}
+ /**
+ * Set the callback to be run when the view is interacted with (e.g. tapped)
+ */
public void setOnInteractionCallback(Runnable callback) {
mOnInteraction = callback;
}
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/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
index 5a52fd0..59c291c 100644
--- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt
@@ -437,7 +437,7 @@
* number of columns. This helps prevent text truncation on these devices.
*/
private fun findMaxColumns(): Int {
- val res = context.resources
+ val res = activityContext.resources
var maxColumns = res.getInteger(R.integer.controls_max_columns)
val maxColumnsAdjustWidth =
res.getInteger(R.integer.controls_max_columns_adjust_below_width_dp)
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
index 20cd5b9..68b74bd 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeTriggers.java
@@ -41,6 +41,7 @@
import com.android.systemui.doze.DozeMachine.State;
import com.android.systemui.doze.dagger.DozeScope;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.Assert;
@@ -96,6 +97,7 @@
private final AuthController mAuthController;
private final DelayableExecutor mMainExecutor;
private final KeyguardStateController mKeyguardStateController;
+ private final BatteryController mBatteryController;
private final UiEventLogger mUiEventLogger;
private final DevicePostureController mDevicePostureController;
@@ -187,7 +189,8 @@
@Main DelayableExecutor mainExecutor,
UiEventLogger uiEventLogger,
KeyguardStateController keyguardStateController,
- DevicePostureController devicePostureController) {
+ DevicePostureController devicePostureController,
+ BatteryController batteryController) {
mContext = context;
mDozeHost = dozeHost;
mConfig = config;
@@ -210,6 +213,7 @@
mMainExecutor = mainExecutor;
mUiEventLogger = uiEventLogger;
mKeyguardStateController = keyguardStateController;
+ mBatteryController = batteryController;
}
private final DevicePostureController.Callback mDevicePostureCallback =
posture -> {
@@ -320,7 +324,12 @@
gentleWakeUp(pulseReason);
} else if (isPickup) {
if (shouldDropPickupEvent()) {
- mDozeLog.traceSensorEventDropped(pulseReason, "keyguard occluded");
+ mDozeLog.traceSensorEventDropped(
+ pulseReason,
+ "keyguardOccluded="
+ + mKeyguardStateController.isOccluded()
+ + " pluggedInWireless="
+ + mBatteryController.isPluggedInWireless());
return;
}
gentleWakeUp(pulseReason);
@@ -351,7 +360,7 @@
}
private boolean shouldDropPickupEvent() {
- return mKeyguardStateController.isOccluded();
+ return mKeyguardStateController.isOccluded() || mBatteryController.isPluggedInWireless();
}
private void gentleWakeUp(int reason) {
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/ComplicationLayoutEngine.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
index 5223f37..0b80d8a 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/ComplicationLayoutEngine.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.complication;
+import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent.COMPLICATION_MARGIN;
import static com.android.systemui.dreams.complication.dagger.ComplicationHostViewComponent.SCOPED_COMPLICATIONS_LAYOUT;
import android.util.Log;
@@ -54,12 +55,14 @@
private final Parent mParent;
@Complication.Category
private final int mCategory;
+ private final int mMargin;
/**
* Default constructor. {@link Parent} allows for the {@link ViewEntry}'s surrounding
* view hierarchy to be accessed without traversing the entire view tree.
*/
- ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent) {
+ ViewEntry(View view, ComplicationLayoutParams layoutParams, int category, Parent parent,
+ int margin) {
mView = view;
// Views that are generated programmatically do not have a unique id assigned to them
// at construction. A new id is assigned here to enable ConstraintLayout relative
@@ -69,6 +72,7 @@
mLayoutParams = layoutParams;
mCategory = category;
mParent = parent;
+ mMargin = margin;
}
/**
@@ -173,6 +177,23 @@
}
break;
}
+
+ if (!isRoot) {
+ switch(direction) {
+ case ComplicationLayoutParams.DIRECTION_DOWN:
+ params.setMargins(0, mMargin, 0, 0);
+ break;
+ case ComplicationLayoutParams.DIRECTION_UP:
+ params.setMargins(0, 0, 0, mMargin);
+ break;
+ case ComplicationLayoutParams.DIRECTION_END:
+ params.setMarginStart(mMargin);
+ break;
+ case ComplicationLayoutParams.DIRECTION_START:
+ params.setMarginEnd(mMargin);
+ break;
+ }
+ }
});
mView.setLayoutParams(params);
@@ -224,6 +245,7 @@
private final ComplicationLayoutParams mLayoutParams;
private final int mCategory;
private Parent mParent;
+ private int mMargin;
Builder(View view, ComplicationLayoutParams lp, @Complication.Category int category) {
mView = view;
@@ -257,10 +279,19 @@
}
/**
+ * Sets the margin that will be applied in the direction the complication is laid out
+ * towards.
+ */
+ Builder setMargin(int margin) {
+ mMargin = margin;
+ return this;
+ }
+
+ /**
* Builds and returns the resulting {@link ViewEntry}.
*/
ViewEntry build() {
- return new ViewEntry(mView, mLayoutParams, mCategory, mParent);
+ return new ViewEntry(mView, mLayoutParams, mCategory, mParent, mMargin);
}
}
@@ -408,13 +439,16 @@
}
private final ConstraintLayout mLayout;
+ private final int mMargin;
private final HashMap<ComplicationId, ViewEntry> mEntries = new HashMap<>();
private final HashMap<Integer, PositionGroup> mPositions = new HashMap<>();
/** */
@Inject
- public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout) {
+ public ComplicationLayoutEngine(@Named(SCOPED_COMPLICATIONS_LAYOUT) ConstraintLayout layout,
+ @Named(COMPLICATION_MARGIN) int margin) {
mLayout = layout;
+ mMargin = margin;
}
/**
@@ -434,7 +468,8 @@
removeComplication(id);
}
- final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category);
+ final ViewEntry.Builder entryBuilder = new ViewEntry.Builder(view, lp, category)
+ .setMargin(mMargin);
// Add position group if doesn't already exist
final int position = lp.getPosition();
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/ComplicationHostViewComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java
index 4cc905e..20b0e50 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/ComplicationHostViewComponent.java
@@ -18,12 +18,14 @@
import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import android.content.res.Resources;
import android.view.LayoutInflater;
import androidx.constraintlayout.widget.ConstraintLayout;
import com.android.internal.util.Preconditions;
import com.android.systemui.R;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.dreams.complication.ComplicationHostViewController;
import java.lang.annotation.Documented;
@@ -48,7 +50,7 @@
@ComplicationHostViewComponent.ComplicationHostViewScope
public interface ComplicationHostViewComponent {
String SCOPED_COMPLICATIONS_LAYOUT = "scoped_complications_layout";
-
+ String COMPLICATION_MARGIN = "complication_margin";
/** Scope annotation for singleton items within {@link ComplicationHostViewComponent}. */
@Documented
@Retention(RUNTIME)
@@ -85,5 +87,12 @@
null),
"R.layout.dream_overlay_complications_layer did not properly inflated");
}
+
+ @Provides
+ @Named(COMPLICATION_MARGIN)
+ @ComplicationHostViewScope
+ static int providesComplicationPadding(@Main Resources resources) {
+ return resources.getDimensionPixelSize(R.dimen.dream_overlay_complication_margin);
+ }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
index eaffb1c..dd7f10c 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
@@ -104,8 +104,7 @@
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
- INSERT_ORDER_WEIGHT,
- true);
+ INSERT_ORDER_WEIGHT);
}
}
}
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..de11b61 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;
}
/**
@@ -104,8 +109,7 @@
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_UP,
- INSERT_ORDER_WEIGHT,
- true);
+ INSERT_ORDER_WEIGHT);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
index a282594..536f3dc 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
@@ -104,8 +104,7 @@
ComplicationLayoutParams.POSITION_BOTTOM
| ComplicationLayoutParams.POSITION_START,
ComplicationLayoutParams.DIRECTION_END,
- INSERT_ORDER_WEIGHT,
- true);
+ INSERT_ORDER_WEIGHT);
}
}
}
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..12c6e00 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.java
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.java
@@ -46,9 +46,6 @@
public static final BooleanFlag NOTIFICATION_PIPELINE_DEVELOPER_LOGGING =
new BooleanFlag(103, false);
- public static final ResourceBooleanFlag NOTIFICATION_SHADE_DRAG =
- new ResourceBooleanFlag(104, R.bool.config_enableNotificationShadeDrag);
-
public static final BooleanFlag NSSL_DEBUG_LINES =
new BooleanFlag(105, false);
@@ -58,6 +55,9 @@
public static final BooleanFlag NEW_PIPELINE_CRASH_ON_CALL_TO_OLD_PIPELINE =
new BooleanFlag(107, false);
+ public static final ResourceBooleanFlag NOTIFICATION_DRAG_TO_CONTENTS =
+ new ResourceBooleanFlag(108, R.bool.config_notificationToContents);
+
/***************************************/
// 200 - keyguard/lockscreen
@@ -107,13 +107,15 @@
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 =
new BooleanFlag(601, false);
- public static final BooleanFlag STATUS_BAR_USER_SWITCHER =
- new BooleanFlag(602, false);
+ public static final ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
+ new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
/***************************************/
// 700 - dialer/calls
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/KeyguardSliceProvider.java b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
index 30429fb..2a73797 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardSliceProvider.java
@@ -49,7 +49,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
-import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.SystemUIAppComponentFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -139,6 +138,8 @@
public StatusBarStateController mStatusBarStateController;
@Inject
public KeyguardBypassController mKeyguardBypassController;
+ @Inject
+ public KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private CharSequence mMediaTitle;
private CharSequence mMediaArtist;
protected boolean mDozing;
@@ -333,7 +334,7 @@
mAlarmManager.cancel(mUpdateNextAlarm);
if (mRegistered) {
mRegistered = false;
- getKeyguardUpdateMonitor().removeCallback(mKeyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.removeCallback(mKeyguardUpdateMonitorCallback);
getContext().unregisterReceiver(mIntentReceiver);
}
KeyguardSliceProvider.sInstance = null;
@@ -389,7 +390,7 @@
filter.addAction(Intent.ACTION_LOCALE_CHANGED);
getContext().registerReceiver(mIntentReceiver, filter, null /* permission*/,
null /* scheduler */);
- getKeyguardUpdateMonitor().registerCallback(mKeyguardUpdateMonitorCallback);
+ mKeyguardUpdateMonitor.registerCallback(mKeyguardUpdateMonitorCallback);
mRegistered = true;
}
}
@@ -441,10 +442,6 @@
updateNextAlarm();
}
- private KeyguardUpdateMonitor getKeyguardUpdateMonitor() {
- return Dependency.get(KeyguardUpdateMonitor.class);
- }
-
/**
* Called whenever new media metadata is available.
* @param metadata New metadata.
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/KeyguardMediaController.kt b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
index 44727f2..c3f4ce9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/KeyguardMediaController.kt
@@ -45,7 +45,8 @@
private val statusBarStateController: SysuiStatusBarStateController,
private val notifLockscreenUserManager: NotificationLockscreenUserManager,
private val context: Context,
- configurationController: ConfigurationController
+ configurationController: ConfigurationController,
+ private val mediaFlags: MediaFlags
) {
init {
@@ -61,7 +62,11 @@
})
// First let's set the desired state that we want for this host
- mediaHost.expansion = MediaHostState.COLLAPSED
+ mediaHost.expansion = if (mediaFlags.useMediaSessionLayout()) {
+ MediaHostState.EXPANDED
+ } else {
+ MediaHostState.COLLAPSED
+ }
mediaHost.showsOnlyActiveMedia = true
mediaHost.falsingProtectionNeeded = true
@@ -159,8 +164,7 @@
}
fun refreshMediaPosition() {
- val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD ||
- statusBarStateController.state == StatusBarState.FULLSCREEN_USER_SWITCHER)
+ val keyguardOrUserSwitcher = (statusBarStateController.state == StatusBarState.KEYGUARD)
// mediaHost.visible required for proper animations handling
visible = mediaHost.visible &&
!bypassController.bypassEnabled &&
@@ -196,4 +200,4 @@
visibilityChangedListener?.invoke(newVisibility == View.VISIBLE)
}
}
-}
\ No newline at end of file
+}
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..240ca36 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)
@@ -677,7 +657,10 @@
}
val runnable = if (action.actionIntent != null) {
Runnable {
- if (action.isAuthenticationRequired()) {
+ if (action.actionIntent.isActivity) {
+ activityStarter.startPendingIntentDismissingKeyguard(
+ action.actionIntent)
+ } else if (action.isAuthenticationRequired()) {
activityStarter.dismissKeyguardThenExecute({
var result = sendPendingIntent(action.actionIntent)
result
@@ -787,30 +770,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 +881,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/MediaHierarchyManager.kt b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
index 64ebe56..6145f0f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaHierarchyManager.kt
@@ -567,8 +567,7 @@
previousLocation = this.desiredLocation
} else if (forceStateUpdate) {
val onLockscreen = (!bypassController.bypassEnabled &&
- (statusbarState == StatusBarState.KEYGUARD ||
- statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+ (statusbarState == StatusBarState.KEYGUARD))
if (desiredLocation == LOCATION_QS && previousLocation == LOCATION_LOCKSCREEN &&
!onLockscreen) {
// If media active state changed and the device is now unlocked, update the
@@ -955,8 +954,7 @@
return desiredLocation
}
val onLockscreen = (!bypassController.bypassEnabled &&
- (statusbarState == StatusBarState.KEYGUARD ||
- statusbarState == StatusBarState.FULLSCREEN_USER_SWITCHER))
+ (statusbarState == StatusBarState.KEYGUARD))
val allowedOnLockscreen = notifLockscreenUserManager.shouldShowLockscreenNotifications()
val location = when {
dreamOverlayActive -> LOCATION_DREAM_OVERLAY
@@ -1087,4 +1085,4 @@
@IntDef(prefix = ["LOCATION_"], value = [MediaHierarchyManager.LOCATION_QS,
MediaHierarchyManager.LOCATION_QQS, MediaHierarchyManager.LOCATION_LOCKSCREEN])
@Retention(AnnotationRetention.SOURCE)
-annotation class MediaLocation
\ No newline at end of file
+annotation class MediaLocation
diff --git a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
index 791a312..591aad1 100644
--- a/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/MediaViewController.kt
@@ -471,14 +471,16 @@
private fun updateMediaViewControllerType(type: TYPE) {
this.type = type
+
+ // These XML resources contain ConstraintSets that will apply to this player type's layout
when (type) {
TYPE.PLAYER -> {
collapsedLayout.load(context, R.xml.media_collapsed)
expandedLayout.load(context, R.xml.media_expanded)
}
TYPE.PLAYER_SESSION -> {
- collapsedLayout.clone(context, R.layout.media_session_view)
- expandedLayout.clone(context, R.layout.media_session_view)
+ collapsedLayout.load(context, R.xml.media_session_collapsed)
+ expandedLayout.load(context, R.xml.media_session_expanded)
}
TYPE.RECOMMENDATION -> {
collapsedLayout.load(context, R.xml.media_recommendation_collapsed)
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 2bc910e..f8b34f9 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dagger/MediaModule.java
@@ -21,19 +21,22 @@
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;
import com.android.systemui.media.MediaHostStatesManager;
import com.android.systemui.media.dream.dagger.MediaComplicationComponent;
+import com.android.systemui.media.nearby.NearbyMediaDevicesService;
import com.android.systemui.media.taptotransfer.MediaTttCommandLineHelper;
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;
@@ -98,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));
}
/** */
@@ -111,12 +115,14 @@
@SysUISingleton
static Optional<MediaTttChipControllerReceiver> providesMediaTttChipControllerReceiver(
MediaTttFlags mediaTttFlags,
+ CommandQueue commandQueue,
Context context,
WindowManager windowManager) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
- return Optional.of(new MediaTttChipControllerReceiver(context, windowManager));
+ return Optional.of(
+ new MediaTttChipControllerReceiver(commandQueue, context, windowManager));
}
/** */
@@ -126,20 +132,17 @@
MediaTttFlags mediaTttFlags,
CommandRegistry commandRegistry,
Context context,
- MediaTttChipControllerReceiver mediaTttChipControllerReceiver) {
+ @Main Executor mainExecutor) {
if (!mediaTttFlags.isMediaTttEnabled()) {
return Optional.empty();
}
return Optional.of(
- new MediaTttCommandLineHelper(
- commandRegistry,
- context,
- mediaTttChipControllerReceiver));
+ new MediaTttCommandLineHelper(commandRegistry, context, mainExecutor));
}
- /** Inject into MediaTttSenderService. */
+ /** Inject into NearbyMediaDevicesService. */
@Binds
@IntoMap
- @ClassKey(MediaTttSenderService.class)
- Service bindMediaTttSenderService(MediaTttSenderService service);
+ @ClassKey(NearbyMediaDevicesService.class)
+ Service bindMediaNearbyDevicesService(NearbyMediaDevicesService service);
}
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/nearby/MediaNearbyDevicesManager.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt
deleted file mode 100644
index 0453fdb..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/MediaNearbyDevicesManager.kt
+++ /dev/null
@@ -1,51 +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.nearby
-
-import com.android.systemui.dagger.SysUISingleton
-
-/**
- * A manager that returns information about devices that are nearby and can receive media transfers.
- */
-@SysUISingleton
-class MediaNearbyDevicesManager {
-
- /** Returns a list containing the current nearby devices. */
- fun getCurrentNearbyDevices(): List<NearbyDevice> {
- // TODO(b/216313420): Implement this function.
- return emptyList()
- }
-
- /**
- * Registers [callback] to be notified each time a device's range changes or when a new device
- * comes within range.
- */
- fun registerNearbyDevicesCallback(
- callback: (device: NearbyDevice) -> Unit
- ) {
- // TODO(b/216313420): Implement this function.
- }
-
- /**
- * Un-registers [callback]. See [registerNearbyDevicesCallback].
- */
- fun unregisterNearbyDevicesCallback(
- callback: (device: NearbyDevice) -> Unit
- ) {
- // TODO(b/216313420): Implement this function.
- }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt
new file mode 100644
index 0000000..eaf2bd9
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/media/nearby/NearbyMediaDevicesService.kt
@@ -0,0 +1,81 @@
+/*
+ * 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.nearby
+
+import android.app.Service
+import android.content.Intent
+import android.os.IBinder
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider
+import com.android.systemui.shared.media.INearbyMediaDevicesService
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
+import com.android.systemui.shared.media.NearbyDevice
+import javax.inject.Inject
+
+/**
+ * A service that acts as a bridge between (1) external clients that have data on nearby devices
+ * that are able to play media and (2) internal clients (like media Output Switcher) that need data
+ * on these nearby devices.
+ *
+ * TODO(b/216313420): Add logging to this class.
+ */
+@SysUISingleton
+class NearbyMediaDevicesService @Inject constructor() : Service() {
+
+ private var provider: INearbyMediaDevicesProvider? = null
+
+ private val binder: IBinder = object : INearbyMediaDevicesService.Stub() {
+ override fun registerProvider(newProvider: INearbyMediaDevicesProvider) {
+ provider = newProvider
+ newProvider.asBinder().linkToDeath(
+ {
+ // We might've gotten a new provider before the old provider died, so we only
+ // need to clear our provider if the most recent provider died.
+ if (provider == newProvider) {
+ provider = null
+ }
+ },
+ /* flags= */ 0
+ )
+ }
+ }
+
+ override fun onBind(intent: Intent?): IBinder = binder
+
+ /** Returns a list containing the current nearby devices. */
+ fun getCurrentNearbyDevices(): List<NearbyDevice> {
+ val currentProvider = provider ?: return emptyList()
+ return currentProvider.currentNearbyDevices
+ }
+
+ /**
+ * Registers [callback] to be notified each time a device's range changes or when a new device
+ * comes within range.
+ */
+ fun registerNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
+ val currentProvider = provider ?: return
+ currentProvider.registerNearbyDevicesCallback(callback)
+ }
+
+ /**
+ * Un-registers [callback]. See [registerNearbyDevicesCallback].
+ */
+ fun unregisterNearbyDevicesCallback(callback: INearbyMediaDevicesUpdateCallback) {
+ val currentProvider = provider ?: return
+ currentProvider.unregisterNearbyDevicesCallback(callback)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt b/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt
deleted file mode 100644
index 3c890bc..0000000
--- a/packages/SystemUI/src/com/android/systemui/media/nearby/RangeZone.kt
+++ /dev/null
@@ -1,44 +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.nearby
-
-import androidx.annotation.IntDef
-import kotlin.annotation.AnnotationRetention
-
-@IntDef(
- RangeZone.RANGE_UNKNOWN,
- RangeZone.RANGE_FAR,
- RangeZone.RANGE_LONG,
- RangeZone.RANGE_CLOSE,
- RangeZone.RANGE_WITHIN_REACH
-)
-@Retention(AnnotationRetention.SOURCE)
-/** The various range zones a device can be in, in relation to the current device. */
-annotation class RangeZone {
- companion object {
- /** Unknown distance range. */
- const val RANGE_UNKNOWN = 0
- /** Distance is very far away from the peer device. */
- const val RANGE_FAR = 1
- /** Distance is relatively long from the peer device, typically a few meters. */
- const val RANGE_LONG = 2
- /** Distance is close to the peer device, typically with one or two meter. */
- const val RANGE_CLOSE = 3
- /** Distance is very close to the peer device, typically within one meter or less. */
- const val RANGE_WITHIN_REACH = 4
- }
-}
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..26f31cd 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,24 @@
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.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.dagger.qualifiers.Main
+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,210 +44,133 @@
class MediaTttCommandLineHelper @Inject constructor(
commandRegistry: CommandRegistry,
private val context: Context,
- private val mediaTttChipControllerReceiver: MediaTttChipControllerReceiver,
+ @Main private val mainExecutor: Executor
) {
- 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(
- ADD_CHIP_COMMAND_RECEIVER_TAG) { AddChipCommandReceiver() }
- commandRegistry.registerCommand(
- REMOVE_CHIP_COMMAND_RECEIVER_TAG) { RemoveChipCommandReceiver() }
+ commandRegistry.registerCommand(RECEIVER_COMMAND) { ReceiverCommand() }
}
/** 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
- )
- }
+ val commandName = args[1]
+ @StatusBarManager.MediaTransferSenderState
+ val displayState = stateStringToStateInt[commandName]
+ if (displayState == null) {
+ pw.println("Invalid command name $commandName")
+ return
}
+
+ val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
+ as StatusBarManager
+ statusBarManager.updateMediaTapToTransferSenderDisplay(
+ displayState,
+ routeInfo,
+ getUndoExecutor(displayState),
+ getUndoCallback(displayState)
+ )
}
- 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)
+ private fun getUndoExecutor(
+ @StatusBarManager.MediaTransferSenderState displayState: Int
+ ): Executor? {
+ return if (isSucceededState(displayState)) {
+ mainExecutor
} else {
- bindService(command)
+ null
}
}
- 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")
+ private fun getUndoCallback(
+ @StatusBarManager.MediaTransferSenderState displayState: Int
+ ): Runnable? {
+ return if (isSucceededState(displayState)) {
+ Runnable { Log.i(CLI_TAG, "Undo triggered for $displayState") }
+ } else {
+ null
+ }
}
- }
- /** A command to DISPLAY the media ttt chip on the RECEIVER device. */
- inner class AddChipCommandReceiver : Command {
- override fun execute(pw: PrintWriter, args: List<String>) {
- mediaTttChipControllerReceiver.displayChip(
- ChipStateReceiver(appIconDrawable, APP_ICON_CONTENT_DESCRIPTION)
- )
+ 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 $ADD_CHIP_COMMAND_RECEIVER_TAG")
+ pw.println("Usage: adb shell cmd statusbar $SENDER_COMMAND <deviceName> <chipState>")
}
}
- /** A command to REMOVE the media ttt chip on the RECEIVER device. */
- inner class RemoveChipCommandReceiver : Command {
+ /** All commands for the receiver device. */
+ inner class ReceiverCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
- mediaTttChipControllerReceiver.removeChip()
+ val statusBarManager = context.getSystemService(Context.STATUS_BAR_SERVICE)
+ as StatusBarManager
+ when(val commandName = args[0]) {
+ CLOSE_TO_SENDER_STATE ->
+ statusBarManager.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo
+ )
+ FAR_FROM_SENDER_STATE ->
+ statusBarManager.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo
+ )
+ else ->
+ pw.println("Invalid command name $commandName")
+ }
}
+
override fun help(pw: PrintWriter) {
- pw.println("Usage: adb shell cmd statusbar $REMOVE_CHIP_COMMAND_RECEIVER_TAG")
+ pw.println("Usage: adb shell cmd statusbar $RECEIVER_COMMAND <chipState>")
}
}
-
- /** 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"
+const val RECEIVER_COMMAND = "media-ttt-chip-receiver"
@VisibleForTesting
-const val ADD_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-add-receiver"
+const val FAR_FROM_RECEIVER_STATE = "FarFromReceiver"
@VisibleForTesting
-const val REMOVE_CHIP_COMMAND_RECEIVER_TAG = "media-ttt-chip-remove-receiver"
+const val CLOSE_TO_SENDER_STATE = "CloseToSender"
@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"
+const val FAR_FROM_SENDER_STATE = "FarFromSender"
+private const val CLI_TAG = "MediaTransferCli"
-private const val APP_ICON_CONTENT_DESCRIPTION = "Fake media app icon"
-private const val TAG = "MediaTapToTransferCli"
+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/receiver/MediaTttChipControllerReceiver.kt b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
index 1780954..2d3ca5f 100644
--- a/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiver.kt
@@ -16,12 +16,18 @@
package com.android.systemui.media.taptotransfer.receiver
+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.ViewGroup
import android.view.WindowManager
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
/**
@@ -31,13 +37,49 @@
*/
@SysUISingleton
class MediaTttChipControllerReceiver @Inject constructor(
+ commandQueue: CommandQueue,
context: Context,
windowManager: WindowManager,
) : MediaTttChipControllerCommon<ChipStateReceiver>(
context, windowManager, R.layout.media_ttt_chip_receiver
) {
+ // TODO(b/216141279): 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 updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState displayState: Int,
+ routeInfo: MediaRoute2Info
+ ) {
+ this@MediaTttChipControllerReceiver.updateMediaTapToTransferReceiverDisplay(
+ displayState, routeInfo
+ )
+ }
+ }
+
+ init {
+ commandQueue.addCallback(commandQueueCallbacks)
+ }
+
+ private fun updateMediaTapToTransferReceiverDisplay(
+ @StatusBarManager.MediaTransferReceiverState displayState: Int,
+ routeInfo: MediaRoute2Info
+ ) {
+ when(displayState) {
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER ->
+ displayChip(ChipStateReceiver(fakeAppIconDrawable, routeInfo.name.toString()))
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER -> removeChip()
+ else ->
+ Log.e(RECEIVER_TAG, "Unhandled MediaTransferReceiverState $displayState")
+ }
+ }
override fun updateChipView(chipState: ChipStateReceiver, currentChipView: ViewGroup) {
setIcon(chipState, currentChipView)
}
}
+
+private const val RECEIVER_TAG = "MediaTapToTransferReceiver"
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/navigationbar/NavBarHelper.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
index 42b7cc3..5e9edb7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavBarHelper.java
@@ -18,6 +18,8 @@
import static android.provider.Settings.Secure.ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU;
+import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON;
+import static com.android.systemui.accessibility.SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
@@ -39,6 +41,8 @@
import com.android.systemui.Dumpable;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dump.DumpManager;
@@ -70,13 +74,16 @@
@SysUISingleton
public final class NavBarHelper implements
AccessibilityButtonModeObserver.ModeChangedListener,
+ AccessibilityButtonTargetsObserver.TargetsChangedListener,
OverviewProxyService.OverviewProxyListener, NavigationModeController.ModeChangedListener,
Dumpable {
private final AccessibilityManager mAccessibilityManager;
private final Lazy<AssistManager> mAssistManagerLazy;
private final Lazy<Optional<StatusBar>> mStatusBarOptionalLazy;
private final UserTracker mUserTracker;
+ private final SystemActions mSystemActions;
private final AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
+ private final AccessibilityButtonTargetsObserver mAccessibilityButtonTargetsObserver;
private final List<NavbarTaskbarStateUpdater> mA11yEventListeners = new ArrayList<>();
private final Context mContext;
private ContentResolver mContentResolver;
@@ -84,12 +91,13 @@
private boolean mLongPressHomeEnabled;
private boolean mAssistantTouchGestureEnabled;
private int mNavBarMode;
+ private int mA11yButtonState;
private final ContentObserver mAssistContentObserver = new ContentObserver(
new Handler(Looper.getMainLooper())) {
@Override
public void onChange(boolean selfChange, Uri uri) {
- updateAssitantAvailability();
+ updateAssistantAvailability();
}
};
@@ -100,8 +108,9 @@
*/
@Inject
public NavBarHelper(Context context, AccessibilityManager accessibilityManager,
- AccessibilityManagerWrapper accessibilityManagerWrapper,
AccessibilityButtonModeObserver accessibilityButtonModeObserver,
+ AccessibilityButtonTargetsObserver accessibilityButtonTargetsObserver,
+ SystemActions systemActions,
OverviewProxyService overviewProxyService,
Lazy<AssistManager> assistManagerLazy,
Lazy<Optional<StatusBar>> statusBarOptionalLazy,
@@ -114,11 +123,14 @@
mAssistManagerLazy = assistManagerLazy;
mStatusBarOptionalLazy = statusBarOptionalLazy;
mUserTracker = userTracker;
- accessibilityManagerWrapper.addCallback(
+ mSystemActions = systemActions;
+ accessibilityManager.addAccessibilityServicesStateChangeListener(
accessibilityManager1 -> NavBarHelper.this.dispatchA11yEventUpdate());
mAccessibilityButtonModeObserver = accessibilityButtonModeObserver;
+ mAccessibilityButtonTargetsObserver = accessibilityButtonTargetsObserver;
mAccessibilityButtonModeObserver.addListener(this);
+ mAccessibilityButtonTargetsObserver.addListener(this);
mNavBarMode = navigationModeController.addListener(this);
overviewProxyService.addCallback(this);
dumpManager.registerDumpable(this);
@@ -134,7 +146,7 @@
mContentResolver.registerContentObserver(
Settings.Secure.getUriFor(Settings.Secure.ASSIST_TOUCH_GESTURE_ENABLED),
false, mAssistContentObserver, UserHandle.USER_ALL);
- updateAssitantAvailability();
+ updateAssistantAvailability();
}
public void destroy() {
@@ -168,9 +180,63 @@
@Override
public void onAccessibilityButtonModeChanged(int mode) {
+ updateA11yState();
dispatchA11yEventUpdate();
}
+ @Override
+ public void onAccessibilityButtonTargetsChanged(String targets) {
+ updateA11yState();
+ dispatchA11yEventUpdate();
+ }
+
+ /**
+ * Updates the current accessibility button state.
+ */
+ private void updateA11yState() {
+ final int prevState = mA11yButtonState;
+ final boolean clickable;
+ final boolean longClickable;
+ if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
+ == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
+ // If accessibility button is floating menu mode, click and long click state should be
+ // disabled.
+ clickable = false;
+ longClickable = false;
+ mA11yButtonState = 0;
+ } else {
+ // AccessibilityManagerService resolves services for the current user since the local
+ // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS
+ // permission
+ final List<String> a11yButtonTargets =
+ mAccessibilityManager.getAccessibilityShortcutTargets(
+ AccessibilityManager.ACCESSIBILITY_BUTTON);
+ final int requestingServices = a11yButtonTargets.size();
+
+ clickable = requestingServices >= 1;
+ longClickable = requestingServices >= 2;
+ mA11yButtonState = (clickable ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
+ | (longClickable ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ }
+
+ // Update the system actions if the state has changed
+ if (prevState != mA11yButtonState) {
+ updateSystemAction(clickable, SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON);
+ updateSystemAction(longClickable, SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER);
+ }
+ }
+
+ /**
+ * Registers/unregisters the given system action id.
+ */
+ private void updateSystemAction(boolean register, int actionId) {
+ if (register) {
+ mSystemActions.register(actionId);
+ } else {
+ mSystemActions.unregister(actionId);
+ }
+ }
+
/**
* See {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_CLICKABLE} and
* {@link QuickStepContract#SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE}
@@ -179,32 +245,17 @@
* a11y button in the navbar
*/
public int getA11yButtonState() {
- // AccessibilityManagerService resolves services for the current user since the local
- // AccessibilityManager is created from a Context with the INTERACT_ACROSS_USERS permission
- final List<String> a11yButtonTargets =
- mAccessibilityManager.getAccessibilityShortcutTargets(
- AccessibilityManager.ACCESSIBILITY_BUTTON);
- final int requestingServices = a11yButtonTargets.size();
-
- // If accessibility button is floating menu mode, click and long click state should be
- // disabled.
- if (mAccessibilityButtonModeObserver.getCurrentAccessibilityButtonMode()
- == ACCESSIBILITY_BUTTON_MODE_FLOATING_MENU) {
- return 0;
- }
-
- return (requestingServices >= 1 ? SYSUI_STATE_A11Y_BUTTON_CLICKABLE : 0)
- | (requestingServices >= 2 ? SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE : 0);
+ return mA11yButtonState;
}
@Override
public void onConnectionChanged(boolean isConnected) {
if (isConnected) {
- updateAssitantAvailability();
+ updateAssistantAvailability();
}
}
- private void updateAssitantAvailability() {
+ private void updateAssistantAvailability() {
boolean assistantAvailableForUser = mAssistManagerLazy.get()
.getAssistInfoForUser(UserHandle.USER_CURRENT) != null;
boolean longPressDefault = mContext.getResources().getBoolean(
@@ -236,7 +287,7 @@
@Override
public void onNavigationModeChanged(int mode) {
mNavBarMode = mode;
- updateAssitantAvailability();
+ updateAssistantAvailability();
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 1bef32a..d16c019 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.app.StatusBarManager.WindowType;
@@ -110,7 +110,6 @@
import com.android.internal.view.AppearanceRegion;
import com.android.systemui.R;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
-import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Main;
@@ -122,7 +121,6 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.recents.OverviewProxyService;
import com.android.systemui.recents.Recents;
-import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.recents.utilities.Utilities;
import com.android.systemui.shared.rotation.RotationButton;
import com.android.systemui.shared.rotation.RotationButtonController;
@@ -192,7 +190,6 @@
private final Optional<LegacySplitScreen> mSplitScreenOptional;
private final Optional<Recents> mRecentsOptional;
private final Optional<BackAnimation> mBackAnimation;
- private final SystemActions mSystemActions;
private final Handler mHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
@@ -307,7 +304,7 @@
new NavBarHelper.NavbarTaskbarStateUpdater() {
@Override
public void updateAccessibilityServicesState() {
- updateAcessibilityStateFlags();
+ updateAccessibilityStateFlags();
}
@Override
@@ -495,12 +492,10 @@
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
- SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
NavBarHelper navBarHelper,
- UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
AutoHideController mainAutoHideController,
@@ -528,7 +523,6 @@
mSplitScreenOptional = splitScreenOptional;
mRecentsOptional = recentsOptional;
mBackAnimation = backAnimation;
- mSystemActions = systemActions;
mHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
@@ -646,7 +640,7 @@
notifyNavigationBarScreenOn();
mOverviewProxyService.addCallback(mOverviewProxyListener);
- updateSystemUiStateFlags(-1);
+ updateSystemUiStateFlags();
// Currently there is no accelerometer sensor on non-default display.
if (mIsOnDefaultDisplay) {
@@ -904,7 +898,7 @@
mNavigationBarView.setNavigationIconHints(hints);
}
checkBarModes();
- updateSystemUiStateFlags(-1);
+ updateSystemUiStateFlags();
}
@Override
@@ -914,7 +908,7 @@
&& window == StatusBarManager.WINDOW_NAVIGATION_BAR
&& mNavigationBarWindowState != state) {
mNavigationBarWindowState = state;
- updateSystemUiStateFlags(-1);
+ updateSystemUiStateFlags();
mShowOrientedHandleForImmersiveMode = state == WINDOW_STATE_HIDDEN;
if (mOrientationHandle != null
&& mStartingQuickSwitchRotation != -1) {
@@ -993,7 +987,7 @@
if (mBehavior != behavior) {
mBehavior = behavior;
mNavigationBarView.setBehavior(behavior);
- updateSystemUiStateFlags(-1);
+ updateSystemUiStateFlags();
}
}
@@ -1160,7 +1154,7 @@
ButtonDispatcher accessibilityButton = mNavigationBarView.getAccessibilityButton();
accessibilityButton.setOnClickListener(this::onAccessibilityClick);
accessibilityButton.setOnLongClickListener(this::onAccessibilityLongClick);
- updateAcessibilityStateFlags();
+ updateAccessibilityStateFlags();
ButtonDispatcher imeSwitcherButton = mNavigationBarView.getImeSwitchButton();
imeSwitcherButton.setOnClickListener(this::onImeSwitcherClick);
@@ -1386,21 +1380,18 @@
return true;
}
- void updateAcessibilityStateFlags() {
- int a11yFlags = mNavBarHelper.getA11yButtonState();
-
+ void updateAccessibilityStateFlags() {
if (mNavigationBarView != null) {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
mNavigationBarView.setAccessibilityButtonState(clickable, longClickable);
}
- updateSystemUiStateFlags(a11yFlags);
+ updateSystemUiStateFlags();
}
- public void updateSystemUiStateFlags(int a11yFlags) {
- if (a11yFlags < 0) {
- a11yFlags = mNavBarHelper.getA11yButtonState();
- }
+ public void updateSystemUiStateFlags() {
+ int a11yFlags = mNavBarHelper.getA11yButtonState();
boolean clickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_CLICKABLE) != 0;
boolean longClickable = (a11yFlags & SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE) != 0;
@@ -1410,20 +1401,10 @@
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
- registerAction(clickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON);
- registerAction(longClickable, SystemActions.SYSTEM_ACTION_ID_ACCESSIBILITY_BUTTON_CHOOSER);
- }
-
- private void registerAction(boolean register, int actionId) {
- if (register) {
- mSystemActions.register(actionId);
- } else {
- mSystemActions.unregister(actionId);
- }
}
private void updateAssistantEntrypoints(boolean assistantAvailable) {
@@ -1641,7 +1622,7 @@
}
if (Intent.ACTION_USER_SWITCHED.equals(action)) {
// The accessibility settings may be different for the new user
- updateAcessibilityStateFlags();
+ updateAccessibilityStateFlags();
}
}
};
@@ -1673,12 +1654,10 @@
private final ShadeController mShadeController;
private final NotificationRemoteInputManager mNotificationRemoteInputManager;
private final NotificationShadeDepthController mNotificationShadeDepthController;
- private final SystemActions mSystemActions;
private final Handler mMainHandler;
private final NavigationBarOverlayController mNavbarOverlayController;
private final UiEventLogger mUiEventLogger;
private final NavBarHelper mNavBarHelper;
- private final UserTracker mUserTracker;
private final LightBarController mMainLightBarController;
private final LightBarController.Factory mLightBarControllerFactory;
private final AutoHideController mMainAutoHideController;
@@ -1707,12 +1686,10 @@
ShadeController shadeController,
NotificationRemoteInputManager notificationRemoteInputManager,
NotificationShadeDepthController notificationShadeDepthController,
- SystemActions systemActions,
@Main Handler mainHandler,
NavigationBarOverlayController navbarOverlayController,
UiEventLogger uiEventLogger,
NavBarHelper navBarHelper,
- UserTracker userTracker,
LightBarController mainLightBarController,
LightBarController.Factory lightBarControllerFactory,
AutoHideController mainAutoHideController,
@@ -1738,12 +1715,10 @@
mShadeController = shadeController;
mNotificationRemoteInputManager = notificationRemoteInputManager;
mNotificationShadeDepthController = notificationShadeDepthController;
- mSystemActions = systemActions;
mMainHandler = mainHandler;
mNavbarOverlayController = navbarOverlayController;
mUiEventLogger = uiEventLogger;
mNavBarHelper = navBarHelper;
- mUserTracker = userTracker;
mMainLightBarController = mainLightBarController;
mLightBarControllerFactory = lightBarControllerFactory;
mMainAutoHideController = mainAutoHideController;
@@ -1763,9 +1738,9 @@
mSysUiFlagsContainer, mBroadcastDispatcher, mCommandQueue, mPipOptional,
mSplitScreenOptional, mRecentsOptional, mStatusBarOptionalLazy,
mShadeController, mNotificationRemoteInputManager,
- mNotificationShadeDepthController, mSystemActions, mMainHandler,
+ mNotificationShadeDepthController, mMainHandler,
mNavbarOverlayController, mUiEventLogger, mNavBarHelper,
- mUserTracker, mMainLightBarController, mLightBarControllerFactory,
+ mMainLightBarController, mLightBarControllerFactory,
mMainAutoHideController, mAutoHideControllerFactory, mTelecomManagerOptional,
mInputMethodManager, mBackAnimation);
}
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
index 2dd89f3..593b278c 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBarView.java
@@ -774,11 +774,12 @@
updateRecentsIcon();
boolean isImeRenderingNavButtons = isGesturalMode(mNavBarMode)
- && mImeCanRenderGesturalNavButtons;
+ && mImeCanRenderGesturalNavButtons
+ && (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) != 0;
// Update IME button visibility, a11y and rotate button always overrides the appearance
boolean disableImeSwitcher =
- (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SHOWN) == 0
+ (mNavigationIconHints & StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN) == 0
|| isImeRenderingNavButtons;
mContextualButtonGroup.setButtonVisibility(R.id.ime_switcher, !disableImeSwitcher);
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
index ec15b24..75a3df7 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/TaskbarDelegate.java
@@ -17,7 +17,7 @@
package com.android.systemui.navigationbar;
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
-import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
import static android.view.InsetsState.ITYPE_EXTRA_NAVIGATION_BAR;
import static android.view.InsetsState.containsType;
@@ -293,7 +293,7 @@
.setFlag(SYSUI_STATE_IME_SHOWING,
(mNavigationIconHints & NAVIGATION_HINT_BACK_ALT) != 0)
.setFlag(SYSUI_STATE_IME_SWITCHER_SHOWING,
- (mNavigationIconHints & NAVIGATION_HINT_IME_SHOWN) != 0)
+ (mNavigationIconHints & NAVIGATION_HINT_IME_SWITCHER_SHOWN) != 0)
.setFlag(SYSUI_STATE_OVERVIEW_DISABLED,
(mDisabledFlags & View.STATUS_BAR_DISABLE_RECENT) != 0)
.setFlag(SYSUI_STATE_HOME_DISABLED,
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..58ebe89
--- /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, true)
+
+ 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/QSHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
index 9f585bd..7cf63f6 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSHost.java
@@ -19,7 +19,6 @@
import com.android.internal.logging.InstanceId;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.plugins.qs.QSTile;
-import com.android.systemui.qs.external.TileServices;
import java.util.Collection;
@@ -35,7 +34,6 @@
Collection<QSTile> getTiles();
void addCallback(Callback callback);
void removeCallback(Callback callback);
- TileServices getTileServices();
void removeTile(String tileSpec);
void removeTiles(Collection<String> specs);
void unmarkTileAsAutoAdded(String tileSpec);
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/QSTileHost.java b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
index cca4913..c693075 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QSTileHost.java
@@ -24,7 +24,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings.Secure;
-import android.service.quicksettings.Tile;
import android.text.TextUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -50,7 +49,6 @@
import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServiceRequestController;
-import com.android.systemui.qs.external.TileServices;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.shared.plugins.PluginManager;
@@ -89,7 +87,6 @@
private final Context mContext;
private final LinkedHashMap<String, QSTile> mTiles = new LinkedHashMap<>();
protected final ArrayList<String> mTileSpecs = new ArrayList<>();
- private final TileServices mServices;
private final TunerService mTunerService;
private final PluginManager mPluginManager;
private final DumpManager mDumpManager;
@@ -111,6 +108,7 @@
private SecureSettings mSecureSettings;
private final TileServiceRequestController mTileServiceRequestController;
+ private TileLifecycleManager.Factory mTileLifeCycleManagerFactory;
@Inject
public QSTileHost(Context context,
@@ -129,7 +127,8 @@
UserTracker userTracker,
SecureSettings secureSettings,
CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
+ TileLifecycleManager.Factory tileLifecycleManagerFactory
) {
mIconController = iconController;
mContext = context;
@@ -141,9 +140,9 @@
mUiEventLogger = uiEventLogger;
mBroadcastDispatcher = broadcastDispatcher;
mTileServiceRequestController = tileServiceRequestControllerBuilder.create(this);
+ mTileLifeCycleManagerFactory = tileLifecycleManagerFactory;
mInstanceIdSequence = new InstanceIdSequence(MAX_QS_INSTANCE_ID);
- mServices = new TileServices(this, bgLooper, mBroadcastDispatcher, userTracker);
mStatusBarOptional = statusBarOptional;
mQsFactories.add(defaultFactory);
@@ -177,7 +176,6 @@
mTiles.values().forEach(tile -> tile.destroy());
mAutoTiles.destroy();
mTunerService.removeTunable(this);
- mServices.destroy();
mPluginManager.removePluginListener(this);
mDumpManager.unregisterDumpable(TAG);
mTileServiceRequestController.destroy();
@@ -257,11 +255,6 @@
return mCurrentUser;
}
- @Override
- public TileServices getTileServices() {
- return mServices;
- }
-
public int indexOf(String spec) {
return mTileSpecs.indexOf(spec);
}
@@ -460,10 +453,8 @@
if (!newTiles.contains(tileSpec)) {
ComponentName component = CustomTile.getComponentFromSpec(tileSpec);
Intent intent = new Intent().setComponent(component);
- TileLifecycleManager lifecycleManager = new TileLifecycleManager(new Handler(),
- mContext, mServices, new Tile(), intent,
- new UserHandle(mCurrentUser),
- mBroadcastDispatcher);
+ TileLifecycleManager lifecycleManager = mTileLifeCycleManagerFactory.create(
+ intent, new UserHandle(mCurrentUser));
lifecycleManager.onStopListening();
lifecycleManager.onTileRemoved();
mCustomTileStatePersister.removeState(new TileServiceKey(component, mCurrentUser));
diff --git a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
index 92690c7d..a3af0e5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/QuickQSPanelController.java
@@ -17,13 +17,16 @@
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_COLLAPSED_LANDSCAPE_MEDIA;
import static com.android.systemui.qs.dagger.QSFragmentModule.QS_USING_MEDIA_PLAYER;
+import androidx.annotation.VisibleForTesting;
+
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.UiEventLogger;
import com.android.systemui.R;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.media.MediaFlags;
import com.android.systemui.media.MediaHierarchyManager;
import com.android.systemui.media.MediaHost;
import com.android.systemui.plugins.qs.QSTile;
@@ -32,6 +35,7 @@
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.settings.brightness.BrightnessMirrorHandler;
import com.android.systemui.statusbar.policy.BrightnessMirrorController;
+import com.android.systemui.util.leak.RotationUtils;
import java.util.ArrayList;
import java.util.List;
@@ -54,34 +58,53 @@
// brightness is visible only in split shade
private final QuickQSBrightnessController mBrightnessController;
private final BrightnessMirrorHandler mBrightnessMirrorHandler;
- private final FooterActionsController mFooterActionsController;
+
+ private final MediaFlags mMediaFlags;
+ private final boolean mUsingCollapsedLandscapeMedia;
@Inject
QuickQSPanelController(QuickQSPanel view, QSTileHost qsTileHost,
QSCustomizerController qsCustomizerController,
@Named(QS_USING_MEDIA_PLAYER) boolean usingMediaPlayer,
@Named(QUICK_QS_PANEL) MediaHost mediaHost,
+ @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA) boolean usingCollapsedLandscapeMedia,
+ MediaFlags mediaFlags,
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;
+ mUsingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia;
+ mMediaFlags = mediaFlags;
}
@Override
protected void onInit() {
super.onInit();
- mMediaHost.setExpansion(0.0f);
+ updateMediaExpansion();
mMediaHost.setShowsOnlyActiveMedia(true);
mMediaHost.init(MediaHierarchyManager.LOCATION_QQS);
mBrightnessController.init(mShouldUseSplitNotificationShade);
- mFooterActionsController.init();
- mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
+ }
+
+ private void updateMediaExpansion() {
+ int rotation = getRotation();
+ boolean isLandscape = rotation == RotationUtils.ROTATION_LANDSCAPE
+ || rotation == RotationUtils.ROTATION_SEASCAPE;
+ if (mMediaFlags.useMediaSessionLayout()
+ && (!mUsingCollapsedLandscapeMedia || !isLandscape)) {
+ mMediaHost.setExpansion(MediaHost.EXPANDED);
+ } else {
+ mMediaHost.setExpansion(MediaHost.COLLAPSED);
+ }
+ }
+
+ @VisibleForTesting
+ protected int getRotation() {
+ return RotationUtils.getRotation(getContext());
}
@Override
@@ -102,7 +125,6 @@
void setListening(boolean listening) {
super.setListening(listening);
mBrightnessController.setListening(listening);
- mFooterActionsController.setListening(listening);
}
public boolean isListening() {
@@ -123,7 +145,7 @@
@Override
protected void onConfigurationChanged() {
mBrightnessController.refreshVisibility(mShouldUseSplitNotificationShade);
- mFooterActionsController.refreshVisibility(mShouldUseSplitNotificationShade);
+ updateMediaExpansion();
}
@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..fdf9ae0 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSFragmentModule.java
@@ -16,20 +16,21 @@
package com.android.systemui.qs.dagger;
+import static com.android.systemui.util.Utils.useCollapsedMediaInLandscape;
import static com.android.systemui.util.Utils.useQsMediaPlayer;
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,9 +56,8 @@
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";
+ String QS_USING_COLLAPSED_LANDSCAPE_MEDIA = "qs_using_collapsed_landscape_media";
/**
* Provide a context themed using the QS theme
@@ -122,46 +122,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();
@@ -195,6 +175,13 @@
/** */
@Provides
+ @Named(QS_USING_COLLAPSED_LANDSCAPE_MEDIA)
+ static boolean providesQSUsingCollapsedLandscapeMedia(Context context) {
+ return useCollapsedMediaInLandscape(context.getResources());
+ }
+
+ /** */
+ @Provides
@QSScope
static OngoingPrivacyChip providesPrivacyChip(QuickStatusBarHeader qsHeader) {
return qsHeader.findViewById(R.id.privacy_chip);
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..5d4b3da 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/dagger/QSModule.java
@@ -28,14 +28,13 @@
import com.android.systemui.qs.QSHost;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.qs.ReduceBrightColorsController;
+import com.android.systemui.qs.external.QSExternalModule;
import com.android.systemui.statusbar.phone.AutoTileManager;
import com.android.systemui.statusbar.phone.ManagedProfileController;
import com.android.systemui.statusbar.policy.CastController;
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;
@@ -49,7 +48,7 @@
* Module for QS dependencies
*/
@Module(subcomponents = {QSFragmentComponent.class},
- includes = {MediaModule.class, QSFlagsModule.class})
+ includes = {MediaModule.class, QSExternalModule.class, QSFlagsModule.class})
public interface QSModule {
@Provides
@@ -91,9 +90,4 @@
/** */
@Binds
QSHost provideQsHost(QSTileHost controllerImpl);
-
- /** */
- @Binds
- RunningFgsController provideRunningFgsController(
- RunningFgsControllerImpl runningFgsController);
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
index 4f15351..c4386ab 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java
@@ -51,7 +51,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
-import com.android.systemui.Dependency;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.plugins.ActivityStarter;
@@ -103,6 +102,7 @@
private final TileServiceKey mKey;
private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false);
+ private final TileServices mTileServices;
private CustomTile(
QSHost host,
@@ -115,10 +115,12 @@
QSLogger qsLogger,
String action,
Context userContext,
- CustomTileStatePersister customTileStatePersister
+ CustomTileStatePersister customTileStatePersister,
+ TileServices tileServices
) {
super(host, backgroundLooper, mainHandler, falsingManager, metricsLogger,
statusBarStateController, activityStarter, qsLogger);
+ mTileServices = tileServices;
mWindowManager = WindowManagerGlobal.getWindowManagerService();
mComponent = ComponentName.unflattenFromString(action);
mTile = new Tile();
@@ -126,7 +128,7 @@
mUser = mUserContext.getUserId();
mKey = new TileServiceKey(mComponent, mUser);
- mServiceManager = host.getTileServices().getTileWrapper(this);
+ mServiceManager = tileServices.getTileWrapper(this);
mService = mServiceManager.getTileService();
mCustomTileStatePersister = customTileStatePersister;
}
@@ -349,7 +351,7 @@
} catch (RemoteException e) {
}
}
- mHost.getTileServices().freeService(this, mServiceManager);
+ mTileServices.freeService(this, mServiceManager);
}
@Override
@@ -473,7 +475,7 @@
}
public void startUnlockAndRun() {
- Dependency.get(ActivityStarter.class).postQSRunnableDismissingKeyguard(() -> {
+ mActivityStarter.postQSRunnableDismissingKeyguard(() -> {
try {
mService.onUnlockComplete();
} catch (RemoteException e) {
@@ -529,6 +531,7 @@
final ActivityStarter mActivityStarter;
final QSLogger mQSLogger;
final CustomTileStatePersister mCustomTileStatePersister;
+ private TileServices mTileServices;
Context mUserContext;
String mSpec = "";
@@ -543,7 +546,8 @@
StatusBarStateController statusBarStateController,
ActivityStarter activityStarter,
QSLogger qsLogger,
- CustomTileStatePersister customTileStatePersister
+ CustomTileStatePersister customTileStatePersister,
+ TileServices tileServices
) {
mQSHostLazy = hostLazy;
mBackgroundLooper = backgroundLooper;
@@ -554,6 +558,7 @@
mActivityStarter = activityStarter;
mQSLogger = qsLogger;
mCustomTileStatePersister = customTileStatePersister;
+ mTileServices = tileServices;
}
Builder setSpec(@NonNull String spec) {
@@ -583,7 +588,8 @@
mQSLogger,
action,
mUserContext,
- mCustomTileStatePersister
+ mCustomTileStatePersister,
+ mTileServices
);
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
index ffe66f4..6cf4441 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/PackageManagerAdapter.java
@@ -26,6 +26,8 @@
import android.content.pm.ServiceInfo;
import android.os.RemoteException;
+import javax.inject.Inject;
+
// Adapter that wraps calls to PackageManager or IPackageManager for {@link TileLifecycleManager}.
// TODO: This is very much an intermediate step to allow for PackageManager mocking and should be
// abstracted into something more useful for other tests in systemui.
@@ -37,6 +39,7 @@
// Uses the PackageManager for the provided context.
// When necessary, uses the IPackagemanger in AppGlobals.
+ @Inject
public PackageManagerAdapter(Context context) {
mPackageManager = context.getPackageManager();
mIPackageManager = AppGlobals.getPackageManager();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl b/packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt
similarity index 71%
copy from packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
copy to packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt
index 861a4ed..f7db80b 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/mediattt/DeviceInfo.aidl
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/QSExternalModule.kt
@@ -14,6 +14,14 @@
* limitations under the License.
*/
-package com.android.systemui.shared.mediattt;
+package com.android.systemui.qs.external
-parcelable DeviceInfo;
+import android.service.quicksettings.IQSService
+import dagger.Binds
+import dagger.Module
+
+@Module
+interface QSExternalModule {
+ @Binds
+ fun bindsIQSService(impl: TileServices): IQSService
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 17ae7ff..32e0805 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -31,21 +31,24 @@
import android.os.UserHandle;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.Nullable;
-import androidx.annotation.VisibleForTesting;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
/**
* Manages the lifecycle of a TileService.
* <p>
@@ -102,16 +105,10 @@
// Return value from bindServiceAsUser, determines whether safe to call unbind.
private boolean mIsBound;
- public TileLifecycleManager(Handler handler, Context context, IQSService service, Tile tile,
- Intent intent, UserHandle user, BroadcastDispatcher broadcastDispatcher) {
- this(handler, context, service, tile, intent, user, new PackageManagerAdapter(context),
- broadcastDispatcher);
- }
-
- @VisibleForTesting
- TileLifecycleManager(Handler handler, Context context, IQSService service, Tile tile,
- Intent intent, UserHandle user, PackageManagerAdapter packageManagerAdapter,
- BroadcastDispatcher broadcastDispatcher) {
+ @AssistedInject
+ TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
+ PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
+ @Assisted Intent intent, @Assisted UserHandle user) {
mContext = context;
mHandler = handler;
mIntent = intent;
@@ -123,6 +120,13 @@
if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
+ /** Injectable factory for TileLifecycleManager. */
+ @AssistedFactory
+ public interface Factory {
+ /** */
+ TileLifecycleManager create(Intent intent, UserHandle userHandle);
+ }
+
public ComponentName getComponent() {
return mIntent.getComponent();
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index fda755b..bf565a8 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -26,7 +26,6 @@
import android.os.Handler;
import android.os.IBinder;
import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.util.Log;
@@ -73,10 +72,11 @@
private boolean mStarted = false;
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
- Tile tile, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
+ BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
this(tileServices, handler, userTracker, new TileLifecycleManager(handler,
- tileServices.getContext(), tileServices, tile, new Intent().setComponent(component),
- userTracker.getUserHandle(), broadcastDispatcher));
+ tileServices.getContext(), tileServices,
+ new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
+ new Intent().setComponent(component), userTracker.getUserHandle()));
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index 0a3c17c..32515a2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -38,8 +38,8 @@
import androidx.annotation.Nullable;
import com.android.internal.statusbar.StatusBarIcon;
-import com.android.systemui.Dependency;
import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.qs.QSTileHost;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.statusbar.phone.StatusBarIconController;
@@ -50,6 +50,8 @@
import java.util.Comparator;
import java.util.Objects;
+import javax.inject.Inject;
+
/**
* Runs the day-to-day operations of which tiles should be bound and when.
*/
@@ -65,14 +67,21 @@
private final Handler mHandler;
private final Handler mMainHandler;
private final QSTileHost mHost;
+ private final KeyguardStateController mKeyguardStateController;
private final BroadcastDispatcher mBroadcastDispatcher;
private final UserTracker mUserTracker;
private int mMaxBound = DEFAULT_MAX_BOUND;
- public TileServices(QSTileHost host, Looper looper, BroadcastDispatcher broadcastDispatcher,
- UserTracker userTracker) {
+ @Inject
+ public TileServices(
+ QSTileHost host,
+ @Main Looper looper,
+ BroadcastDispatcher broadcastDispatcher,
+ UserTracker userTracker,
+ KeyguardStateController keyguardStateController) {
mHost = host;
+ mKeyguardStateController = keyguardStateController;
mContext = mHost.getContext();
mBroadcastDispatcher = broadcastDispatcher;
mHandler = new Handler(looper);
@@ -96,8 +105,7 @@
public TileServiceManager getTileWrapper(CustomTile tile) {
ComponentName component = tile.getComponent();
- TileServiceManager service = onCreateTileService(component, tile.getQsTile(),
- mBroadcastDispatcher);
+ TileServiceManager service = onCreateTileService(component, mBroadcastDispatcher);
synchronized (mServices) {
mServices.put(tile, service);
mTiles.put(component, tile);
@@ -108,9 +116,9 @@
return service;
}
- protected TileServiceManager onCreateTileService(ComponentName component, Tile tile,
+ protected TileServiceManager onCreateTileService(ComponentName component,
BroadcastDispatcher broadcastDispatcher) {
- return new TileServiceManager(this, mHandler, component, tile,
+ return new TileServiceManager(this, mHandler, component,
broadcastDispatcher, mUserTracker);
}
@@ -321,16 +329,12 @@
@Override
public boolean isLocked() {
- KeyguardStateController keyguardStateController =
- Dependency.get(KeyguardStateController.class);
- return keyguardStateController.isShowing();
+ return mKeyguardStateController.isShowing();
}
@Override
public boolean isSecure() {
- KeyguardStateController keyguardStateController =
- Dependency.get(KeyguardStateController.class);
- return keyguardStateController.isMethodSecure() && keyguardStateController.isShowing();
+ return mKeyguardStateController.isMethodSecure() && mKeyguardStateController.isShowing();
}
@Nullable
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
index 821dfa5f..a712ce2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileViewImpl.kt
@@ -25,6 +25,7 @@
import android.content.res.Resources.ID_NULL
import android.graphics.drawable.Drawable
import android.graphics.drawable.RippleDrawable
+import android.os.Trace
import android.service.quicksettings.Tile
import android.text.TextUtils
import android.util.Log
@@ -163,6 +164,12 @@
updateResources()
}
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ Trace.traceBegin(Trace.TRACE_TAG_APP, "QSTileViewImpl#onMeasure")
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ Trace.endSection()
+ }
+
override fun resetOverride() {
heightOverride = HeightOverrideable.NO_OVERRIDE
updateHeight()
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
index 77c61a4..597f7b7 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyRecentsImpl.java
@@ -112,8 +112,6 @@
final Optional<StatusBar> statusBarOptional = mStatusBarOptionalLazy.get();
if (statusBarOptional.map(StatusBar::isKeyguardShowing).orElse(false)) {
statusBarOptional.get().executeRunnableDismissingKeyguard(() -> {
- // Flush trustmanager before checking device locked per user
- mTrustManager.reportKeyguardShowingChanged();
mHandler.post(toggleRecents);
}, null, true /* dismissShade */, false /* afterKeyguardGone */,
true /* deferred */);
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 00a3149..6b251b0 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -26,6 +26,7 @@
import static com.android.internal.accessibility.common.ShortcutConstants.CHOOSER_PACKAGE_NAME;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_RECENT_TASKS;
+import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_BACK_ANIMATION;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_ONE_HANDED;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_PIP;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SHELL_SHELL_TRANSITIONS;
@@ -104,6 +105,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarWindowCallback;
import com.android.systemui.statusbar.policy.CallbackController;
+import com.android.wm.shell.back.BackAnimation;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreen;
import com.android.wm.shell.onehanded.OneHanded;
import com.android.wm.shell.pip.Pip;
@@ -163,6 +165,7 @@
private final Optional<StartingSurface> mStartingSurface;
private final KeyguardUnlockAnimationController mSysuiUnlockAnimationController;
private final Optional<RecentTasks> mRecentTasks;
+ private final Optional<BackAnimation> mBackAnimation;
private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
@@ -508,6 +511,9 @@
mRecentTasks.ifPresent(recentTasks -> params.putBinder(
KEY_EXTRA_RECENT_TASKS,
recentTasks.createExternalInterface().asBinder()));
+ mBackAnimation.ifPresent((backAnimation) -> params.putBinder(
+ KEY_EXTRA_SHELL_BACK_ANIMATION,
+ backAnimation.createExternalInterface().asBinder()));
try {
mOverviewProxy.onInitialize(params);
@@ -566,6 +572,7 @@
Optional<SplitScreen> splitScreenOptional,
Optional<OneHanded> oneHandedOptional,
Optional<RecentTasks> recentTasks,
+ Optional<BackAnimation> backAnimation,
Optional<StartingSurface> startingSurface,
BroadcastDispatcher broadcastDispatcher,
ShellTransitions shellTransitions,
@@ -593,6 +600,7 @@
mOneHandedOptional = oneHandedOptional;
mShellTransitions = shellTransitions;
mRecentTasks = recentTasks;
+ mBackAnimation = backAnimation;
mUiEventLogger = uiEventLogger;
// Assumes device always starts with back button until launcher tells it that it does not
@@ -677,7 +685,7 @@
}
if (navBarFragment != null) {
- navBarFragment.updateSystemUiStateFlags(-1);
+ navBarFragment.updateSystemUiStateFlags();
}
if (navBarView != null) {
navBarView.updateDisabledSystemUiStateFlags();
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
similarity index 84%
rename from packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
rename to packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
index dec5afd..c4ea67e 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotActionChip.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/OverlayActionChip.java
@@ -16,6 +16,8 @@
package com.android.systemui.screenshot;
+import static java.util.Objects.requireNonNull;
+
import android.app.PendingIntent;
import android.content.Context;
import android.graphics.drawable.Icon;
@@ -28,10 +30,11 @@
import com.android.systemui.R;
+
/**
* View for a chip with an icon and text.
*/
-public class ScreenshotActionChip extends FrameLayout {
+public class OverlayActionChip extends FrameLayout {
private static final String TAG = "ScreenshotActionChip";
@@ -39,27 +42,27 @@
private TextView mTextView;
private boolean mIsPending = false;
- public ScreenshotActionChip(Context context) {
+ public OverlayActionChip(Context context) {
this(context, null);
}
- public ScreenshotActionChip(Context context, AttributeSet attrs) {
+ public OverlayActionChip(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
- public ScreenshotActionChip(Context context, AttributeSet attrs, int defStyleAttr) {
+ public OverlayActionChip(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
- public ScreenshotActionChip(
+ public OverlayActionChip(
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
protected void onFinishInflate() {
- mIconView = findViewById(R.id.screenshot_action_chip_icon);
- mTextView = findViewById(R.id.screenshot_action_chip_text);
+ mIconView = requireNonNull(findViewById(R.id.overlay_action_chip_icon));
+ mTextView = requireNonNull(findViewById(R.id.overlay_action_chip_text));
updatePadding(mTextView.getText().length() > 0);
}
@@ -116,15 +119,15 @@
(LinearLayout.LayoutParams) mTextView.getLayoutParams();
if (hasText) {
int paddingHorizontal = mContext.getResources().getDimensionPixelSize(
- R.dimen.screenshot_action_chip_padding_horizontal);
+ R.dimen.overlay_action_chip_padding_horizontal);
int spacing = mContext.getResources().getDimensionPixelSize(
- R.dimen.screenshot_action_chip_spacing);
+ R.dimen.overlay_action_chip_spacing);
iconParams.setMarginStart(paddingHorizontal);
iconParams.setMarginEnd(spacing);
textParams.setMarginEnd(paddingHorizontal);
} else {
int paddingHorizontal = mContext.getResources().getDimensionPixelSize(
- R.dimen.screenshot_action_chip_icon_only_padding_horizontal);
+ R.dimen.overlay_action_chip_icon_only_padding_horizontal);
iconParams.setMarginStart(paddingHorizontal);
iconParams.setMarginEnd(paddingHorizontal);
}
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/screenshot/ScreenshotView.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
index e5649a1..f982790 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScreenshotView.java
@@ -131,9 +131,9 @@
private final Resources mResources;
private final Interpolator mFastOutSlowIn;
private final DisplayMetrics mDisplayMetrics;
- private final float mCornerSizeX;
- private final float mDismissDeltaY;
+ private final float mFixedSize;
private final AccessibilityManager mAccessibilityManager;
+ private final GestureDetector mSwipeDetector;
private int mNavMode;
private boolean mOrientationPortrait;
@@ -151,23 +151,21 @@
private LinearLayout mActionsView;
private ImageView mBackgroundProtection;
private FrameLayout mDismissButton;
- private ScreenshotActionChip mShareChip;
- private ScreenshotActionChip mEditChip;
- private ScreenshotActionChip mScrollChip;
- private ScreenshotActionChip mQuickShareChip;
+ private OverlayActionChip mShareChip;
+ private OverlayActionChip mEditChip;
+ private OverlayActionChip mScrollChip;
+ private OverlayActionChip mQuickShareChip;
private UiEventLogger mUiEventLogger;
private ScreenshotViewCallback mCallbacks;
- private Animator mDismissAnimation;
private boolean mPendingSharedTransition;
- private GestureDetector mSwipeDetector;
private SwipeDismissHandler mSwipeDismissHandler;
private InputMonitorCompat mInputMonitor;
private InputChannelCompat.InputEventReceiver mInputEventReceiver;
private boolean mShowScrollablePreview;
private String mPackageName = "";
- private final ArrayList<ScreenshotActionChip> mSmartChips = new ArrayList<>();
+ private final ArrayList<OverlayActionChip> mSmartChips = new ArrayList<>();
private PendingInteraction mPendingInteraction;
private enum PendingInteraction {
@@ -194,9 +192,7 @@
super(context, attrs, defStyleAttr, defStyleRes);
mResources = mContext.getResources();
- mCornerSizeX = mResources.getDimensionPixelSize(R.dimen.screenshot_x_scale);
- mDismissDeltaY = mResources.getDimensionPixelSize(
- R.dimen.screenshot_dismissal_height_delta);
+ mFixedSize = mResources.getDimensionPixelSize(R.dimen.overlay_x_scale);
// standard material ease
mFastOutSlowIn =
@@ -474,16 +470,14 @@
int orientation = mContext.getResources().getConfiguration().orientation;
mOrientationPortrait = (orientation == ORIENTATION_PORTRAIT);
updateInsets(insets);
- int screenshotFixedSize =
- mContext.getResources().getDimensionPixelSize(R.dimen.screenshot_x_scale);
ViewGroup.LayoutParams params = mScreenshotPreview.getLayoutParams();
if (mOrientationPortrait) {
- params.width = screenshotFixedSize;
+ params.width = (int) mFixedSize;
params.height = LayoutParams.WRAP_CONTENT;
mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_START);
} else {
params.width = LayoutParams.WRAP_CONTENT;
- params.height = screenshotFixedSize;
+ params.height = (int) mFixedSize;
mScreenshotPreview.setScaleType(ImageView.ScaleType.FIT_END);
}
@@ -500,7 +494,7 @@
// ratio of preview width, end vs. start size
float cornerScale =
- mCornerSizeX / (mOrientationPortrait ? bounds.width() : bounds.height());
+ mFixedSize / (mOrientationPortrait ? bounds.width() : bounds.height());
final float currentScale = 1 / cornerScale;
AnimatorSet dropInAnimation = new AnimatorSet();
@@ -651,7 +645,7 @@
} catch (RemoteException e) {
}
- ArrayList<ScreenshotActionChip> chips = new ArrayList<>();
+ ArrayList<OverlayActionChip> chips = new ArrayList<>();
mShareChip.setContentDescription(mContext.getString(R.string.screenshot_share_description));
mShareChip.setIcon(Icon.createWithResource(mContext, R.drawable.ic_screenshot_share), true);
@@ -716,7 +710,7 @@
+ (t * (1 - SCREENSHOT_ACTIONS_START_SCALE_X));
mActionsContainer.setScaleX(containerScale);
mActionsContainerBackground.setScaleX(containerScale);
- for (ScreenshotActionChip chip : chips) {
+ for (OverlayActionChip chip : chips) {
chip.setAlpha(t);
chip.setScaleX(1 / containerScale); // invert to keep size of children constant
}
@@ -772,8 +766,8 @@
LayoutInflater inflater = LayoutInflater.from(mContext);
for (Notification.Action smartAction : imageData.smartActions) {
- ScreenshotActionChip actionChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.screenshot_action_chip, mActionsView, false);
+ OverlayActionChip actionChip = (OverlayActionChip) inflater.inflate(
+ R.layout.overlay_action_chip, mActionsView, false);
actionChip.setText(smartAction.title);
actionChip.setIcon(smartAction.getIcon(), false);
actionChip.setPendingIntent(smartAction.actionIntent,
@@ -792,8 +786,8 @@
void addQuickShareChip(Notification.Action quickShareAction) {
if (mPendingInteraction == null) {
LayoutInflater inflater = LayoutInflater.from(mContext);
- mQuickShareChip = (ScreenshotActionChip) inflater.inflate(
- R.layout.screenshot_action_chip, mActionsView, false);
+ mQuickShareChip = (OverlayActionChip) inflater.inflate(
+ R.layout.overlay_action_chip, mActionsView, false);
mQuickShareChip.setText(quickShareAction.title);
mQuickShareChip.setIcon(quickShareAction.getIcon(), false);
mQuickShareChip.setOnClickListener(v -> {
@@ -894,7 +888,7 @@
if (mShowScrollablePreview) {
Rect scrollableArea = scrollableAreaOnScreen(response);
- float scale = mCornerSizeX
+ float scale = mFixedSize
/ (mOrientationPortrait ? screenBitmap.getWidth() : screenBitmap.getHeight());
ConstraintLayout.LayoutParams params =
(ConstraintLayout.LayoutParams) mScrollablePreview.getLayoutParams();
@@ -945,7 +939,7 @@
}
boolean isDismissing() {
- return (mDismissAnimation != null && mDismissAnimation.isRunning());
+ return mSwipeDismissHandler.isDismissing();
}
boolean isPendingSharedTransition() {
@@ -961,12 +955,6 @@
Log.d(TAG, "reset screenshot view");
}
- if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
- if (DEBUG_ANIM) {
- Log.d(TAG, "cancelling dismiss animation");
- }
- mDismissAnimation.cancel();
- }
mSwipeDismissHandler.cancel();
if (DEBUG_WINDOW) {
Log.d(TAG, "removing OnComputeInternalInsetsListener");
@@ -994,7 +982,7 @@
mShareChip.setIsPending(false);
mEditChip.setIsPending(false);
mPendingInteraction = null;
- for (ScreenshotActionChip chip : mSmartChips) {
+ for (OverlayActionChip chip : mSmartChips) {
mActionsView.removeView(chip);
}
mSmartChips.clear();
@@ -1019,31 +1007,6 @@
}
}
- private AnimatorSet createScreenshotTranslateDismissAnimation() {
- ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
- alphaAnim.setStartDelay(SCREENSHOT_DISMISS_ALPHA_OFFSET_MS);
- alphaAnim.setDuration(SCREENSHOT_DISMISS_ALPHA_DURATION_MS);
- alphaAnim.addUpdateListener(animation -> {
- setAlpha(1 - animation.getAnimatedFraction());
- });
-
- ValueAnimator xAnim = ValueAnimator.ofFloat(0, 1);
- xAnim.setInterpolator(mAccelerateInterpolator);
- xAnim.setDuration(SCREENSHOT_DISMISS_X_DURATION_MS);
- float deltaX = mDirectionLTR
- ? -1 * (mScreenshotPreviewBorder.getX() + mScreenshotPreviewBorder.getWidth())
- : (mDisplayMetrics.widthPixels - mScreenshotPreviewBorder.getX());
- xAnim.addUpdateListener(animation -> {
- float currXDelta = MathUtils.lerp(0, deltaX, animation.getAnimatedFraction());
- mScreenshotStatic.setTranslationX(currXDelta);
- });
-
- AnimatorSet animSet = new AnimatorSet();
- animSet.play(xAnim).with(alphaAnim);
-
- return animSet;
- }
-
ValueAnimator createScreenshotFadeDismissAnimation() {
ValueAnimator alphaAnim = ValueAnimator.ofFloat(0, 1);
alphaAnim.addUpdateListener(animation -> {
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
index 4e96003..451fb13 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/SwipeDismissHandler.java
@@ -16,6 +16,7 @@
package com.android.systemui.screenshot;
+import static com.android.systemui.screenshot.LogConfig.DEBUG_ANIM;
import static com.android.systemui.screenshot.LogConfig.DEBUG_DISMISS;
import android.animation.Animator;
@@ -137,10 +138,20 @@
}
/**
+ * Return whether the view is currently being dismissed
+ */
+ public boolean isDismissing() {
+ return (mDismissAnimation != null && mDismissAnimation.isRunning());
+ }
+
+ /**
* Cancel the currently-running dismissal animation, if any.
*/
public void cancel() {
- if (mDismissAnimation != null && mDismissAnimation.isRunning()) {
+ if (isDismissing()) {
+ if (DEBUG_ANIM) {
+ Log.d(TAG, "cancelling dismiss animation");
+ }
mDismissAnimation.cancel();
}
}
@@ -182,7 +193,13 @@
// make sure the UI gets all the way off the screen in the direction of movement
// (the actions container background is guaranteed to be both the leftmost and
// rightmost UI element in LTR and RTL)
- float finalX = startX <= 0 ? -1 * mView.getRight() : mDisplayMetrics.widthPixels;
+ float finalX;
+ int layoutDir = mView.getContext().getResources().getConfiguration().getLayoutDirection();
+ if (startX > 0 || (startX == 0 && layoutDir == View.LAYOUT_DIRECTION_RTL)) {
+ finalX = mDisplayMetrics.widthPixels;
+ } else {
+ finalX = -1 * mView.getRight();
+ }
float distance = Math.abs(finalX - startX);
anim.addUpdateListener(animation -> {
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/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index 5302188..4a7606c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -22,6 +22,7 @@
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
+import android.app.ActivityManager;
import android.app.Notification;
import android.content.Context;
import android.content.pm.ApplicationInfo;
@@ -33,7 +34,6 @@
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.Icon;
import android.os.Parcelable;
@@ -57,6 +57,7 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.statusbar.notification.NotificationIconDozeHelper;
import com.android.systemui.statusbar.notification.NotificationUtils;
+import com.android.systemui.util.drawable.DrawableSize;
import java.text.NumberFormat;
import java.util.Arrays;
@@ -84,16 +85,6 @@
public static final int STATE_DOT = 1;
public static final int STATE_HIDDEN = 2;
- /**
- * Maximum allowed byte count for an icon bitmap
- * @see android.graphics.RecordingCanvas.MAX_BITMAP_SIZE
- */
- private static final int MAX_BITMAP_SIZE = 100 * 1024 * 1024; // 100 MB
- /**
- * Maximum allowed width or height for an icon drawable, if we can't get byte count
- */
- private static final int MAX_IMAGE_SIZE = 5000;
-
private static final String TAG = "StatusBarIconView";
private static final Property<StatusBarIconView, Float> ICON_APPEAR_AMOUNT
= new FloatProperty<StatusBarIconView>("iconAppearAmount") {
@@ -390,21 +381,6 @@
return false;
}
- if (drawable instanceof BitmapDrawable && ((BitmapDrawable) drawable).getBitmap() != null) {
- // If it's a bitmap we can check the size directly
- int byteCount = ((BitmapDrawable) drawable).getBitmap().getByteCount();
- if (byteCount > MAX_BITMAP_SIZE) {
- Log.w(TAG, "Drawable is too large (" + byteCount + " bytes) " + mIcon);
- return false;
- }
- } else if (drawable.getIntrinsicWidth() > MAX_IMAGE_SIZE
- || drawable.getIntrinsicHeight() > MAX_IMAGE_SIZE) {
- // Otherwise, check dimensions
- Log.w(TAG, "Drawable is too large (" + drawable.getIntrinsicWidth() + "x"
- + drawable.getIntrinsicHeight() + ") " + mIcon);
- return false;
- }
-
if (withClear) {
setImageDrawable(null);
}
@@ -432,7 +408,7 @@
* @return Drawable for this item, or null if the package or item could not
* be found
*/
- public static Drawable getIcon(Context sysuiContext,
+ private Drawable getIcon(Context sysuiContext,
Context context, StatusBarIcon statusBarIcon) {
int userId = statusBarIcon.user.getIdentifier();
if (userId == UserHandle.USER_ALL) {
@@ -446,6 +422,16 @@
typedValue, true);
float scaleFactor = typedValue.getFloat();
+ // We downscale the loaded drawable to reasonable size to protect against applications
+ // using too much memory. The size can be tweaked in config.xml. Drawables
+ // that are already sized properly won't be touched.
+ boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
+ Resources res = sysuiContext.getResources();
+ int maxIconSize = res.getDimensionPixelSize(isLowRamDevice
+ ? com.android.internal.R.dimen.notification_small_icon_size_low_ram
+ : com.android.internal.R.dimen.notification_small_icon_size);
+ icon = DrawableSize.downscaleToSize(res, icon, maxIconSize, maxIconSize);
+
// No need to scale the icon, so return it as is.
if (scaleFactor == 1.f) {
return icon;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
index c0148c0..16bc951e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarState.java
@@ -37,12 +37,6 @@
*/
public static final int SHADE_LOCKED = 2;
- /**
- * Status bar is locked and shows the full screen user switcher.
- */
- public static final int FULLSCREEN_USER_SWITCHER = 3;
-
-
public static String toShortString(int x) {
switch (x) {
case SHADE:
@@ -51,8 +45,6 @@
return "SHD_LCK";
case KEYGUARD:
return "KGRD";
- case FULLSCREEN_USER_SWITCHER:
- return "FS_USRSW";
default:
return "bad_value_" + x;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
index f56602e..ee12cc5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateControllerImpl.java
@@ -76,7 +76,7 @@
// Must be a power of 2
private static final int HISTORY_SIZE = 32;
- private static final int MAX_STATE = StatusBarState.FULLSCREEN_USER_SWITCHER;
+ private static final int MAX_STATE = StatusBarState.SHADE_LOCKED;
private static final int MIN_STATE = StatusBarState.SHADE;
private static final Comparator<RankedListener> sComparator =
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
index 8330169..b66a48e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarStateEvent.java
@@ -34,10 +34,7 @@
STATUS_BAR_STATE_KEYGUARD(430),
@UiEvent(doc = "StatusBarState changed to SHADE_LOCKED state")
- STATUS_BAR_STATE_SHADE_LOCKED(431),
-
- @UiEvent(doc = "StatusBarState changed to FULLSCREEN_USER_SWITCHER state")
- STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER(432);
+ STATUS_BAR_STATE_SHADE_LOCKED(431);
private int mId;
StatusBarStateEvent(int id) {
@@ -60,8 +57,6 @@
return STATUS_BAR_STATE_KEYGUARD;
case StatusBarState.SHADE_LOCKED:
return STATUS_BAR_STATE_SHADE_LOCKED;
- case StatusBarState.FULLSCREEN_USER_SWITCHER:
- return STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER;
default:
return STATUS_BAR_STATE_UNKNOWN;
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
index 8dc01f0..236129f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/DwellRippleShader.kt
@@ -33,7 +33,7 @@
*
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
-class DwellRippleShader internal constructor() : RuntimeShader(SHADER, false) {
+class DwellRippleShader internal constructor() : RuntimeShader(SHADER) {
companion object {
private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
uniform float in_time;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
index 22fbf91..bdad36c 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/charging/RippleShader.kt
@@ -28,7 +28,7 @@
*
* Modeled after frameworks/base/graphics/java/android/graphics/drawable/RippleShader.java.
*/
-class RippleShader internal constructor() : RuntimeShader(SHADER, false) {
+class RippleShader internal constructor() : RuntimeShader(SHADER) {
companion object {
private const val SHADER_UNIFORMS = """uniform vec2 in_origin;
uniform float in_progress;
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/NotifPipelineChoreographer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
new file mode 100644
index 0000000..6f8e5da
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographer.kt
@@ -0,0 +1,140 @@
+/*
+ * 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.statusbar.notification.collection
+
+import android.view.Choreographer
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Main
+import com.android.systemui.util.ListenerSet
+import com.android.systemui.util.concurrency.DelayableExecutor
+import dagger.Module
+import dagger.Provides
+
+/**
+ * Choreographs evaluation resulting from multiple asynchronous sources. Specifically, it exposes
+ * [schedule], and [addOnEvalListener]; the former will "schedule" an asynchronous invocation of the
+ * latter. Multiple invocations of [schedule] before any added listeners are invoked have no effect.
+ */
+interface NotifPipelineChoreographer {
+ /**
+ * Schedules all listeners registered with [addOnEvalListener] to be asynchronously executed at
+ * some point in the future. The exact timing is up to the implementation.
+ */
+ fun schedule()
+
+ /** Cancels a pending evaluation triggered by any recent calls to [schedule]. */
+ fun cancel()
+
+ /** Adds a listener [Runnable] that will be invoked when the scheduled evaluation occurs. */
+ fun addOnEvalListener(onEvalListener: Runnable)
+
+ /** Removes a listener previously registered with [addOnEvalListener]. */
+ fun removeOnEvalListener(onEvalListener: Runnable)
+}
+
+@Module
+object NotifPipelineChoreographerModule {
+ @Provides
+ @JvmStatic
+ @SysUISingleton
+ fun provideChoreographer(
+ choreographer: Choreographer,
+ @Main mainExecutor: DelayableExecutor
+ ): NotifPipelineChoreographer = NotifPipelineChoreographerImpl(choreographer, mainExecutor)
+}
+
+private const val TIMEOUT_MS: Long = 100
+
+private class NotifPipelineChoreographerImpl(
+ private val viewChoreographer: Choreographer,
+ private val executor: DelayableExecutor
+) : NotifPipelineChoreographer {
+
+ private val listeners = ListenerSet<Runnable>()
+ private var timeoutSubscription: Runnable? = null
+ private var isScheduled = false
+
+ private val frameCallback = Choreographer.FrameCallback {
+ if (isScheduled) {
+ isScheduled = false
+ timeoutSubscription?.run()
+ listeners.forEach { it.run() }
+ }
+ }
+
+ override fun schedule() {
+ if (isScheduled) return
+ isScheduled = true
+ viewChoreographer.postFrameCallback(frameCallback)
+ if (!isScheduled) {
+ // Guard against synchronous evaluation of the frame callback.
+ return
+ }
+ timeoutSubscription = executor.executeDelayed(::onTimeout, TIMEOUT_MS)
+ }
+
+ override fun cancel() {
+ if (!isScheduled) return
+ timeoutSubscription?.run()
+ viewChoreographer.removeFrameCallback(frameCallback)
+ }
+
+ override fun addOnEvalListener(onEvalListener: Runnable) {
+ listeners.addIfAbsent(onEvalListener)
+ }
+
+ override fun removeOnEvalListener(onEvalListener: Runnable) {
+ listeners.remove(onEvalListener)
+ }
+
+ private fun onTimeout() {
+ if (isScheduled) {
+ isScheduled = false
+ viewChoreographer.removeFrameCallback(frameCallback)
+ listeners.forEach { it.run() }
+ }
+ }
+}
+
+class FakeNotifPipelineChoreographer : NotifPipelineChoreographer {
+
+ var isScheduled = false
+ val listeners = ListenerSet<Runnable>()
+
+ fun runIfScheduled() {
+ if (isScheduled) {
+ isScheduled = false
+ listeners.forEach { it.run() }
+ }
+ }
+
+ override fun schedule() {
+ isScheduled = true
+ }
+
+ override fun cancel() {
+ isScheduled = false
+ }
+
+ override fun addOnEvalListener(onEvalListener: Runnable) {
+ listeners.addIfAbsent(onEvalListener)
+ }
+
+ override fun removeOnEvalListener(onEvalListener: Runnable) {
+ listeners.remove(onEvalListener)
+ }
+}
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..e0c2fd5 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
@@ -122,20 +122,23 @@
private List<ListEntry> mReadOnlyNotifList = Collections.unmodifiableList(mNotifList);
private List<ListEntry> mReadOnlyNewNotifList = Collections.unmodifiableList(mNewNotifList);
+ private final NotifPipelineChoreographer mChoreographer;
@Inject
public ShadeListBuilder(
- SystemClock systemClock,
- NotifPipelineFlags flags,
- ShadeListBuilderLogger logger,
DumpManager dumpManager,
- NotificationInteractionTracker interactionTracker
+ NotifPipelineChoreographer pipelineChoreographer,
+ NotifPipelineFlags flags,
+ NotificationInteractionTracker interactionTracker,
+ ShadeListBuilderLogger logger,
+ SystemClock systemClock
) {
Assert.isMainThread();
mSystemClock = systemClock;
mLogger = logger;
mAlwaysLogList = flags.isDevLoggingEnabled();
mInteractionTracker = interactionTracker;
+ mChoreographer = pipelineChoreographer;
dumpManager.registerDumpable(TAG, this);
setSectioners(Collections.emptyList());
@@ -148,6 +151,7 @@
public void attach(NotifCollection collection) {
Assert.isMainThread();
collection.setBuildListener(mReadyForBuildListener);
+ mChoreographer.addOnEvalListener(this::buildList);
}
/**
@@ -226,8 +230,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()));
@@ -285,7 +294,7 @@
mLogger.logOnBuildList();
mAllEntries = entries;
- buildList();
+ mChoreographer.schedule();
}
};
@@ -426,14 +435,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 +1009,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 +1100,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 +1125,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 +1149,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;
@@ -1242,7 +1285,7 @@
private void rebuildListIfBefore(@PipelineState.StateName int state) {
mPipelineState.requireIsBefore(state);
if (mPipelineState.is(STATE_IDLE)) {
- buildList();
+ mChoreographer.schedule();
}
}
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/dagger/NotificationsModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
index 45a9092..35d4582 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/dagger/NotificationsModule.java
@@ -47,6 +47,7 @@
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStore;
import com.android.systemui.statusbar.notification.collection.NotifLiveDataStoreImpl;
import com.android.systemui.statusbar.notification.collection.NotifPipeline;
+import com.android.systemui.statusbar.notification.collection.NotifPipelineChoreographerModule;
import com.android.systemui.statusbar.notification.collection.coordinator.ShadeEventCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.VisualStabilityCoordinator;
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorsModule;
@@ -103,6 +104,7 @@
*/
@Module(includes = {
CoordinatorsModule.class,
+ NotifPipelineChoreographerModule.class,
NotifPanelEventSourceModule.class,
NotificationSectionHeadersModule.class,
})
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..c4beb5b 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
@@ -27,8 +27,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.systemui.R;
import com.android.systemui.classifier.FalsingCollector;
+import com.android.systemui.flags.FeatureFlags;
+import com.android.systemui.flags.Flags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -89,6 +90,7 @@
private final OnUserInteractionCallback mOnUserInteractionCallback;
private final FalsingManager mFalsingManager;
private final FalsingCollector mFalsingCollector;
+ private final FeatureFlags mFeatureFlags;
private final boolean mAllowLongPress;
private final PeopleNotificationIdentifier mPeopleNotificationIdentifier;
private final Optional<BubblesManager> mBubblesManagerOptional;
@@ -119,6 +121,7 @@
OnUserInteractionCallback onUserInteractionCallback,
FalsingManager falsingManager,
FalsingCollector falsingCollector,
+ FeatureFlags featureFlags,
PeopleNotificationIdentifier peopleNotificationIdentifier,
Optional<BubblesManager> bubblesManagerOptional,
ExpandableNotificationRowDragController dragController) {
@@ -145,6 +148,7 @@
mOnFeedbackClickListener = mNotificationGutsManager::openGuts;
mAllowLongPress = allowLongPress;
mFalsingCollector = falsingCollector;
+ mFeatureFlags = featureFlags;
mPeopleNotificationIdentifier = peopleNotificationIdentifier;
mBubblesManagerOptional = bubblesManagerOptional;
mDragController = dragController;
@@ -179,7 +183,7 @@
);
mView.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
if (mAllowLongPress) {
- if (mView.getResources().getBoolean(R.bool.config_notificationToContents)) {
+ if (mFeatureFlags.isEnabled(Flags.NOTIFICATION_DRAG_TO_CONTENTS)) {
mView.setDragController(mDragController);
}
@@ -248,19 +252,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/row/FooterView.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
index 0b6d759..7d035a7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/row/FooterView.java
@@ -35,6 +35,10 @@
private FooterViewButton mClearAllButton;
private FooterViewButton mManageButton;
private boolean mShowHistory;
+ // String cache, for performance reasons.
+ // Reading them from a Resources object can be quite slow sometimes.
+ private String mManageNotificationText;
+ private String mManageNotificationHistoryText;
public FooterView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -68,6 +72,8 @@
super.onFinishInflate();
mClearAllButton = (FooterViewButton) findSecondaryView();
mManageButton = findViewById(R.id.manage_text);
+ updateResources();
+ updateText();
}
public void setManageButtonClickListener(OnClickListener listener) {
@@ -86,15 +92,20 @@
}
public void showHistory(boolean showHistory) {
+ if (mShowHistory == showHistory) {
+ return;
+ }
mShowHistory = showHistory;
+ updateText();
+ }
+
+ private void updateText() {
if (mShowHistory) {
- mManageButton.setText(R.string.manage_notifications_history_text);
- mManageButton.setContentDescription(
- mContext.getString(R.string.manage_notifications_history_text));
+ mManageButton.setText(mManageNotificationHistoryText);
+ mManageButton.setContentDescription(mManageNotificationHistoryText);
} else {
- mManageButton.setText(R.string.manage_notifications_text);
- mManageButton.setContentDescription(
- mContext.getString(R.string.manage_notifications_text));
+ mManageButton.setText(mManageNotificationText);
+ mManageButton.setContentDescription(mManageNotificationText);
}
}
@@ -109,7 +120,8 @@
mClearAllButton.setText(R.string.clear_all_notifications_text);
mClearAllButton.setContentDescription(
mContext.getString(R.string.accessibility_clear_all));
- showHistory(mShowHistory);
+ updateResources();
+ updateText();
}
/**
@@ -124,6 +136,12 @@
mManageButton.setTextColor(textColor);
}
+ private void updateResources() {
+ mManageNotificationText = getContext().getString(R.string.manage_notifications_text);
+ mManageNotificationHistoryText = getContext()
+ .getString(R.string.manage_notifications_history_text);
+ }
+
@Override
public ExpandableViewState createExpandableViewState() {
return new FooterViewState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
index ce3e27c..f40a3c7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/AmbientState.java
@@ -76,7 +76,10 @@
private float mHideAmount;
private boolean mAppearing;
private float mPulseHeight = MAX_PULSE_HEIGHT;
+
+ /** How we much we are sleeping. 1f fully dozing (AOD), 0f fully awake (for all other states) */
private float mDozeAmount = 0.0f;
+
private Runnable mOnPulseHeightChangedListener;
private ExpandableNotificationRow mTrackedHeadsUpRow;
private float mAppearFraction;
@@ -96,6 +99,9 @@
/** Height of the notifications panel without top padding when expansion completes. */
private float mStackEndHeight;
+ /** Whether we are swiping up. */
+ private boolean mIsSwipingUp;
+
/**
* @return Height of the notifications panel without top padding when expansion completes.
*/
@@ -133,6 +139,20 @@
}
/**
+ * @param isSwipingUp Whether we are swiping up.
+ */
+ public void setSwipingUp(boolean isSwipingUp) {
+ mIsSwipingUp = isSwipingUp;
+ }
+
+ /**
+ * @return Whether we are swiping up.
+ */
+ public boolean isSwipingUp() {
+ return mIsSwipingUp;
+ }
+
+ /**
* @return Fraction of shade expansion.
*/
public float getExpansionFraction() {
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 dd72615..2c4db77 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;
@@ -204,6 +203,9 @@
private float mQsExpansionFraction;
private final int mSplitShadeMinContentHeight;
+ /** Whether we are flinging the shade open or closed. */
+ private boolean mIsFlinging;
+
/**
* The algorithm which calculates the properties for our children
*/
@@ -696,8 +698,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);
}
@@ -1272,6 +1273,16 @@
}
/**
+ * @return Whether we should skip stack height update for lockscreen swipe-up or unlock hint.
+ */
+ private boolean shouldSkipHeightUpdate() {
+ // After the user swipes up on lockscreen and lets go,
+ // {@link PanelViewController) flings the shade back down.
+ return mAmbientState.isOnKeyguard() && (
+ mAmbientState.isUnlockHintRunning() || mAmbientState.isSwipingUp() || mIsFlinging);
+ }
+
+ /**
* Apply expansion fraction to the y position and height of the notifications panel.
* @param listenerNeedsAnimation does the listener need to animate?
*/
@@ -1285,7 +1296,7 @@
if (mOnStackYChanged != null) {
mOnStackYChanged.accept(listenerNeedsAnimation);
}
- if (mQsExpansionFraction <= 0) {
+ if (mQsExpansionFraction <= 0 && !shouldSkipHeightUpdate()) {
final float endHeight = updateStackEndHeight(
getHeight(), getEmptyBottomMargin(), mTopPadding);
updateStackHeight(endHeight, fraction);
@@ -1327,22 +1338,27 @@
@ShadeViewRefactor(RefactorComponent.COORDINATOR)
public void setExpandedHeight(float height) {
final float shadeBottom = getHeight() - getEmptyBottomMargin();
- final float expansionFraction = MathUtils.saturate(height / shadeBottom);
- mAmbientState.setExpansionFraction(expansionFraction);
+ final boolean skipHeightUpdate = shouldSkipHeightUpdate();
+ if (!skipHeightUpdate) {
+ final float expansionFraction = MathUtils.saturate(height / shadeBottom);
+ mAmbientState.setExpansionFraction(expansionFraction);
+ }
updateStackPosition();
- mExpandedHeight = height;
- setIsExpanded(height > 0);
- int minExpansionHeight = getMinExpansionHeight();
- if (height < minExpansionHeight) {
- mClipRect.left = 0;
- mClipRect.right = getWidth();
- mClipRect.top = 0;
- mClipRect.bottom = (int) height;
- height = minExpansionHeight;
- setRequestedClipBounds(mClipRect);
- } else {
- setRequestedClipBounds(null);
+ if (!skipHeightUpdate) {
+ mExpandedHeight = height;
+ setIsExpanded(height > 0);
+ int minExpansionHeight = getMinExpansionHeight();
+ if (height < minExpansionHeight) {
+ mClipRect.left = 0;
+ mClipRect.right = getWidth();
+ mClipRect.top = 0;
+ mClipRect.bottom = (int) height;
+ height = minExpansionHeight;
+ setRequestedClipBounds(mClipRect);
+ } else {
+ setRequestedClipBounds(null);
+ }
}
int stackHeight;
float translationY;
@@ -1370,7 +1386,7 @@
}
}
} else {
- stackHeight = (int) height;
+ stackHeight = (int) (skipHeightUpdate ? mExpandedHeight : height);
}
} else {
appearFraction = calculateAppearFraction(height);
@@ -1388,7 +1404,7 @@
}
}
mAmbientState.setAppearFraction(appearFraction);
- if (stackHeight != mCurrentStackHeight) {
+ if (stackHeight != mCurrentStackHeight && !skipHeightUpdate) {
mCurrentStackHeight = stackHeight;
updateAlgorithmHeightAndPadding();
requestChildrenUpdate();
@@ -5003,6 +5019,13 @@
mAmbientState.setUnlockHintRunning(running);
}
+ /**
+ * @param isFlinging Whether we are flinging the shade open or closed.
+ */
+ public void setIsFlinging(boolean isFlinging) {
+ mIsFlinging = isFlinging;
+ }
+
@ShadeViewRefactor(RefactorComponent.SHADE_VIEW)
public void setHeadsUpGoingAwayAnimationsAllowed(boolean headsUpGoingAwayAnimationsAllowed) {
mHeadsUpGoingAwayAnimationsAllowed = headsUpGoingAwayAnimationsAllowed;
@@ -5243,11 +5266,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..e1116f8 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();
}
@@ -1178,6 +1195,13 @@
mView.setUnlockHintRunning(running);
}
+ /**
+ * @param isFlinging Whether we are flinging the shade open or close.
+ */
+ public void setIsFlinging(boolean isFlinging) {
+ mView.setIsFlinging(isFlinging);
+ }
+
public boolean isFooterViewNotGone() {
return mView.isFooterViewNotGone();
}
@@ -1199,6 +1223,7 @@
* are true.
*/
public void updateShowEmptyShadeView() {
+ Trace.beginSection("NSSLC.updateShowEmptyShadeView");
mShowEmptyShadeView = mBarState != KEYGUARD
&& (!mView.isQsExpanded() || mView.isUsingSplitNotificationShade())
&& getVisibleNotificationCount() == 0;
@@ -1206,6 +1231,7 @@
mView.updateEmptyShadeView(
mShowEmptyShadeView,
mZenModeController.areNotificationsHiddenInShade());
+ Trace.endSection();
}
public boolean areNotificationsHiddenInShade() {
@@ -1323,11 +1349,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/notification/stack/NotificationSwipeHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
index 1038e76..2d2fbe5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelper.java
@@ -33,6 +33,7 @@
import com.android.internal.jank.InteractionJankMonitor;
import com.android.systemui.SwipeHelper;
import com.android.systemui.dagger.qualifiers.Main;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper;
@@ -66,9 +67,10 @@
NotificationSwipeHelper(
Resources resources, ViewConfiguration viewConfiguration,
- FalsingManager falsingManager, int swipeDirection, NotificationCallback callback,
+ FalsingManager falsingManager, FeatureFlags featureFlags,
+ int swipeDirection, NotificationCallback callback,
NotificationMenuRowPlugin.OnMenuEventListener menuListener) {
- super(swipeDirection, callback, resources, viewConfiguration, falsingManager);
+ super(swipeDirection, callback, resources, viewConfiguration, falsingManager, featureFlags);
mMenuListener = menuListener;
mCallback = callback;
mFalsingCheck = () -> resetExposedMenuView(true /* animate */, true /* force */);
@@ -508,16 +510,18 @@
private final Resources mResources;
private final ViewConfiguration mViewConfiguration;
private final FalsingManager mFalsingManager;
+ private final FeatureFlags mFeatureFlags;
private int mSwipeDirection;
private NotificationCallback mNotificationCallback;
private NotificationMenuRowPlugin.OnMenuEventListener mOnMenuEventListener;
@Inject
Builder(@Main Resources resources, ViewConfiguration viewConfiguration,
- FalsingManager falsingManager) {
+ FalsingManager falsingManager, FeatureFlags featureFlags) {
mResources = resources;
mViewConfiguration = viewConfiguration;
mFalsingManager = falsingManager;
+ mFeatureFlags = featureFlags;
}
Builder setSwipeDirection(int swipeDirection) {
@@ -538,7 +542,7 @@
NotificationSwipeHelper build() {
return new NotificationSwipeHelper(mResources, mViewConfiguration, mFalsingManager,
- mSwipeDirection, mNotificationCallback, mOnMenuEventListener);
+ mFeatureFlags, mSwipeDirection, mNotificationCallback, mOnMenuEventListener);
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index 04d3e9a..6a78370 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -180,6 +180,7 @@
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
private final StatusBarStateController mStatusBarStateController;
+ private final LatencyTracker mLatencyTracker;
private long mLastFpFailureUptimeMillis;
private int mNumConsecutiveFpFailures;
@@ -281,7 +282,8 @@
AuthController authController,
StatusBarStateController statusBarStateController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
- SessionTracker sessionTracker) {
+ SessionTracker sessionTracker,
+ LatencyTracker latencyTracker) {
mContext = context;
mPowerManager = powerManager;
mShadeController = shadeController;
@@ -289,6 +291,7 @@
mDozeParameters = dozeParameters;
mUpdateMonitor.registerCallback(this);
mMediaManager = notificationMediaManager;
+ mLatencyTracker = latencyTracker;
wakefulnessLifecycle.addObserver(mWakefulnessObserver);
screenLifecycle.addObserver(mScreenObserver);
@@ -343,13 +346,13 @@
public void onBiometricAcquired(BiometricSourceType biometricSourceType) {
Trace.beginSection("BiometricUnlockController#onBiometricAcquired");
releaseBiometricWakeLock();
- if (!mUpdateMonitor.isDeviceInteractive()) {
- if (LatencyTracker.isEnabled(mContext)) {
+ if (isWakeAndUnlock()) {
+ if (mLatencyTracker.isEnabled()) {
int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
if (biometricSourceType == BiometricSourceType.FACE) {
action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
}
- LatencyTracker.getInstance(mContext).onActionStart(action);
+ mLatencyTracker.onActionStart(action);
}
mWakeLock = mPowerManager.newWakeLock(
PowerManager.PARTIAL_WAKE_LOCK, BIOMETRIC_WAKE_LOCK_NAME);
@@ -652,6 +655,14 @@
Optional.ofNullable(BiometricUiEvent.FAILURE_EVENT_BY_SOURCE_TYPE.get(biometricSourceType))
.ifPresent(event -> UI_EVENT_LOGGER.log(event, getSessionId()));
+ if (mLatencyTracker.isEnabled()) {
+ int action = LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK;
+ if (biometricSourceType == BiometricSourceType.FACE) {
+ action = LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK;
+ }
+ mLatencyTracker.onActionCancel(action);
+ }
+
if (biometricSourceType == BiometricSourceType.FINGERPRINT
&& mUpdateMonitor.isUdfpsSupported()) {
long currUptimeMillis = SystemClock.uptimeMillis();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
index 2ec5f25..b8e9875 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -40,6 +40,8 @@
import android.widget.RelativeLayout;
import android.widget.TextView;
+import androidx.annotation.VisibleForTesting;
+
import com.android.settingslib.Utils;
import com.android.systemui.R;
import com.android.systemui.animation.Interpolators;
@@ -67,8 +69,10 @@
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
+ private ViewGroup mUserSwitcherContainer;
private boolean mKeyguardUserSwitcherEnabled;
+ private boolean mKeyguardUserAvatarEnabled;
private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
@@ -111,10 +115,15 @@
mCutoutSpace = findViewById(R.id.cutout_space_view);
mStatusIconArea = findViewById(R.id.status_icon_area);
mStatusIconContainer = findViewById(R.id.statusIcons);
+ mUserSwitcherContainer = findViewById(R.id.user_switcher_container);
mIsPrivacyDotEnabled = mContext.getResources().getBoolean(R.bool.config_enablePrivacyDot);
loadDimens();
}
+ public ViewGroup getUserSwitcherContainer() {
+ return mUserSwitcherContainer;
+ }
+
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@@ -186,6 +195,17 @@
}
private void updateVisibilities() {
+ // Multi user avatar is disabled in favor of the user switcher chip
+ if (!mKeyguardUserAvatarEnabled) {
+ if (mMultiUserAvatar.getParent() == mStatusIconArea) {
+ mStatusIconArea.removeView(mMultiUserAvatar);
+ } else if (mMultiUserAvatar.getParent() != null) {
+ getOverlay().remove(mMultiUserAvatar);
+ }
+
+ return;
+ }
+
if (mMultiUserAvatar.getParent() != mStatusIconArea
&& !mKeyguardUserSwitcherEnabled) {
if (mMultiUserAvatar.getParent() != null) {
@@ -346,6 +366,16 @@
mKeyguardUserSwitcherEnabled = enabled;
}
+ void setKeyguardUserAvatarEnabled(boolean enabled) {
+ mKeyguardUserAvatarEnabled = enabled;
+ updateVisibilities();
+ }
+
+ @VisibleForTesting
+ boolean isKeyguardUserAvatarEnabled() {
+ return mKeyguardUserAvatarEnabled;
+ }
+
private void animateNextLayoutChange() {
final int systemIconsCurrentX = mSystemIconsContainer.getLeft();
final boolean userAvatarVisible = mMultiUserAvatar.getParent() == mStatusIconArea;
@@ -416,9 +446,14 @@
/** Should only be called from {@link KeyguardStatusBarViewController}. */
void onOverlayChanged() {
- mCarrierLabel.setTextAppearance(
- Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall));
+ int theme = Utils.getThemeAttr(mContext, com.android.internal.R.attr.textAppearanceSmall);
+ mCarrierLabel.setTextAppearance(theme);
mBatteryView.updatePercentView();
+
+ TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
+ if (userSwitcherName != null) {
+ userSwitcherName.setTextAppearance(theme);
+ }
}
private void updateIconsAndTextColors(StatusBarIconController.TintedIconManager iconManager) {
@@ -429,6 +464,14 @@
R.color.light_mode_icon_color_single_tone);
float intensity = textColor == Color.WHITE ? 0 : 1;
mCarrierLabel.setTextColor(iconColor);
+
+ TextView userSwitcherName = mUserSwitcherContainer.findViewById(R.id.current_user_name);
+ if (userSwitcherName != null) {
+ userSwitcherName.setTextColor(Utils.getColorStateListDefaultColor(
+ mContext,
+ R.color.light_mode_icon_color_single_tone));
+ }
+
if (iconManager != null) {
iconManager.setTint(iconColor);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
index ee97fd6..1df1aff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -47,6 +47,9 @@
import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
@@ -95,6 +98,9 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private final StatusBarContentInsetsProvider mInsetsProvider;
private final UserManager mUserManager;
+ private final StatusBarUserSwitcherFeatureController mFeatureController;
+ private final StatusBarUserSwitcherController mUserSwitcherController;
+ private final StatusBarUserInfoTracker mStatusBarUserInfoTracker;
private final ConfigurationController.ConfigurationListener mConfigurationListener =
new ConfigurationController.ConfigurationListener() {
@@ -246,7 +252,10 @@
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
StatusBarContentInsetsProvider statusBarContentInsetsProvider,
- UserManager userManager
+ UserManager userManager,
+ StatusBarUserSwitcherFeatureController featureController,
+ StatusBarUserSwitcherController userSwitcherController,
+ StatusBarUserInfoTracker statusBarUserInfoTracker
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -265,6 +274,9 @@
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
mUserManager = userManager;
+ mFeatureController = featureController;
+ mUserSwitcherController = userSwitcherController;
+ mStatusBarUserInfoTracker = statusBarUserInfoTracker;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -286,6 +298,10 @@
r.getString(com.android.internal.R.string.status_bar_call_strength)));
mNotificationsHeaderCollideDistance = r.getDimensionPixelSize(
R.dimen.header_notifications_collide_distance);
+
+ mView.setKeyguardUserAvatarEnabled(
+ !mFeatureController.isStatusBarUserSwitcherFeatureEnabled());
+ mFeatureController.addCallback(enabled -> mView.setKeyguardUserAvatarEnabled(!enabled));
}
@Override
@@ -293,6 +309,7 @@
super.onInit();
mCarrierTextController.init();
mBatteryMeterViewController.init();
+ mUserSwitcherController.init();
}
@Override
@@ -334,6 +351,9 @@
/** Sets whether user switcher is enabled. */
public void setKeyguardUserSwitcherEnabled(boolean enabled) {
mView.setKeyguardUserSwitcherEnabled(enabled);
+ // We don't have a listener for when the user switcher setting changes, so this is
+ // where we re-check the state
+ mStatusBarUserInfoTracker.checkEnabled();
}
/** Sets whether this controller should listen to battery updates. */
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
index 3d3a1da..278b4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -806,7 +806,6 @@
QsFrameTranslateController qsFrameTranslateController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
super(view,
- featureFlags,
falsingManager,
dozeLog,
keyguardStateController,
@@ -1885,9 +1884,16 @@
mHeadsUpTouchHelper.notifyFling(!expand);
mKeyguardStateController.notifyPanelFlingStart(!expand /* flingingToDismiss */);
setClosingWithAlphaFadeout(!expand && !isOnKeyguard() && getFadeoutAlpha() == 1.0f);
+ mNotificationStackScrollLayoutController.setIsFlinging(true);
super.flingToHeight(vel, expand, target, collapseSpeedUpFactor, expandBecauseOfFalsing);
}
+ @Override
+ protected void onFlingEnd(boolean cancelled) {
+ super.onFlingEnd(cancelled);
+ mNotificationStackScrollLayoutController.setIsFlinging(false);
+ }
+
private boolean onQsIntercept(MotionEvent event) {
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
index 474653b..9f9e7d9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationShadeWindowControllerImpl.java
@@ -310,10 +310,8 @@
if (onKeyguard
&& mAuthController.isUdfpsEnrolled(KeyguardUpdateMonitor.getCurrentUser())) {
mLpChanged.preferredMaxDisplayRefreshRate = mKeyguardPreferredRefreshRate;
- mLpChanged.preferredMinDisplayRefreshRate = mKeyguardPreferredRefreshRate;
} else {
mLpChanged.preferredMaxDisplayRefreshRate = 0;
- mLpChanged.preferredMinDisplayRefreshRate = 0;
}
Trace.setCounter("display_set_preferred_refresh_rate",
(long) mKeyguardPreferredRefreshRate);
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/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
index c466a8c..85e8042 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java
@@ -53,8 +53,6 @@
import com.android.systemui.animation.Interpolators;
import com.android.systemui.classifier.Classifier;
import com.android.systemui.doze.DozeLog;
-import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.keyguard.KeyguardUnlockAnimationController;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
@@ -223,7 +221,6 @@
public PanelViewController(
PanelView view,
- FeatureFlags featureFlags,
FalsingManager falsingManager,
DozeLog dozeLog,
KeyguardStateController keyguardStateController,
@@ -292,7 +289,8 @@
mBounceInterpolator = new BounceInterpolator();
mFalsingManager = falsingManager;
mDozeLog = dozeLog;
- mNotificationsDragEnabled = featureFlags.isEnabled(Flags.NOTIFICATION_SHADE_DRAG);
+ mNotificationsDragEnabled = mResources.getBoolean(
+ R.bool.config_enableNotificationShadeDrag);
mVibratorHelper = vibratorHelper;
mVibrateOnOpening = mResources.getBoolean(R.bool.config_vibrateOnIconAnimation);
mStatusBarTouchableRegionManager = statusBarTouchableRegionManager;
@@ -461,7 +459,7 @@
boolean expands = onEmptySpaceClick(mInitialTouchX);
onTrackingStopped(expands);
}
-
+ mAmbientState.setSwipingUp(false);
mVelocityTracker.clear();
}
@@ -708,7 +706,7 @@
animator.start();
}
- private void onFlingEnd(boolean cancelled) {
+ void onFlingEnd(boolean cancelled) {
mIsFlinging = false;
// No overshoot when the animation ends
setOverExpansionInternal(0, false /* isFromGesture */);
@@ -1393,6 +1391,10 @@
mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
}
if ((!mGestureWaitForTouchSlop || mTracking) && !isTrackingBlocked()) {
+ // Count h==0 as part of swipe-up,
+ // otherwise {@link NotificationStackScrollLayout}
+ // wrongly enables stack height updates at the start of lockscreen swipe-up
+ mAmbientState.setSwipingUp(h <= 0);
setExpandedHeightInternal(newHeight);
}
break;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
index 1cb19ab..224b2e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewController.kt
@@ -21,15 +21,19 @@
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
+
import com.android.systemui.R
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.UNFOLD_STATUS_BAR
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import com.android.systemui.util.ViewController
import com.android.systemui.util.kotlin.getOrNull
+
import java.util.Optional
+
import javax.inject.Inject
import javax.inject.Named
@@ -38,6 +42,7 @@
view: PhoneStatusBarView,
@Named(UNFOLD_STATUS_BAR) private val progressProvider: ScopedUnfoldTransitionProgressProvider?,
private val moveFromCenterAnimationController: StatusBarMoveFromCenterAnimationController?,
+ private val userSwitcherController: StatusBarUserSwitcherController,
touchEventHandler: PhoneStatusBarView.TouchEventHandler,
private val configurationController: ConfigurationController
) : ViewController<PhoneStatusBarView>(view) {
@@ -49,29 +54,29 @@
}
override fun onViewAttached() {
- moveFromCenterAnimationController?.let { animationController ->
- val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
- val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
+ if (moveFromCenterAnimationController == null) return
- val viewsToAnimate = arrayOf(
- statusBarLeftSide,
- systemIconArea
- )
+ val statusBarLeftSide: View = mView.findViewById(R.id.status_bar_left_side)
+ val systemIconArea: ViewGroup = mView.findViewById(R.id.system_icon_area)
- mView.viewTreeObserver.addOnPreDrawListener(object :
- ViewTreeObserver.OnPreDrawListener {
- override fun onPreDraw(): Boolean {
- animationController.onViewsReady(viewsToAnimate)
- mView.viewTreeObserver.removeOnPreDrawListener(this)
- return true
- }
- })
+ val viewsToAnimate = arrayOf(
+ statusBarLeftSide,
+ systemIconArea
+ )
- mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
- val widthChanged = right - left != oldRight - oldLeft
- if (widthChanged) {
- moveFromCenterAnimationController.onStatusBarWidthChanged()
- }
+ mView.viewTreeObserver.addOnPreDrawListener(object :
+ ViewTreeObserver.OnPreDrawListener {
+ override fun onPreDraw(): Boolean {
+ moveFromCenterAnimationController.onViewsReady(viewsToAnimate)
+ mView.viewTreeObserver.removeOnPreDrawListener(this)
+ return true
+ }
+ })
+
+ mView.addOnLayoutChangeListener { _, left, _, right, _, oldLeft, _, oldRight, _ ->
+ val widthChanged = right - left != oldRight - oldLeft
+ if (widthChanged) {
+ moveFromCenterAnimationController.onStatusBarWidthChanged()
}
}
@@ -89,6 +94,10 @@
mView.setTouchEventHandler(touchEventHandler)
}
+ override fun onInit() {
+ userSwitcherController.init()
+ }
+
fun setImportantForAccessibility(mode: Int) {
mView.importantForAccessibility = mode
}
@@ -153,6 +162,7 @@
private val unfoldComponent: Optional<SysUIUnfoldComponent>,
@Named(UNFOLD_STATUS_BAR)
private val progressProvider: Optional<ScopedUnfoldTransitionProgressProvider>,
+ private val userSwitcherController: StatusBarUserSwitcherController,
private val configurationController: ConfigurationController
) {
fun create(
@@ -162,9 +172,8 @@
PhoneStatusBarViewController(
view,
progressProvider.getOrNull(),
- unfoldComponent.map {
- it.getStatusBarMoveFromCenterAnimationController()
- }.getOrNull(),
+ unfoldComponent.getOrNull()?.getStatusBarMoveFromCenterAnimationController(),
+ userSwitcherController,
touchEventHandler,
configurationController
)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index 2f3300a..c8cc807 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2935,14 +2935,6 @@
return updateIsKeyguard();
}
- /**
- * stop(tag)
- * @return True if StatusBar state is FULLSCREEN_USER_SWITCHER.
- */
- public boolean isFullScreenUserSwitcherState() {
- return mState == StatusBarState.FULLSCREEN_USER_SWITCHER;
- }
-
boolean updateIsKeyguard() {
return updateIsKeyguard(false /* forceStateChange */);
}
@@ -2996,9 +2988,7 @@
onLaunchTransitionFadingEnded();
}
mMessageRouter.cancelMessages(MSG_LAUNCH_TRANSITION_TIMEOUT);
- if (mUserSwitcherController != null && mUserSwitcherController.useFullscreenUserSwitcher()) {
- mStatusBarStateController.setState(StatusBarState.FULLSCREEN_USER_SWITCHER);
- } else if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
+ if (!mLockscreenShadeTransitionController.isWakingToShadeLocked()) {
mStatusBarStateController.setState(StatusBarState.KEYGUARD);
}
updatePanelExpansionForKeyguard();
@@ -3009,8 +2999,6 @@
if (mState == StatusBarState.KEYGUARD && mBiometricUnlockController.getMode()
!= BiometricUnlockController.MODE_WAKE_AND_UNLOCK && !mBouncerShowing) {
mShadeController.instantExpandNotificationsPanel();
- } else if (mState == StatusBarState.FULLSCREEN_USER_SWITCHER) {
- instantCollapseNotificationPanel();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index b833c89..d42a423 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -49,7 +49,6 @@
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
-import com.android.systemui.DejankUtils;
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dock.DockManager;
import com.android.systemui.dreams.DreamOverlayStateController;
@@ -214,6 +213,7 @@
private final SysuiStatusBarStateController mStatusBarStateController;
private final DockManager mDockManager;
private final KeyguardUpdateMonitor mKeyguardUpdateManager;
+ private final LatencyTracker mLatencyTracker;
private KeyguardBypassController mBypassController;
@Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor;
@@ -246,7 +246,8 @@
NotificationMediaManager notificationMediaManager,
KeyguardBouncer.Factory keyguardBouncerFactory,
KeyguardMessageAreaController.Factory keyguardMessageAreaFactory,
- Lazy<ShadeController> shadeController) {
+ Lazy<ShadeController> shadeController,
+ LatencyTracker latencyTracker) {
mContext = context;
mViewMediatorCallback = callback;
mLockPatternUtils = lockPatternUtils;
@@ -262,6 +263,7 @@
mKeyguardBouncerFactory = keyguardBouncerFactory;
mKeyguardMessageAreaFactory = keyguardMessageAreaFactory;
mShadeController = shadeController;
+ mLatencyTracker = latencyTracker;
}
@Override
@@ -859,15 +861,11 @@
}
private void wakeAndUnlockDejank() {
- if (mBiometricUnlockController.getMode() == MODE_WAKE_AND_UNLOCK
- && LatencyTracker.isEnabled(mContext)) {
+ if (mBiometricUnlockController.isWakeAndUnlock() && mLatencyTracker.isEnabled()) {
BiometricSourceType type = mBiometricUnlockController.getBiometricType();
- DejankUtils.postAfterTraversal(() -> {
- LatencyTracker.getInstance(mContext).onActionEnd(
- type == BiometricSourceType.FACE
- ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK
- : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
- });
+ mLatencyTracker.onActionEnd(type == BiometricSourceType.FACE
+ ? LatencyTracker.ACTION_FACE_WAKE_AND_UNLOCK
+ : LatencyTracker.ACTION_FINGERPRINT_WAKE_AND_UNLOCK);
}
}
@@ -1186,7 +1184,6 @@
// When a dream overlay is active, scrimming will cause any expansion to immediately expand.
return (mOccluded && !mDreamOverlayStateController.isOverlayActive())
|| mBouncer.willDismissWithAction()
- || mStatusBar.isFullScreenUserSwitcherState()
|| (mBouncer.isShowing() && mBouncer.isScrimmed())
|| mBouncer.isFullscreenBouncer();
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
index 6d033477..79c0984 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarMoveFromCenterAnimationController.kt
@@ -18,11 +18,13 @@
import android.view.View
import android.view.WindowManager
import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator
+import com.android.systemui.shared.animation.UnfoldMoveFromCenterAnimator.AlphaProvider
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController.StatusBarViewsCenterProvider
import com.android.systemui.unfold.SysUIUnfoldScope
import com.android.systemui.unfold.UnfoldTransitionProgressProvider.TransitionProgressListener
import com.android.systemui.unfold.util.ScopedUnfoldTransitionProgressProvider
import javax.inject.Inject
+import kotlin.math.max
@SysUIUnfoldScope
class StatusBarMoveFromCenterAnimationController @Inject constructor(
@@ -31,8 +33,11 @@
) {
private val transitionListener = TransitionListener()
- private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(windowManager,
- viewCenterProvider = StatusBarViewsCenterProvider())
+ private val moveFromCenterAnimator = UnfoldMoveFromCenterAnimator(
+ windowManager,
+ viewCenterProvider = StatusBarViewsCenterProvider(),
+ alphaProvider = StatusBarIconsAlphaProvider()
+ )
fun onViewsReady(viewsToAnimate: Array<View>) {
moveFromCenterAnimator.updateDisplayProperties()
@@ -65,4 +70,15 @@
moveFromCenterAnimator.onTransitionProgress(1f)
}
}
+
+ private class StatusBarIconsAlphaProvider : AlphaProvider {
+ override fun getAlpha(progress: Float): Float {
+ return max(
+ 0f,
+ (progress - ICONS_START_APPEARING_PROGRESS) / (1 - ICONS_START_APPEARING_PROGRESS)
+ )
+ }
+ }
}
+
+private const val ICONS_START_APPEARING_PROGRESS = 0.75F
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
index 3292934..d464acb 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusIconContainer.java
@@ -217,8 +217,10 @@
* frameworks/base/core/res/res/values/config.xml
*/
public void addIgnoredSlot(String slotName) {
- addIgnoredSlotInternal(slotName);
- requestLayout();
+ boolean added = addIgnoredSlotInternal(slotName);
+ if (added) {
+ requestLayout();
+ }
}
/**
@@ -226,17 +228,27 @@
* @param slots names of the icons to ignore
*/
public void addIgnoredSlots(List<String> slots) {
+ boolean willAddAny = false;
for (String slot : slots) {
- addIgnoredSlotInternal(slot);
+ willAddAny |= addIgnoredSlotInternal(slot);
}
- requestLayout();
+ if (willAddAny) {
+ requestLayout();
+ }
}
- private void addIgnoredSlotInternal(String slotName) {
- if (!mIgnoredSlots.contains(slotName)) {
- mIgnoredSlots.add(slotName);
+ /**
+ *
+ * @param slotName
+ * @return
+ */
+ private boolean addIgnoredSlotInternal(String slotName) {
+ if (mIgnoredSlots.contains(slotName)) {
+ return false;
}
+ mIgnoredSlots.add(slotName);
+ return true;
}
/**
@@ -245,9 +257,10 @@
* @param slotName name of the icon slot to remove from the ignored list
*/
public void removeIgnoredSlot(String slotName) {
- mIgnoredSlots.remove(slotName);
-
- requestLayout();
+ boolean removed = mIgnoredSlots.remove(slotName);
+ if (removed) {
+ requestLayout();
+ }
}
/**
@@ -256,11 +269,14 @@
* @param slots name of the icon slots to remove from the ignored list
*/
public void removeIgnoredSlots(List<String> slots) {
+ boolean removedAny = false;
for (String slot : slots) {
- mIgnoredSlots.remove(slot);
+ removedAny |= mIgnoredSlots.remove(slot);
}
- requestLayout();
+ if (removedAny) {
+ requestLayout();
+ }
}
/**
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
index dea1b43..e2dc905 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/fragment/dagger/StatusBarFragmentModule.java
@@ -26,11 +26,15 @@
import com.android.systemui.statusbar.phone.PhoneStatusBarView;
import com.android.systemui.statusbar.phone.PhoneStatusBarViewController;
import com.android.systemui.statusbar.phone.fragment.CollapsedStatusBarFragment;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherContainer;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherControllerImpl;
import com.android.systemui.statusbar.policy.Clock;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import javax.inject.Named;
+import dagger.Binds;
import dagger.Module;
import dagger.Provides;
@@ -83,6 +87,20 @@
/** */
@Provides
@StatusBarFragmentScope
+ static StatusBarUserSwitcherContainer provideStatusBarUserSwitcherContainer(
+ @RootView PhoneStatusBarView view) {
+ return view.findViewById(R.id.user_switcher_container);
+ }
+
+ /** */
+ @Binds
+ @StatusBarFragmentScope
+ StatusBarUserSwitcherController bindStatusBarUserSwitcherController(
+ StatusBarUserSwitcherControllerImpl controller);
+
+ /** */
+ @Provides
+ @StatusBarFragmentScope
static PhoneStatusBarViewController providePhoneStatusBarViewController(
PhoneStatusBarViewController.Factory phoneStatusBarViewControllerFactory,
@RootView PhoneStatusBarView phoneStatusBarView,
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
new file mode 100644
index 0000000..2dbc19c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserInfoTracker.kt
@@ -0,0 +1,122 @@
+/*
+ * 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.statusbar.phone.userswitcher
+
+import android.graphics.drawable.Drawable
+import android.os.UserManager
+
+import com.android.systemui.DejankUtils.whitelistIpcs
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.statusbar.policy.CallbackController
+import com.android.systemui.statusbar.policy.UserInfoController
+import com.android.systemui.statusbar.policy.UserInfoController.OnUserInfoChangedListener
+
+import javax.inject.Inject
+
+/**
+ * Since every user switcher chip will user the exact same information and logic on whether or not
+ * to show, and what data to show, it makes sense to create a single tracker here
+ */
+@SysUISingleton
+class StatusBarUserInfoTracker @Inject constructor(
+ private val userInfoController: UserInfoController,
+ private val userManager: UserManager
+) : CallbackController<CurrentUserChipInfoUpdatedListener> {
+ var currentUserName: String? = null
+ private set
+ var currentUserAvatar: Drawable? = null
+ private set
+ var userSwitcherEnabled = false
+ private set
+ private var listening = false
+
+ private val listeners = mutableListOf<CurrentUserChipInfoUpdatedListener>()
+
+ private val userInfoChangedListener = OnUserInfoChangedListener { name, picture, _ ->
+ currentUserAvatar = picture
+ currentUserName = name
+ notifyListenersUserInfoChanged()
+ }
+
+ init {
+ startListening()
+ }
+
+ override fun addCallback(listener: CurrentUserChipInfoUpdatedListener) {
+ if (listeners.isEmpty()) {
+ startListening()
+ }
+
+ if (!listeners.contains(listener)) {
+ listeners.add(listener)
+ }
+ }
+
+ override fun removeCallback(listener: CurrentUserChipInfoUpdatedListener) {
+ listeners.remove(listener)
+
+ if (listeners.isEmpty()) {
+ stopListening()
+ }
+ }
+
+ private fun notifyListenersUserInfoChanged() {
+ listeners.forEach {
+ it.onCurrentUserChipInfoUpdated()
+ }
+ }
+
+ private fun notifyListenersSettingChanged() {
+ listeners.forEach {
+ it.onStatusBarUserSwitcherSettingChanged(userSwitcherEnabled)
+ }
+ }
+
+ private fun startListening() {
+ listening = true
+ userInfoController.addCallback(userInfoChangedListener)
+ }
+
+ private fun stopListening() {
+ listening = false
+ userInfoController.removeCallback(userInfoChangedListener)
+ }
+
+ private fun checkUserSwitcherEnabled() {
+ whitelistIpcs {
+ userSwitcherEnabled = userManager.isUserSwitcherEnabled
+ }
+ }
+
+ /**
+ * Force a check to [UserManager.isUserSwitcherEnabled], and update listeners if the value has
+ * changed
+ */
+ fun checkEnabled() {
+ val wasEnabled = userSwitcherEnabled
+ checkUserSwitcherEnabled()
+
+ if (wasEnabled != userSwitcherEnabled) {
+ notifyListenersSettingChanged()
+ }
+ }
+}
+
+interface CurrentUserChipInfoUpdatedListener {
+ fun onCurrentUserChipInfoUpdated()
+ fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {}
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
new file mode 100644
index 0000000..2c8677d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherContainer.kt
@@ -0,0 +1,40 @@
+/*
+ * 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.statusbar.phone.userswitcher
+
+import android.content.Context
+import android.util.AttributeSet
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import com.android.systemui.R
+
+class StatusBarUserSwitcherContainer(
+ context: Context?,
+ attrs: AttributeSet?
+) : LinearLayout(context, attrs) {
+ lateinit var text: TextView
+ private set
+ lateinit var avatar: ImageView
+ private set
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ text = findViewById(R.id.current_user_name)
+ avatar = findViewById(R.id.current_user_avatar)
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
new file mode 100644
index 0000000..a124753
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherController.kt
@@ -0,0 +1,88 @@
+/*
+ * 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.statusbar.phone.userswitcher
+
+import android.view.View
+
+import com.android.systemui.qs.user.UserSwitchDialogController
+import com.android.systemui.util.ViewController
+
+import javax.inject.Inject
+
+/**
+ * ViewController for [StatusBarUserSwitcherContainer]
+ */
+class StatusBarUserSwitcherControllerImpl @Inject constructor(
+ view: StatusBarUserSwitcherContainer,
+ private val tracker: StatusBarUserInfoTracker,
+ private val featureController: StatusBarUserSwitcherFeatureController,
+ private val userSwitcherDialogController: UserSwitchDialogController
+) : ViewController<StatusBarUserSwitcherContainer>(view),
+ StatusBarUserSwitcherController {
+ private val listener = object : CurrentUserChipInfoUpdatedListener {
+ override fun onCurrentUserChipInfoUpdated() {
+ updateChip()
+ }
+
+ override fun onStatusBarUserSwitcherSettingChanged(enabled: Boolean) {
+ updateEnabled()
+ }
+ }
+
+ private val featureFlagListener = object : OnUserSwitcherPreferenceChangeListener {
+ override fun onUserSwitcherPreferenceChange(enabled: Boolean) {
+ updateEnabled()
+ }
+ }
+
+ override fun onViewAttached() {
+ tracker.addCallback(listener)
+ featureController.addCallback(featureFlagListener)
+ mView.setOnClickListener {
+ userSwitcherDialogController.showDialog(it)
+ }
+
+ updateEnabled()
+ }
+
+ override fun onViewDetached() {
+ tracker.removeCallback(listener)
+ featureController.removeCallback(featureFlagListener)
+ mView.setOnClickListener(null)
+ }
+
+ private fun updateChip() {
+ mView.text.text = tracker.currentUserName
+ mView.avatar.setImageDrawable(tracker.currentUserAvatar)
+ }
+
+ private fun updateEnabled() {
+ if (featureController.isStatusBarUserSwitcherFeatureEnabled() &&
+ tracker.userSwitcherEnabled) {
+ mView.visibility = View.VISIBLE
+ updateChip()
+ } else {
+ mView.visibility = View.GONE
+ }
+ }
+}
+
+interface StatusBarUserSwitcherController {
+ fun init()
+}
+
+private const val TAG = "SbUserSwitcherController"
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
new file mode 100644
index 0000000..7bae9ff
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/userswitcher/StatusBarUserSwitcherFeatureController.kt
@@ -0,0 +1,63 @@
+/*
+ * 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.statusbar.phone.userswitcher
+
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.statusbar.policy.CallbackController
+
+import javax.inject.Inject
+
+@SysUISingleton
+class StatusBarUserSwitcherFeatureController @Inject constructor(
+ private val flags: FeatureFlags
+) : CallbackController<OnUserSwitcherPreferenceChangeListener> {
+ private val listeners = mutableListOf<OnUserSwitcherPreferenceChangeListener>()
+
+ init {
+ flags.addListener(Flags.STATUS_BAR_USER_SWITCHER) {
+ it.requestNoRestart()
+ notifyListeners()
+ }
+ }
+
+ fun isStatusBarUserSwitcherFeatureEnabled(): Boolean {
+ return flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
+ }
+
+ override fun addCallback(listener: OnUserSwitcherPreferenceChangeListener) {
+ if (!listeners.contains(listener)) {
+ listeners.add(listener)
+ }
+ }
+
+ override fun removeCallback(listener: OnUserSwitcherPreferenceChangeListener) {
+ listeners.remove(listener)
+ }
+
+ private fun notifyListeners() {
+ val enabled = flags.isEnabled(Flags.STATUS_BAR_USER_SWITCHER)
+ listeners.forEach {
+ it.onUserSwitcherPreferenceChange(enabled)
+ }
+ }
+}
+
+interface OnUserSwitcherPreferenceChangeListener {
+ fun onUserSwitcherPreferenceChange(enabled: Boolean)
+}
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/src/com/android/systemui/statusbar/policy/UserSwitcherController.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
index 49e712d..1b73595 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/UserSwitcherController.java
@@ -19,7 +19,6 @@
import static android.os.UserManager.SWITCHABILITY_STATUS_OK;
import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
@@ -427,22 +426,6 @@
return mSimpleUserSwitcher;
}
- public boolean useFullscreenUserSwitcher() {
- // Use adb to override:
- // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off.
- // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on.
- // Restart SystemUI or adb reboot.
- final int DEFAULT = -1;
- final int overrideUseFullscreenUserSwitcher =
- whitelistIpcs(() -> Settings.System.getInt(mContext.getContentResolver(),
- "enable_fullscreen_user_switcher", DEFAULT));
- if (overrideUseFullscreenUserSwitcher != DEFAULT) {
- return overrideUseFullscreenUserSwitcher != 0;
- }
- // Otherwise default to the build setting.
- return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
- }
-
public void setResumeUserOnGuestLogout(boolean resume) {
mResumeUserOnGuestLogout = resume;
}
diff --git a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
index 09dbfee..fa4f314 100644
--- a/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
+++ b/packages/SystemUI/src/com/android/systemui/util/AutoMarqueeTextView.java
@@ -19,7 +19,6 @@
import android.content.Context;
import android.text.TextUtils;
import android.util.AttributeSet;
-import android.widget.TextView;
/**
* TextView that changes its ellipsize value with its visibility.
@@ -27,7 +26,7 @@
* The View responds to changes in user-visibility to change its ellipsize from MARQUEE to END
* and back. Useful for TextView that need to marquee forever.
*/
-public class AutoMarqueeTextView extends TextView {
+public class AutoMarqueeTextView extends SafeMarqueeTextView {
private boolean mAggregatedVisible = false;
diff --git a/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
new file mode 100644
index 0000000..1c1a990
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/SafeMarqueeTextView.kt
@@ -0,0 +1,44 @@
+package com.android.systemui.util
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.ViewGroup
+import android.widget.TextView
+
+/**
+ * A TextField that doesn't relayout when changing from marquee to ellipsis.
+ */
+@SuppressLint("AppCompatCustomView")
+open class SafeMarqueeTextView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ defStyleAttr: Int = 0,
+ defStyleRes: Int = 0
+) : TextView(context, attrs, defStyleAttr, defStyleRes) {
+
+ private var safelyIgnoreLayout = false
+ private val hasStableWidth
+ get() = layoutParams.width != ViewGroup.LayoutParams.WRAP_CONTENT
+
+ override fun requestLayout() {
+ if (safelyIgnoreLayout) {
+ return
+ }
+ super.requestLayout()
+ }
+
+ override fun startMarquee() {
+ val wasIgnoring = safelyIgnoreLayout
+ safelyIgnoreLayout = hasStableWidth
+ super.startMarquee()
+ safelyIgnoreLayout = wasIgnoring
+ }
+
+ override fun stopMarquee() {
+ val wasIgnoring = safelyIgnoreLayout
+ safelyIgnoreLayout = hasStableWidth
+ super.stopMarquee()
+ safelyIgnoreLayout = wasIgnoring
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/Utils.java b/packages/SystemUI/src/com/android/systemui/util/Utils.java
index 407dc5e..71d8e33 100644
--- a/packages/SystemUI/src/com/android/systemui/util/Utils.java
+++ b/packages/SystemUI/src/com/android/systemui/util/Utils.java
@@ -162,6 +162,14 @@
}
/**
+ * Returns true if the device should use the collapsed layout for the media player when in
+ * landscape (or seascape) orientation
+ */
+ public static boolean useCollapsedMediaInLandscape(Resources resources) {
+ return resources.getBoolean(R.bool.config_quickSettingsMediaLandscapeCollapsed);
+ }
+
+ /**
* Returns true if the device should use the split notification shade, based on orientation and
* screen width.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
new file mode 100644
index 0000000..b506808
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/util/drawable/DrawableSize.kt
@@ -0,0 +1,123 @@
+package com.android.systemui.util.drawable
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.Canvas
+import android.graphics.drawable.Animatable
+import android.graphics.drawable.Animatable2
+import android.graphics.drawable.AnimatedImageDrawable
+import android.graphics.drawable.AnimatedRotateDrawable
+import android.graphics.drawable.AnimatedStateListDrawable
+import android.graphics.drawable.AnimatedVectorDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.Drawable
+import android.util.Log
+import androidx.annotation.Px
+import com.android.systemui.util.traceSection
+
+class DrawableSize {
+
+ companion object {
+
+ const val TAG = "SysUiDrawableSize"
+
+ /**
+ * Downscales passed Drawable to set maximum width and height. This will only
+ * be done for Drawables that can be downscaled non-destructively - e.g. animated
+ * and stateful drawables will no be downscaled.
+ *
+ * Downscaling will keep the aspect ratio.
+ * This method will not touch drawables that already fit into size specification.
+ *
+ * @param resources Resources on which to base the density of resized drawable.
+ * @param drawable Drawable to downscale.
+ * @param maxWidth Maximum width of the downscaled drawable.
+ * @param maxHeight Maximum height of the downscaled drawable.
+ *
+ * @return returns downscaled drawable if it's possible to downscale it or original if it's
+ * not.
+ */
+ @JvmStatic
+ fun downscaleToSize(
+ res: Resources,
+ drawable: Drawable,
+ @Px maxWidth: Int,
+ @Px maxHeight: Int
+ ): Drawable {
+ traceSection("DrawableSize#downscaleToSize") {
+ // Bitmap drawables can contain big bitmaps as their content while sneaking it past
+ // us using density scaling. Inspect inside the Bitmap drawables for actual bitmap
+ // size for those.
+ val originalWidth = (drawable as? BitmapDrawable)?.bitmap?.width
+ ?: drawable.intrinsicWidth
+ val originalHeight = (drawable as? BitmapDrawable)?.bitmap?.height
+ ?: drawable.intrinsicHeight
+
+ // Don't touch drawable if we can't resolve sizes for whatever reason.
+ if (originalWidth <= 0 || originalHeight <= 0) {
+ return drawable
+ }
+
+ // Do not touch drawables that are already within bounds.
+ if (originalWidth < maxWidth && originalHeight < maxHeight) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Not resizing $originalWidth x $originalHeight" + " " +
+ "to $maxWidth x $maxHeight")
+ }
+
+ return drawable
+ }
+
+ if (!isSimpleBitmap(drawable)) {
+ return drawable
+ }
+
+ val scaleWidth = maxWidth.toFloat() / originalWidth.toFloat()
+ val scaleHeight = maxHeight.toFloat() / originalHeight.toFloat()
+ val scale = minOf(scaleHeight, scaleWidth)
+
+ val width = (originalWidth * scale).toInt()
+ val height = (originalHeight * scale).toInt()
+
+ if (width <= 0 || height <= 0) {
+ Log.w(TAG, "Attempted to resize ${drawable.javaClass.simpleName} " +
+ "from $originalWidth x $originalHeight to invalid $width x $height.")
+ return drawable
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Resizing large drawable (${drawable.javaClass.simpleName}) " +
+ "from $originalWidth x $originalHeight to $width x $height")
+ }
+
+ // We want to keep existing config if it's more efficient than 32-bit RGB.
+ val config = (drawable as? BitmapDrawable)?.bitmap?.config
+ ?: Bitmap.Config.ARGB_8888
+ val scaledDrawableBitmap = Bitmap.createBitmap(width, height, config)
+ val canvas = Canvas(scaledDrawableBitmap)
+
+ val originalBounds = drawable.bounds
+ drawable.setBounds(0, 0, width, height)
+ drawable.draw(canvas)
+ drawable.bounds = originalBounds
+
+ return BitmapDrawable(res, scaledDrawableBitmap)
+ }
+ }
+
+ private fun isSimpleBitmap(drawable: Drawable): Boolean {
+ return !(drawable.isStateful || isAnimated(drawable))
+ }
+
+ private fun isAnimated(drawable: Drawable): Boolean {
+ if (drawable is Animatable || drawable is Animatable2) {
+ return true
+ }
+
+ return drawable is AnimatedImageDrawable ||
+ drawable is AnimatedRotateDrawable ||
+ drawable is AnimatedStateListDrawable ||
+ drawable is AnimatedVectorDrawable
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
index 089650c..8215360 100644
--- a/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
+++ b/packages/SystemUI/src/com/android/systemui/util/leak/DumpTruck.java
@@ -50,7 +50,7 @@
private static final int BUFSIZ = 1024 * 1024; // 1MB
private final Context context;
- private GarbageMonitor mGarbageMonitor;
+ private final GarbageMonitor mGarbageMonitor;
private Uri hprofUri;
private long rss;
final StringBuilder body = new StringBuilder();
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/doze/DozeTriggersTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
index 35dca7e..7fc354f 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeTriggersTest.java
@@ -45,6 +45,7 @@
import com.android.systemui.dock.DockManager;
import com.android.systemui.doze.DozeTriggers.DozingUpdateUiEvent;
import com.android.systemui.statusbar.phone.DozeParameters;
+import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.DevicePostureController;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.util.concurrency.FakeExecutor;
@@ -90,6 +91,8 @@
private KeyguardStateController mKeyguardStateController;
@Mock
private DevicePostureController mDevicePostureController;
+ @Mock
+ private BatteryController mBatteryController;
private DozeTriggers mTriggers;
private FakeSensorManager mSensors;
@@ -122,7 +125,7 @@
asyncSensorManager, wakeLock, mDockManager, mProximitySensor,
mProximityCheck, mock(DozeLog.class), mBroadcastDispatcher, new FakeSettings(),
mAuthController, mExecutor, mUiEventLogger, mKeyguardStateController,
- mDevicePostureController);
+ mDevicePostureController, mBatteryController);
mTriggers.setDozeMachine(mMachine);
waitForSensorManager();
}
@@ -230,7 +233,9 @@
when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
// WHEN the pick up gesture is triggered and keyguard isn't occluded
+ // and device isn't on a wireless charger
when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mBatteryController.isPluggedInWireless()).thenReturn(false);
mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
// THEN wakeup
@@ -244,6 +249,22 @@
// WHEN the pick up gesture is triggered and keyguard IS occluded
when(mKeyguardStateController.isOccluded()).thenReturn(true);
+ when(mBatteryController.isPluggedInWireless()).thenReturn(false);
+ mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
+
+ // THEN never wakeup
+ verify(mMachine, never()).wakeUp();
+ }
+
+ @Test
+ public void testPickupGestureWirelessCharger() {
+ // GIVEN device is in doze (screen blank, but running doze sensors)
+ when(mMachine.getState()).thenReturn(DozeMachine.State.DOZE);
+
+ // WHEN the pick up gesture is triggered
+ // and device IS on a wireless charger
+ when(mKeyguardStateController.isOccluded()).thenReturn(false);
+ when(mBatteryController.isPluggedInWireless()).thenReturn(true);
mTriggers.onSensor(DozeLog.REASON_SENSOR_PICKUP, 100, 100, null);
// THEN never wakeup
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/ComplicationLayoutEngineTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
index d5ab708..64b267d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/ComplicationLayoutEngineTest.java
@@ -112,7 +112,7 @@
Complication.CATEGORY_STANDARD,
mLayout);
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
addComplication(engine, firstViewInfo);
// Ensure the view is added to the top end corner
@@ -139,7 +139,7 @@
Complication.CATEGORY_STANDARD,
mLayout);
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
addComplication(engine, firstViewInfo);
// Ensure the view is added to the top end corner
@@ -155,7 +155,7 @@
*/
@Test
public void testDirectionLayout() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
@@ -203,7 +203,7 @@
*/
@Test
public void testPositionLayout() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
@@ -285,11 +285,86 @@
}
/**
+ * Ensures margin is applied
+ */
+ @Test
+ public void testMargin() {
+ final int margin = 5;
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, margin);
+
+ final ViewInfo firstViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_DOWN,
+ 0),
+ Complication.CATEGORY_STANDARD,
+ mLayout);
+
+ addComplication(engine, firstViewInfo);
+
+ final ViewInfo secondViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 0),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, secondViewInfo);
+
+ firstViewInfo.clearInvocations();
+ secondViewInfo.clearInvocations();
+
+ final ViewInfo thirdViewInfo = new ViewInfo(
+ new ComplicationLayoutParams(
+ 100,
+ 100,
+ ComplicationLayoutParams.POSITION_TOP
+ | ComplicationLayoutParams.POSITION_END,
+ ComplicationLayoutParams.DIRECTION_START,
+ 1),
+ Complication.CATEGORY_SYSTEM,
+ mLayout);
+
+ addComplication(engine, thirdViewInfo);
+
+ // The first added view should now be underneath the second view.
+ verifyChange(firstViewInfo, false, lp -> {
+ assertThat(lp.topToBottom == thirdViewInfo.view.getId()).isTrue();
+ assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.topMargin).isEqualTo(margin);
+ });
+
+ // The second view should be in underneath the third view.
+ verifyChange(secondViewInfo, false, lp -> {
+ assertThat(lp.endToStart == thirdViewInfo.view.getId()).isTrue();
+ assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.getMarginEnd()).isEqualTo(margin);
+ });
+
+ // The third view should be in at the top.
+ verifyChange(thirdViewInfo, true, lp -> {
+ assertThat(lp.topToTop == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.endToEnd == ConstraintLayout.LayoutParams.PARENT_ID).isTrue();
+ assertThat(lp.getMarginStart()).isEqualTo(0);
+ assertThat(lp.getMarginEnd()).isEqualTo(0);
+ assertThat(lp.topMargin).isEqualTo(0);
+ assertThat(lp.bottomMargin).isEqualTo(0);
+ });
+ }
+
+ /**
* Ensures layout in a particular position updates.
*/
@Test
public void testRemoval() {
- final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout);
+ final ComplicationLayoutEngine engine = new ComplicationLayoutEngine(mLayout, 0);
final ViewInfo firstViewInfo = new ViewInfo(
new ComplicationLayoutParams(
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/dreams/touch/BouncerSwipeTouchHandlerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
index 1a8326f..cad98f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/touch/BouncerSwipeTouchHandlerTest.java
@@ -32,6 +32,7 @@
import android.view.MotionEvent;
import android.view.VelocityTracker;
+import androidx.test.filters.FlakyTest;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
@@ -117,6 +118,7 @@
/**
* Ensures expansion only happens when touch down happens in valid part of the screen.
*/
+ @FlakyTest
@Test
public void testSessionStart() {
mTouchHandler.onSessionStart(mTouchSession);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
index d1f505b..51c2580 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/KeyguardSliceProviderTest.java
@@ -90,6 +90,7 @@
private DozeParameters mDozeParameters;
@Mock
private NextAlarmController mNextAlarmController;
+ @Mock
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private TestableKeyguardSliceProvider mProvider;
private boolean mIsZenMode;
@@ -97,7 +98,6 @@
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
- mKeyguardUpdateMonitor = mDependency.injectMockDependency(KeyguardUpdateMonitor.class);
mIsZenMode = false;
mProvider = new TestableKeyguardSliceProvider();
mProvider.setContextAvailableCallback(context -> { });
@@ -265,6 +265,7 @@
mStatusBarStateController = KeyguardSliceProviderTest.this.mStatusBarStateController;
mKeyguardBypassController = KeyguardSliceProviderTest.this.mKeyguardBypassController;
mMediaManager = KeyguardSliceProviderTest.this.mNotificationMediaManager;
+ mKeyguardUpdateMonitor = KeyguardSliceProviderTest.this.mKeyguardUpdateMonitor;
}
@Override
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/KeyguardMediaControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
index 43d9a75..1484c9d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/KeyguardMediaControllerTest.kt
@@ -36,6 +36,7 @@
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.verify
import org.mockito.junit.MockitoJUnit
import org.mockito.Mockito.`when` as whenever
@@ -51,6 +52,8 @@
private lateinit var statusBarStateController: SysuiStatusBarStateController
@Mock
private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var mediaFlags: MediaFlags
@Mock
private lateinit var notificationLockscreenUserManager: NotificationLockscreenUserManager
@@ -70,13 +73,15 @@
.thenReturn(true)
whenever(mediaHost.hostView).thenReturn(hostView)
hostView.layoutParams = FrameLayout.LayoutParams(100, 100)
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(false)
keyguardMediaController = KeyguardMediaController(
mediaHost,
bypassController,
statusBarStateController,
notificationLockscreenUserManager,
context,
- configurationController
+ configurationController,
+ mediaFlags
)
keyguardMediaController.attachSinglePaneContainer(mediaContainerView)
keyguardMediaController.useSplitShade = false
@@ -95,7 +100,6 @@
fun testVisibleOnKeyguardOrFullScreenUserSwitcher() {
testStateVisibility(StatusBarState.SHADE, GONE)
testStateVisibility(StatusBarState.SHADE_LOCKED, GONE)
- testStateVisibility(StatusBarState.FULLSCREEN_USER_SWITCHER, VISIBLE)
testStateVisibility(StatusBarState.KEYGUARD, VISIBLE)
}
@@ -151,4 +155,24 @@
assertTrue("HostView wasn't attached to the single pane container",
mediaContainerView.childCount == 1)
}
+
+ @Test
+ fun testNotificationLayout_collapsedPlayer() {
+ verify(mediaHost).expansion = MediaHostState.COLLAPSED
+ }
+
+ @Test
+ fun testSessionLayout_expandedPlayer() {
+ whenever(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+ keyguardMediaController = KeyguardMediaController(
+ mediaHost,
+ bypassController,
+ statusBarStateController,
+ notificationLockscreenUserManager,
+ context,
+ configurationController,
+ mediaFlags
+ )
+ verify(mediaHost).expansion = MediaHostState.EXPANDED
+ }
}
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/nearby/NearbyMediaDevicesServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt
new file mode 100644
index 0000000..c261086
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/nearby/NearbyMediaDevicesServiceTest.kt
@@ -0,0 +1,124 @@
+package com.android.systemui.media.nearby
+
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.shared.media.INearbyMediaDevicesProvider
+import com.android.systemui.shared.media.INearbyMediaDevicesService
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_LONG
+import com.android.systemui.shared.media.INearbyMediaDevicesUpdateCallback.RANGE_WITHIN_REACH
+import com.android.systemui.shared.media.NearbyDevice
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+
+@SmallTest
+class NearbyMediaDevicesServiceTest : SysuiTestCase() {
+
+ private lateinit var service: NearbyMediaDevicesService
+ private lateinit var binderInterface: INearbyMediaDevicesService
+
+ @Before
+ fun setUp() {
+ service = NearbyMediaDevicesService()
+ binderInterface = INearbyMediaDevicesService.Stub.asInterface(service.onBind(null))
+ }
+
+ @Test
+ fun getCurrentNearbyDevices_noProviderRegistered_returnsEmptyList() {
+ assertThat(service.getCurrentNearbyDevices()).isEmpty()
+ }
+
+ @Test
+ fun getCurrentNearbyDevices_providerRegistered_returnsProviderInfo() {
+ val nearbyDevice1 = NearbyDevice("routeId1", RANGE_LONG)
+ val nearbyDevice2 = NearbyDevice("routeId2", RANGE_WITHIN_REACH)
+ val provider = object : INearbyMediaDevicesProvider.Stub() {
+ override fun getCurrentNearbyDevices(): List<NearbyDevice> {
+ return listOf(nearbyDevice1, nearbyDevice2)
+ }
+
+ override fun registerNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {}
+ override fun unregisterNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {}
+ }
+ binderInterface.registerProvider(provider)
+
+ val returnedNearbyDevices = service.getCurrentNearbyDevices()
+
+ assertThat(returnedNearbyDevices).isEqualTo(listOf(nearbyDevice1, nearbyDevice2))
+ }
+
+ @Test
+ fun registerNearbyDevicesCallback_noProviderRegistered_noCrash() {
+ // No assert, just needs no crash
+ service.registerNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
+ override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+ })
+ }
+
+ @Test
+ fun registerNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
+ val provider = object : INearbyMediaDevicesProvider.Stub() {
+ var registeredCallback: INearbyMediaDevicesUpdateCallback? = null
+ override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
+
+ override fun registerNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {
+ registeredCallback = callback
+ }
+
+ override fun unregisterNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {}
+ }
+ binderInterface.registerProvider(provider)
+
+ val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
+ override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+ }
+
+ service.registerNearbyDevicesCallback(callback)
+
+ assertThat(provider.registeredCallback).isEqualTo(callback)
+ }
+
+ @Test
+ fun unregisterNearbyDevicesCallback_noProviderRegistered_noCrash() {
+ // No assert, just needs no crash
+ service.unregisterNearbyDevicesCallback(object : INearbyMediaDevicesUpdateCallback.Stub() {
+ override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+ })
+ }
+
+ @Test
+ fun unregisterNearbyDevicesCallback_providerRegistered_providerReceivesCallback() {
+ val provider = object : INearbyMediaDevicesProvider.Stub() {
+ var unregisteredCallback: INearbyMediaDevicesUpdateCallback? = null
+ override fun getCurrentNearbyDevices(): List<NearbyDevice> = listOf()
+
+ override fun registerNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {}
+
+ override fun unregisterNearbyDevicesCallback(
+ callback: INearbyMediaDevicesUpdateCallback?
+ ) {
+ unregisteredCallback = callback
+ }
+ }
+ binderInterface.registerProvider(provider)
+
+ val callback = object : INearbyMediaDevicesUpdateCallback.Stub() {
+ override fun nearbyDeviceUpdate(routeId: String?, rangeZone: Int) {}
+ }
+
+ service.unregisterNearbyDevicesCallback(callback)
+
+ assertThat(provider.unregisteredCallback).isEqualTo(callback)
+ }
+}
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..cb05d03 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,33 @@
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,25 +59,17 @@
private lateinit var mediaTttCommandLineHelper: MediaTttCommandLineHelper
@Mock
- private lateinit var mediaTttChipControllerReceiver: MediaTttChipControllerReceiver
- @Mock
- private lateinit var mediaSenderService: IDeviceSenderService.Stub
- private lateinit var mediaSenderServiceComponentName: ComponentName
+ private lateinit var statusBarManager: StatusBarManager
@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,
- mediaTttChipControllerReceiver,
+ FakeExecutor(FakeSystemClock()),
)
}
@@ -83,174 +81,147 @@
}
@Test(expected = IllegalStateException::class)
- fun constructor_addReceiverCommandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the add command, it
+ fun constructor_receiverCommandAlreadyRegistered() {
+ // Since creating the chip controller should automatically register the receiver command, it
// should throw when registering it again.
- commandRegistry.registerCommand(
- ADD_CHIP_COMMAND_RECEIVER_TAG
- ) { EmptyCommand() }
- }
-
- @Test(expected = IllegalStateException::class)
- fun constructor_removeReceiverCommandAlreadyRegistered() {
- // Since creating the chip controller should automatically register the remove command, it
- // should throw when registering it again.
- commandRegistry.registerCommand(
- REMOVE_CHIP_COMMAND_RECEIVER_TAG
- ) { EmptyCommand() }
+ commandRegistry.registerCommand(RECEIVER_COMMAND) { EmptyCommand() }
}
@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
- fun receiver_addCommand_chipAdded() {
- commandRegistry.onShellCommand(pw, arrayOf(ADD_CHIP_COMMAND_RECEIVER_TAG))
+ fun receiver_closeToSender_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getReceiverCommand(CLOSE_TO_SENDER_STATE))
- verify(mediaTttChipControllerReceiver).displayChip(any(ChipStateReceiver::class.java))
+ verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER),
+ any()
+ )
}
@Test
- fun receiver_removeCommand_chipRemoved() {
- commandRegistry.onShellCommand(pw, arrayOf(REMOVE_CHIP_COMMAND_RECEIVER_TAG))
+ fun receiver_farFromSender_serviceCallbackCalled() {
+ commandRegistry.onShellCommand(pw, getReceiverCommand(FAR_FROM_SENDER_STATE))
- verify(mediaTttChipControllerReceiver).removeChip()
+ verify(statusBarManager).updateMediaTapToTransferReceiverDisplay(
+ eq(StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER),
+ any()
+ )
}
- private fun getMoveCloserToStartCastCommand(): Array<String> =
- arrayOf(
- SENDER_COMMAND,
- DEVICE_NAME,
- MOVE_CLOSER_TO_START_CAST_COMMAND_NAME
- )
+ private fun getSenderCommand(displayState: String): Array<String> =
+ arrayOf(SENDER_COMMAND, DEVICE_NAME, displayState)
- 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 getReceiverCommand(displayState: String): Array<String> =
+ arrayOf(RECEIVER_COMMAND, displayState)
class EmptyCommand : Command {
override fun execute(pw: PrintWriter, args: List<String>) {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
index 1d1265b1..fce4954 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/media/taptotransfer/receiver/MediaTttChipControllerReceiverTest.kt
@@ -16,7 +16,9 @@
package com.android.systemui.media.taptotransfer.receiver
+import android.app.StatusBarManager
import android.graphics.drawable.Icon
+import android.media.MediaRoute2Info
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
@@ -24,6 +26,7 @@
import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.CommandQueue
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
import org.junit.Before
@@ -31,7 +34,8 @@
import org.junit.Test
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
@SmallTest
@@ -41,11 +45,55 @@
@Mock
private lateinit var windowManager: WindowManager
+ @Mock
+ private lateinit var commandQueue: CommandQueue
+ private lateinit var commandQueueCallback: CommandQueue.Callbacks
@Before
fun setUp() {
MockitoAnnotations.initMocks(this)
- controllerReceiver = MediaTttChipControllerReceiver(context, windowManager)
+ controllerReceiver = MediaTttChipControllerReceiver(commandQueue, context, windowManager)
+
+ val callbackCaptor = ArgumentCaptor.forClass(CommandQueue.Callbacks::class.java)
+ verify(commandQueue).addCallback(callbackCaptor.capture())
+ commandQueueCallback = callbackCaptor.value!!
+ }
+
+ @Test
+ fun commandQueueCallback_closeToSender_triggersChip() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo
+ )
+
+ assertThat(getChipView().getAppIconView().contentDescription).isEqualTo(ROUTE_NAME)
+ }
+
+ @Test
+ fun commandQueueCallback_farFromSender_noChipShown() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo
+ )
+
+ verify(windowManager, never()).addView(any(), any())
+ }
+
+ @Test
+ fun commandQueueCallback_closeThenFar_chipShownThenHidden() {
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_CLOSE_TO_SENDER,
+ routeInfo
+ )
+
+ commandQueueCallback.updateMediaTapToTransferReceiverDisplay(
+ StatusBarManager.MEDIA_TRANSFER_RECEIVER_STATE_FAR_FROM_SENDER,
+ routeInfo
+ )
+
+ val viewCaptor = ArgumentCaptor.forClass(View::class.java)
+ verify(windowManager).addView(viewCaptor.capture(), any())
+ verify(windowManager).removeView(viewCaptor.value)
}
@Test
@@ -61,9 +109,14 @@
private fun getChipView(): ViewGroup {
val viewCaptor = ArgumentCaptor.forClass(View::class.java)
- Mockito.verify(windowManager).addView(viewCaptor.capture(), any())
+ verify(windowManager).addView(viewCaptor.capture(), any())
return viewCaptor.value as ViewGroup
}
private fun ViewGroup.getAppIconView() = this.requireViewById<ImageView>(R.id.app_icon)
}
+
+private const val ROUTE_NAME = "Test name"
+private val routeInfo = MediaRoute2Info.Builder("id", ROUTE_NAME)
+ .addFeature("feature")
+ .build()
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/navigationbar/NavBarHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
index a445d6f..3a95ed3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavBarHelperTest.java
@@ -31,6 +31,8 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
+import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.recents.OverviewProxyService;
@@ -55,10 +57,12 @@
@Mock
AccessibilityManager mAccessibilityManager;
@Mock
- AccessibilityManagerWrapper mAccessibilityManagerWrapper;
- @Mock
AccessibilityButtonModeObserver mAccessibilityButtonModeObserver;
@Mock
+ AccessibilityButtonTargetsObserver mAccessibilityButtonTargetObserver;
+ @Mock
+ SystemActions mSystemActions;
+ @Mock
OverviewProxyService mOverviewProxyService;
@Mock
Lazy<AssistManager> mAssistManagerLazy;
@@ -85,8 +89,9 @@
when(mUserTracker.getUserId()).thenReturn(1);
mNavBarHelper = new NavBarHelper(mContext, mAccessibilityManager,
- mAccessibilityManagerWrapper, mAccessibilityButtonModeObserver,
- mOverviewProxyService, mAssistManagerLazy, () -> Optional.of(mock(StatusBar.class)),
+ mAccessibilityButtonModeObserver, mAccessibilityButtonTargetObserver,
+ mSystemActions, mOverviewProxyService, mAssistManagerLazy,
+ () -> Optional.of(mock(StatusBar.class)),
mNavigationModeController, mUserTracker, mDumpManager);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 9ca898b..090ce43 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -18,6 +18,7 @@
import static android.app.StatusBarManager.NAVIGATION_HINT_BACK_ALT;
import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SHOWN;
+import static android.app.StatusBarManager.NAVIGATION_HINT_IME_SWITCHER_SHOWN;
import static android.inputmethodservice.InputMethodService.BACK_DISPOSITION_DEFAULT;
import static android.inputmethodservice.InputMethodService.IME_INVISIBLE;
import static android.inputmethodservice.InputMethodService.IME_VISIBLE;
@@ -73,6 +74,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.SysuiTestableContext;
import com.android.systemui.accessibility.AccessibilityButtonModeObserver;
+import com.android.systemui.accessibility.AccessibilityButtonTargetsObserver;
import com.android.systemui.accessibility.SystemActions;
import com.android.systemui.assist.AssistManager;
import com.android.systemui.broadcast.BroadcastDispatcher;
@@ -120,6 +122,8 @@
private SysuiTestableContext mSysuiTestableContextExternal;
@Mock
+ private SystemActions mSystemActions;
+ @Mock
private OverviewProxyService mOverviewProxyService;
@Mock
private StatusBarStateController mStatusBarStateController;
@@ -182,8 +186,9 @@
mDependency.injectTestDependency(NavigationModeController.class, mNavigationModeController);
TestableLooper.get(this).runWithLooper(() -> {
mNavBarHelper = spy(new NavBarHelper(mContext, mock(AccessibilityManager.class),
- mock(AccessibilityManagerWrapper.class),
- mock(AccessibilityButtonModeObserver.class), mOverviewProxyService,
+ mock(AccessibilityButtonModeObserver.class),
+ mock(AccessibilityButtonTargetsObserver.class),
+ mSystemActions, mOverviewProxyService,
() -> mock(AssistManager.class), () -> Optional.of(mStatusBar),
mock(NavigationModeController.class), mock(UserTracker.class),
mock(DumpManager.class)));
@@ -285,20 +290,26 @@
BACK_DISPOSITION_DEFAULT, true);
// Verify IME window state will be updated in default NavBar & external NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
defaultNavBar.getNavigationIconHints());
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((externalNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
externalNavBar.setImeWindowStatus(EXTERNAL_DISPLAY_ID, null, IME_VISIBLE,
BACK_DISPOSITION_DEFAULT, true);
defaultNavBar.setImeWindowStatus(
DEFAULT_DISPLAY, null, IME_INVISIBLE, BACK_DISPOSITION_DEFAULT, false);
// Verify IME window state will be updated in external NavBar & default NavBar state reset.
- assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN,
+ assertEquals(NAVIGATION_HINT_BACK_ALT | NAVIGATION_HINT_IME_SHOWN
+ | NAVIGATION_HINT_IME_SWITCHER_SHOWN,
externalNavBar.getNavigationIconHints());
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((defaultNavBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
}
@Test
@@ -316,6 +327,8 @@
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
// Verify navbar didn't alter and showing back icon when the keyguard is showing without
// requesting IME insets visible.
@@ -324,6 +337,8 @@
BACK_DISPOSITION_DEFAULT, true);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertFalse((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
// Verify navbar altered and showing back icon when the keyguard is showing and
// requesting IME insets visible.
@@ -333,6 +348,8 @@
BACK_DISPOSITION_DEFAULT, true);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_BACK_ALT) != 0);
assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SHOWN) != 0);
+ assertTrue((mNavigationBar.getNavigationIconHints() & NAVIGATION_HINT_IME_SWITCHER_SHOWN)
+ != 0);
}
@Test
@@ -346,7 +363,7 @@
NavBarHelper.NavbarTaskbarStateUpdater.class));
// Should be safe even though the internal view is now null.
- mNavigationBar.updateAcessibilityStateFlags();
+ mNavigationBar.updateAccessibilityStateFlags();
}
private NavigationBar createNavBar(Context context) {
@@ -372,12 +389,10 @@
mock(ShadeController.class),
mock(NotificationRemoteInputManager.class),
mock(NotificationShadeDepthController.class),
- mock(SystemActions.class),
mHandler,
mock(NavigationBarOverlayController.class),
mUiEventLogger,
mNavBarHelper,
- mock(UserTracker.class),
mLightBarController,
mLightBarcontrollerFactory,
mAutoHideController,
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/QSFragmentTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
index 8b353d9..3266d6a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSFragmentTest.java
@@ -47,6 +47,7 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.dagger.QSFragmentComponent;
import com.android.systemui.qs.external.CustomTileStatePersister;
+import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
import com.android.systemui.qs.tileimpl.QSFactoryImpl;
@@ -136,6 +137,7 @@
QSFragment qs = (QSFragment) mFragment;
mFragments.dispatchResume();
processAllMessages();
+
QSTileHost host = new QSTileHost(mContext, mock(StatusBarIconController.class),
mock(QSFactoryImpl.class), new Handler(), Looper.myLooper(),
mock(PluginManager.class), mock(TunerService.class),
@@ -143,7 +145,7 @@
mock(BroadcastDispatcher.class), Optional.of(mock(StatusBar.class)),
mock(QSLogger.class), mock(UiEventLogger.class), mock(UserTracker.class),
mock(SecureSettings.class), mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder);
+ mTileServiceRequestControllerBuilder, mock(TileLifecycleManager.Factory.class));
qs.setHost(host);
qs.setListening(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
index 1e651be..8872e28 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java
@@ -34,6 +34,7 @@
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
+import android.os.UserHandle;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
@@ -50,13 +51,13 @@
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
-import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.qs.QSFactory;
import com.android.systemui.plugins.qs.QSTile;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.qs.external.CustomTile;
import com.android.systemui.qs.external.CustomTileStatePersister;
+import com.android.systemui.qs.external.TileLifecycleManager;
import com.android.systemui.qs.external.TileServiceKey;
import com.android.systemui.qs.external.TileServiceRequestController;
import com.android.systemui.qs.logging.QSLogger;
@@ -127,6 +128,10 @@
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
+ @Mock
+ private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
+ @Mock
+ private TileLifecycleManager mTileLifecycleManager;
private Handler mHandler;
private TestableLooper mLooper;
@@ -139,6 +144,8 @@
mHandler = new Handler(mLooper.getLooper());
when(mTileServiceRequestControllerBuilder.create(any()))
.thenReturn(mTileServiceRequestController);
+ when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
+ .thenReturn(mTileLifecycleManager);
mSecureSettings = new FakeSettings();
mSecureSettings.putStringForUser(
@@ -146,7 +153,8 @@
mQSTileHost = new TestQSTileHost(mContext, mIconController, mDefaultFactory, mHandler,
mLooper.getLooper(), mPluginManager, mTunerService, mAutoTiles, mDumpManager,
mBroadcastDispatcher, mStatusBar, mQSLogger, mUiEventLogger, mUserTracker,
- mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder);
+ mSecureSettings, mCustomTileStatePersister, mTileServiceRequestControllerBuilder,
+ mTileLifecycleManagerFactory);
setUpTileFactory();
}
@@ -432,11 +440,13 @@
BroadcastDispatcher broadcastDispatcher, StatusBar statusBar, QSLogger qsLogger,
UiEventLogger uiEventLogger, UserTracker userTracker,
SecureSettings secureSettings, CustomTileStatePersister customTileStatePersister,
- TileServiceRequestController.Builder tileServiceRequestControllerBuilder) {
+ TileServiceRequestController.Builder tileServiceRequestControllerBuilder,
+ TileLifecycleManager.Factory tileLifecycleManagerFactory) {
super(context, iconController, defaultFactory, mainHandler, bgLooper, pluginManager,
tunerService, autoTiles, dumpManager, broadcastDispatcher,
Optional.of(statusBar), qsLogger, uiEventLogger, userTracker, secureSettings,
- customTileStatePersister, tileServiceRequestControllerBuilder);
+ customTileStatePersister, tileServiceRequestControllerBuilder,
+ tileLifecycleManagerFactory);
}
@Override
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..1eb16fd 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QuickQSPanelControllerTest.kt
@@ -23,11 +23,14 @@
import com.android.internal.logging.testing.UiEventLoggerFake
import com.android.systemui.SysuiTestCase
import com.android.systemui.dump.DumpManager
+import com.android.systemui.media.MediaFlags
import com.android.systemui.media.MediaHost
+import com.android.systemui.media.MediaHostState
import com.android.systemui.plugins.qs.QSTile
import com.android.systemui.plugins.qs.QSTileView
import com.android.systemui.qs.customize.QSCustomizerController
import com.android.systemui.qs.logging.QSLogger
+import com.android.systemui.util.leak.RotationUtils
import org.junit.After
import org.junit.Before
import org.junit.Test
@@ -55,6 +58,8 @@
@Mock
private lateinit var mediaHost: MediaHost
@Mock
+ private lateinit var mediaFlags: MediaFlags
+ @Mock
private lateinit var metricsLogger: MetricsLogger
private val uiEventLogger = UiEventLoggerFake()
@Mock
@@ -68,12 +73,10 @@
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>
- private lateinit var controller: QuickQSPanelController
+ private lateinit var controller: TestQuickQSPanelController
@Before
fun setUp() {
@@ -84,19 +87,21 @@
`when`(quickQSPanel.dumpableTag).thenReturn("")
`when`(quickQSPanel.resources).thenReturn(mContext.resources)
`when`(qsTileHost.createTileView(any(), any(), anyBoolean())).thenReturn(tileView)
+ `when`(mediaFlags.useMediaSessionLayout()).thenReturn(false)
- controller = QuickQSPanelController(
+ controller = TestQuickQSPanelController(
quickQSPanel,
qsTileHost,
qsCustomizerController,
false,
mediaHost,
+ true,
+ mediaFlags,
metricsLogger,
uiEventLogger,
qsLogger,
dumpManager,
- quickQsBrightnessController,
- footerActionsController
+ quickQsBrightnessController
)
controller.init()
@@ -128,14 +133,57 @@
}
@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())
+ }
+
+ @Test
+ fun testMediaExpansionUpdatedWhenConfigurationChanged() {
+ `when`(mediaFlags.useMediaSessionLayout()).thenReturn(true)
+
+ // 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(mediaHost).expansion = MediaHostState.EXPANDED
+
+ // Rotate device, verify media size updated
+ controller.setRotation(RotationUtils.ROTATION_LANDSCAPE)
+ captor.allValues.forEach { it.onConfigurationChange(Configuration.EMPTY) }
+
+ // times(2) because init will have set to collapsed because the flag was off
+ verify(mediaHost, times(2)).expansion = MediaHostState.COLLAPSED
+ }
+
+ class TestQuickQSPanelController(
+ view: QuickQSPanel,
+ qsTileHost: QSTileHost,
+ qsCustomizerController: QSCustomizerController,
+ usingMediaPlayer: Boolean,
+ mediaHost: MediaHost,
+ usingCollapsedLandscapeMedia: Boolean,
+ mediaFlags: MediaFlags,
+ metricsLogger: MetricsLogger,
+ uiEventLogger: UiEventLoggerFake,
+ qsLogger: QSLogger,
+ dumpManager: DumpManager,
+ quickQSBrightnessController: QuickQSBrightnessController
+ ) : QuickQSPanelController(view, qsTileHost, qsCustomizerController, usingMediaPlayer,
+ mediaHost, usingCollapsedLandscapeMedia, mediaFlags, metricsLogger, uiEventLogger, qsLogger,
+ dumpManager, quickQSBrightnessController) {
+
+ private var rotation = RotationUtils.ROTATION_NONE
+
+ @Override
+ override fun getRotation(): Int = rotation
+
+ fun setRotation(newRotation: Int) {
+ rotation = newRotation
+ }
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
index 97ad8bc..f3fcdbf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt
@@ -92,7 +92,6 @@
mContext.addMockSystemService("window", windowService)
mContext.setMockPackageManager(packageManager)
- `when`(tileHost.tileServices).thenReturn(tileServices)
`when`(tileHost.context).thenReturn(mContext)
`when`(tileServices.getTileWrapper(any(CustomTile::class.java)))
.thenReturn(tileServiceManager)
@@ -113,7 +112,8 @@
statusBarStateController,
activityStarter,
qsLogger,
- customTileStatePersister
+ customTileStatePersister,
+ tileServices
)
customTile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index f2303c2..b559d18 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -43,7 +43,6 @@
import android.os.UserHandle;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
-import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.test.suitebuilder.annotation.SmallTest;
@@ -96,11 +95,11 @@
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
- Mockito.mock(IQSService.class), new Tile(),
- mTileServiceIntent,
- mUser,
+ Mockito.mock(IQSService.class),
mMockPackageManagerAdapter,
- mMockBroadcastDispatcher);
+ mMockBroadcastDispatcher,
+ mTileServiceIntent,
+ mUser);
}
@After
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index d604b2c..e39d6a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -34,7 +34,6 @@
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
-import android.service.quicksettings.Tile;
import android.service.quicksettings.TileService;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
@@ -54,6 +53,7 @@
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.statusbar.phone.StatusBarIconController;
import com.android.systemui.statusbar.policy.BluetoothController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.util.settings.SecureSettings;
@@ -104,6 +104,12 @@
private TileServiceRequestController.Builder mTileServiceRequestControllerBuilder;
@Mock
private TileServiceRequestController mTileServiceRequestController;
+ @Mock
+ private KeyguardStateController mKeyguardStateController;
+ @Mock
+ private TileLifecycleManager.Factory mTileLifecycleManagerFactory;
+ @Mock
+ private TileLifecycleManager mTileLifecycleManager;
@Before
public void setUp() throws Exception {
@@ -113,6 +119,8 @@
when(mTileServiceRequestControllerBuilder.create(any()))
.thenReturn(mTileServiceRequestController);
+ when(mTileLifecycleManagerFactory.create(any(Intent.class), any(UserHandle.class)))
+ .thenReturn(mTileLifecycleManager);
QSTileHost host = new QSTileHost(mContext,
mStatusBarIconController,
@@ -130,14 +138,16 @@
mUserTracker,
mSecureSettings,
mock(CustomTileStatePersister.class),
- mTileServiceRequestControllerBuilder);
+ mTileServiceRequestControllerBuilder,
+ mTileLifecycleManagerFactory);
mTileService = new TestTileServices(host, Looper.getMainLooper(), mBroadcastDispatcher,
- mUserTracker);
+ mUserTracker, mKeyguardStateController);
}
@After
public void tearDown() throws Exception {
mTileService.getHost().destroy();
+ mTileService.destroy();
TestableLooper.get(this).processAllMessages();
}
@@ -217,13 +227,14 @@
private class TestTileServices extends TileServices {
TestTileServices(QSTileHost host, Looper looper,
- BroadcastDispatcher broadcastDispatcher, UserTracker userTracker) {
- super(host, looper, broadcastDispatcher, userTracker);
+ BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
+ KeyguardStateController keyguardStateController) {
+ super(host, looper, broadcastDispatcher, userTracker, keyguardStateController);
}
@Override
- protected TileServiceManager onCreateTileService(ComponentName component, Tile qsTile,
- BroadcastDispatcher broadcastDispatcher) {
+ protected TileServiceManager onCreateTileService(
+ ComponentName component, BroadcastDispatcher broadcastDispatcher) {
TileServiceManager manager = mock(TileServiceManager.class);
mManagers.add(manager);
when(manager.isLifecycleStarted()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
index d51d370..9076e16 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/LockscreenShadeTransitionControllerTest.kt
@@ -169,8 +169,6 @@
transitionController.goToLockedShade(null)
whenever(statusbarStateController.state).thenReturn(StatusBarState.SHADE)
transitionController.goToLockedShade(null)
- whenever(statusbarStateController.state).thenReturn(StatusBarState.FULLSCREEN_USER_SWITCHER)
- transitionController.goToLockedShade(null)
verify(statusbarStateController, never()).setState(anyInt())
}
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/StatusBarIconViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
index 85ea52b..d13451d 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarIconViewTest.java
@@ -16,6 +16,8 @@
package com.android.systemui.statusbar;
+import static com.google.common.truth.Truth.assertThat;
+
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNull;
@@ -37,6 +39,7 @@
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Icon;
import android.os.UserHandle;
import android.service.notification.StatusBarNotification;
@@ -130,7 +133,12 @@
Icon icon = Icon.createWithBitmap(largeBitmap);
StatusBarIcon largeIcon = new StatusBarIcon(UserHandle.ALL, "mockPackage",
icon, 0, 0, "");
- assertFalse(mIconView.set(largeIcon));
+ assertTrue(mIconView.set(largeIcon));
+
+ // The view should downscale the bitmap.
+ BitmapDrawable drawable = (BitmapDrawable) mIconView.getDrawable();
+ assertThat(drawable.getBitmap().getWidth()).isLessThan(1000);
+ assertThat(drawable.getBitmap().getHeight()).isLessThan(1000);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
index b736f38..a5ea897 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateControllerImplTest.kt
@@ -61,18 +61,16 @@
@Test
fun testChangeState_logged() {
TestableLooper.get(this).runWithLooper {
- controller.state = StatusBarState.FULLSCREEN_USER_SWITCHER
controller.state = StatusBarState.KEYGUARD
controller.state = StatusBarState.SHADE
controller.state = StatusBarState.SHADE_LOCKED
}
val logs = uiEventLogger.logs
- assertEquals(4, logs.size)
+ assertEquals(3, logs.size)
val ids = logs.map(UiEventLoggerFake.FakeUiEvent::eventId)
- assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER.id, ids[0])
- assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[1])
- assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[2])
- assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[3])
+ assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD.id, ids[0])
+ assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE.id, ids[1])
+ assertEquals(StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED.id, ids[2])
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
index b5b2f1f..79a2008 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/StatusBarStateEventTest.kt
@@ -33,18 +33,16 @@
StatusBarStateEvent.STATUS_BAR_STATE_SHADE,
StatusBarStateEvent.STATUS_BAR_STATE_SHADE_LOCKED,
StatusBarStateEvent.STATUS_BAR_STATE_KEYGUARD,
- StatusBarStateEvent.STATUS_BAR_STATE_FULLSCREEN_USER_SWITCHER,
StatusBarStateEvent.STATUS_BAR_STATE_UNKNOWN
)
val states = listOf(
StatusBarState.SHADE,
StatusBarState.SHADE_LOCKED,
StatusBarState.KEYGUARD,
- StatusBarState.FULLSCREEN_USER_SWITCHER,
-1
)
events.zip(states).forEach { (event, state) ->
assertEquals(event, StatusBarStateEvent.fromState(state))
}
}
-}
\ No newline at end of file
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
new file mode 100644
index 0000000..3820b98
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/NotifPipelineChoreographerTest.kt
@@ -0,0 +1,100 @@
+/*
+ * 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.statusbar.notification.collection
+
+import android.testing.AndroidTestingRunner
+import android.view.Choreographer
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.mockito.any
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.withArgCaptor
+import org.junit.Assert.assertTrue
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.anyLong
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when` as whenever
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class NotifPipelineChoreographerTest : SysuiTestCase() {
+
+ val viewChoreographer: Choreographer = mock()
+ val timeoueSubscription: Runnable = mock()
+ val executor: DelayableExecutor = mock<DelayableExecutor>().also {
+ whenever(it.executeDelayed(any(), anyLong())).thenReturn(timeoueSubscription)
+ }
+
+ val pipelineChoreographer: NotifPipelineChoreographer = NotifPipelineChoreographerModule
+ .provideChoreographer(viewChoreographer, executor)
+
+ @Test
+ fun scheduleThenEvalFrameCallback() {
+ // GIVEN a registered eval listener and scheduled choreographer
+ var hasEvaluated = false
+ pipelineChoreographer.addOnEvalListener {
+ hasEvaluated = true
+ }
+ pipelineChoreographer.schedule()
+ val frameCallback: Choreographer.FrameCallback = withArgCaptor {
+ verify(viewChoreographer).postFrameCallback(capture())
+ }
+ // WHEN the choreographer would invoke its callback
+ frameCallback.doFrame(0)
+ // THEN the choreographer would evaluate, and the timeoutSubscription would have been
+ // cancelled
+ assertTrue(hasEvaluated)
+ verify(timeoueSubscription).run()
+ }
+
+ @Test
+ fun scheduleThenEvalTimeoutCallback() {
+ // GIVEN a registered eval listener and scheduled choreographer
+ var hasEvaluated = false
+ pipelineChoreographer.addOnEvalListener {
+ hasEvaluated = true
+ }
+ pipelineChoreographer.schedule()
+ val frameCallback: Choreographer.FrameCallback = withArgCaptor {
+ verify(viewChoreographer).postFrameCallback(capture())
+ }
+ val runnable: Runnable = withArgCaptor {
+ verify(executor).executeDelayed(capture(), anyLong())
+ }
+ // WHEN the executor would invoke its callback (indicating a timeout)
+ runnable.run()
+ // THEN the choreographer would evaluate, and the FrameCallback would have been unregistered
+ assertTrue(hasEvaluated)
+ verify(viewChoreographer).removeFrameCallback(frameCallback)
+ }
+
+ @Test
+ fun scheduleThenCancel() {
+ // GIVEN a scheduled choreographer
+ pipelineChoreographer.schedule()
+ val frameCallback: Choreographer.FrameCallback = withArgCaptor {
+ verify(viewChoreographer).postFrameCallback(capture())
+ }
+ // WHEN the scheduled run is cancelled
+ pipelineChoreographer.cancel()
+ // THEN both the FrameCallback is unregistered and the timeout subscription is cancelled.
+ verify(viewChoreographer).removeFrameCallback(frameCallback)
+ verify(timeoueSubscription).run()
+ }
+}
\ No newline at end of file
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..f470715 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;
@@ -109,6 +111,8 @@
@Captor private ArgumentCaptor<CollectionReadyForBuildListener> mBuildListenerCaptor;
+ private final FakeNotifPipelineChoreographer mPipelineChoreographer =
+ new FakeNotifPipelineChoreographer();
private CollectionReadyForBuildListener mReadyForBuildListener;
private List<NotificationEntryBuilder> mPendingSet = new ArrayList<>();
private List<NotificationEntry> mEntrySet = new ArrayList<>();
@@ -125,11 +129,12 @@
allowTestableLooperAsMainThread();
mListBuilder = new ShadeListBuilder(
- mSystemClock,
- mNotifPipelineFlags,
- mLogger,
mDumpManager,
- mInteractionTracker
+ mPipelineChoreographer,
+ mNotifPipelineFlags,
+ mInteractionTracker,
+ mLogger,
+ mSystemClock
);
mListBuilder.setOnRenderListListener(mOnRenderListListener);
@@ -565,6 +570,7 @@
// WHEN the pipeline is kicked off
mReadyForBuildListener.onBuildList(singletonList(entry));
+ mPipelineChoreographer.runIfScheduled();
// THEN the entry's initialization time is reset
assertFalse(entry.hasFinishedInitialization());
@@ -872,6 +878,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 +1007,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") {};
@@ -954,22 +1028,38 @@
clearInvocations(mOnRenderListListener);
packageFilter.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
idPromoter.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
section.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
hypeComparator.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
+ verify(mOnRenderListListener).onRenderList(anyList());
+
+ clearInvocations(mOnRenderListListener);
+ sectionComparator.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
clearInvocations(mOnRenderListListener);
preRenderInvalidator.invalidateList();
+ assertTrue(mPipelineChoreographer.isScheduled());
+ mPipelineChoreographer.runIfScheduled();
verify(mOnRenderListListener).onRenderList(anyList());
}
@@ -1441,6 +1531,7 @@
// WHEN visual stability manager allows group changes again
mStabilityManager.setAllowGroupChanges(true);
mStabilityManager.invalidateList();
+ mPipelineChoreographer.runIfScheduled();
// THEN entries are grouped
verifyBuiltList(
@@ -1479,6 +1570,7 @@
// WHEN section changes are allowed again
mStabilityManager.setAllowSectionChanges(true);
mStabilityManager.invalidateList();
+ mPipelineChoreographer.runIfScheduled();
// THEN the section updates
assertEquals(newSectioner, mEntrySet.get(0).getSection().getSectioner());
@@ -1699,6 +1791,30 @@
}
@Test
+ public void testMultipleInvalidationsCoalesce() {
+ // GIVEN a PreGroupFilter and a FinalizeFilter
+ NotifFilter filter1 = new PackageFilter(PACKAGE_5);
+ NotifFilter filter2 = new PackageFilter(PACKAGE_0);
+ mListBuilder.addPreGroupFilter(filter1);
+ mListBuilder.addFinalizeFilter(filter2);
+
+ // WHEN both filters invalidate
+ filter1.invalidateList();
+ filter2.invalidateList();
+
+ // THEN the pipeline choreographer is scheduled to evaluate, AND the pipeline hasn't
+ // actually run.
+ assertTrue(mPipelineChoreographer.isScheduled());
+ verify(mOnRenderListListener, never()).onRenderList(anyList());
+
+ // WHEN the pipeline choreographer actually runs
+ mPipelineChoreographer.runIfScheduled();
+
+ // THEN the pipeline runs
+ verify(mOnRenderListListener).onRenderList(anyList());
+ }
+
+ @Test
public void testIsSorted() {
Comparator<Integer> intCmp = Integer::compare;
assertTrue(ShadeListBuilder.isSorted(Collections.emptyList(), intCmp));
@@ -1840,6 +1956,7 @@
}
mReadyForBuildListener.onBuildList(mEntrySet);
+ mPipelineChoreographer.runIfScheduled();
}
private void verifyBuiltList(ExpectedEntry ...expectedEntries) {
@@ -2037,16 +2154,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 +2288,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/row/NotificationEntryManagerInflationTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
index a890414..52189e4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationEntryManagerInflationTest.java
@@ -48,6 +48,7 @@
import com.android.systemui.classifier.FalsingCollectorFake;
import com.android.systemui.classifier.FalsingManagerFake;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.media.MediaFeatureFlag;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
@@ -273,6 +274,7 @@
null,
new FalsingManagerFake(),
new FalsingCollectorFake(),
+ mock(FeatureFlags.class),
mPeopleNotificationIdentifier,
Optional.of(mock(BubblesManager.class)),
mock(ExpandableNotificationRowDragController.class)
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/notification/stack/NotificationSwipeHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
index a0e91fc..1305d79 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationSwipeHelperTest.java
@@ -42,6 +42,7 @@
import com.android.systemui.SwipeHelper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.classifier.FalsingManagerFake;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.plugins.statusbar.NotificationMenuRowPlugin;
import com.android.systemui.plugins.statusbar.NotificationSwipeActionHelper.SnoozeOption;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
@@ -71,6 +72,7 @@
private Handler mHandler;
private ExpandableNotificationRow mNotificationRow;
private Runnable mFalsingCheck;
+ private FeatureFlags mFeatureFlags;
@Rule public MockitoRule mockito = MockitoJUnit.rule();
@@ -78,9 +80,10 @@
public void setUp() throws Exception {
mCallback = mock(NotificationSwipeHelper.NotificationCallback.class);
mListener = mock(NotificationMenuRowPlugin.OnMenuEventListener.class);
+ mFeatureFlags = mock(FeatureFlags.class);
mSwipeHelper = spy(new NotificationSwipeHelper(
mContext.getResources(), ViewConfiguration.get(mContext),
- new FalsingManagerFake(), SwipeHelper.X, mCallback, mListener));
+ new FalsingManagerFake(), mFeatureFlags, SwipeHelper.X, mCallback, mListener));
mView = mock(View.class);
mEvent = mock(MotionEvent.class);
mMenuRow = mock(NotificationMenuRowPlugin.class);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 8c7d22d..fb232ba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -38,6 +38,7 @@
import android.testing.TestableResources;
import com.android.internal.logging.MetricsLogger;
+import com.android.internal.util.LatencyTracker;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.AuthController;
@@ -110,6 +111,8 @@
private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController;
@Mock
private SessionTracker mSessionTracker;
+ @Mock
+ private LatencyTracker mLatencyTracker;
private BiometricUnlockController mBiometricUnlockController;
@Before
@@ -133,7 +136,7 @@
mMetricsLogger, mDumpManager, mPowerManager,
mNotificationMediaManager, mWakefulnessLifecycle, mScreenLifecycle,
mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
- mSessionTracker);
+ mSessionTracker, mLatencyTracker);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
index 01e9822e..7de3545 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewControllerTest.java
@@ -47,6 +47,9 @@
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.events.SystemStatusAnimationScheduler;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserInfoTracker;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController;
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherFeatureController;
import com.android.systemui.statusbar.policy.BatteryController;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
@@ -99,6 +102,12 @@
private ArgumentCaptor<ConfigurationListener> mConfigurationListenerCaptor;
@Captor
private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardCallbackCaptor;
+ @Mock
+ private StatusBarUserSwitcherFeatureController mStatusBarUserSwitcherFeatureController;
+ @Mock
+ private StatusBarUserSwitcherController mStatusBarUserSwitcherController;
+ @Mock
+ private StatusBarUserInfoTracker mStatusBarUserInfoTracker;
private TestNotificationPanelViewStateProvider mNotificationPanelViewStateProvider;
private KeyguardStatusBarView mKeyguardStatusBarView;
@@ -117,7 +126,11 @@
.inflate(R.layout.keyguard_status_bar, null));
});
- mController = new KeyguardStatusBarViewController(
+ mController = createController();
+ }
+
+ private KeyguardStatusBarViewController createController() {
+ return new KeyguardStatusBarViewController(
mKeyguardStatusBarView,
mCarrierTextController,
mConfigurationController,
@@ -134,7 +147,10 @@
mBiometricUnlockController,
mStatusBarStateController,
mStatusBarContentInsetsProvider,
- mUserManager
+ mUserManager,
+ mStatusBarUserSwitcherFeatureController,
+ mStatusBarUserSwitcherController,
+ mStatusBarUserInfoTracker
);
}
@@ -356,6 +372,32 @@
assertThat(mKeyguardStatusBarView.getVisibility()).isEqualTo(View.VISIBLE);
}
+ @Test
+ public void testNewUserSwitcherDisablesAvatar_newUiOn() {
+ // GIVEN the status bar user switcher chip is enabled
+ when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
+ .thenReturn(true);
+
+ // WHEN the controller is created
+ mController = createController();
+
+ // THEN keyguard status bar view avatar is disabled
+ assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isFalse();
+ }
+
+ @Test
+ public void testNewUserSwitcherDisablesAvatar_newUiOff() {
+ // GIVEN the status bar user switcher chip is disabled
+ when(mStatusBarUserSwitcherFeatureController.isStatusBarUserSwitcherFeatureEnabled())
+ .thenReturn(false);
+
+ // WHEN the controller is created
+ mController = createController();
+
+ // THEN keyguard status bar view avatar is enabled
+ assertThat(mKeyguardStatusBarView.isKeyguardUserAvatarEnabled()).isTrue();
+ }
+
private void updateStateToNotKeyguard() {
updateStatusBarState(SHADE);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
index dee88db..7347565 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/NotificationPanelViewControllerTest.java
@@ -37,7 +37,6 @@
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -99,7 +98,6 @@
import com.android.systemui.doze.DozeLog;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
-import com.android.systemui.flags.Flags;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentService;
import com.android.systemui.idle.IdleHostViewController;
@@ -394,7 +392,7 @@
mConfiguration.orientation = ORIENTATION_PORTRAIT;
when(mResources.getDisplayMetrics()).thenReturn(mDisplayMetrics);
mDisplayMetrics.density = 100;
- when(mFeatureFlags.isEnabled(Flags.NOTIFICATION_SHADE_DRAG)).thenReturn(true);
+ when(mResources.getBoolean(R.bool.config_enableNotificationShadeDrag)).thenReturn(true);
when(mResources.getDimensionPixelSize(R.dimen.notifications_top_padding_split_shade))
.thenReturn(NOTIFICATION_SCRIM_TOP_PADDING_IN_SPLIT_SHADE);
when(mResources.getDimensionPixelSize(R.dimen.qs_panel_width)).thenReturn(400);
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/phone/PhoneStatusBarViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
index c65a6b6..5891161 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/PhoneStatusBarViewControllerTest.kt
@@ -26,6 +26,7 @@
import androidx.test.platform.app.InstrumentationRegistry
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.phone.userswitcher.StatusBarUserSwitcherController
import com.android.systemui.statusbar.policy.ConfigurationController
import com.android.systemui.unfold.SysUIUnfoldComponent
import com.android.systemui.unfold.config.UnfoldTransitionConfig
@@ -60,6 +61,8 @@
private lateinit var progressProvider: ScopedUnfoldTransitionProgressProvider
@Mock
private lateinit var configurationController: ConfigurationController
+ @Mock
+ private lateinit var userSwitcherController: StatusBarUserSwitcherController
private lateinit var view: PhoneStatusBarView
private lateinit var controller: PhoneStatusBarViewController
@@ -187,6 +190,7 @@
return PhoneStatusBarViewController.Factory(
Optional.of(sysuiUnfoldComponent),
Optional.of(progressProvider),
+ userSwitcherController,
configurationController
).create(view, touchEventHandler).also {
it.init()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index bb79941..107ba81 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -36,6 +36,7 @@
import androidx.test.filters.SmallTest;
+import com.android.internal.util.LatencyTracker;
import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
@@ -100,6 +101,8 @@
private ShadeController mShadeController;
@Mock
private DreamOverlayStateController mDreamOverlayStateController;
+ @Mock
+ private LatencyTracker mLatencyTracker;
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
@@ -127,7 +130,8 @@
mock(NotificationMediaManager.class),
mKeyguardBouncerFactory,
mKeyguardMessageAreaFactory,
- () -> mShadeController);
+ () -> mShadeController,
+ mLatencyTracker);
mStatusBarKeyguardViewManager.registerStatusBar(
mStatusBar,
mNotificationPanelView,
@@ -171,17 +175,6 @@
}
@Test
- public void onPanelExpansionChanged_neverHidesFullscreenBouncer() {
- // TODO: StatusBar should not be here, mBouncer.isFullscreenBouncer() should do the same.
- when(mStatusBar.isFullScreenUserSwitcherState()).thenReturn(true);
- mStatusBarKeyguardViewManager.onPanelExpansionChanged(
- /* fraction= */ 0.5f,
- /* expanded= */ false,
- /* tracking= */ true);
- verify(mBouncer).setExpansion(eq(KeyguardBouncer.EXPANSION_VISIBLE));
- }
-
- @Test
public void onPanelExpansionChanged_neverHidesScrimmedBouncer() {
when(mBouncer.isShowing()).thenReturn(true);
when(mBouncer.isScrimmed()).thenReturn(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 1564dfe..90b93e7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -885,15 +885,6 @@
}
@Test
- public void testSetState_changesIsFullScreenUserSwitcherState() {
- mStatusBar.setBarStateForTest(StatusBarState.KEYGUARD);
- assertFalse(mStatusBar.isFullScreenUserSwitcherState());
-
- mStatusBar.setBarStateForTest(StatusBarState.FULLSCREEN_USER_SWITCHER);
- assertTrue(mStatusBar.isFullScreenUserSwitcherState());
- }
-
- @Test
public void testShowKeyguardImplementation_setsState() {
when(mLockscreenUserManager.getCurrentProfiles()).thenReturn(new SparseArray<>());
@@ -903,12 +894,6 @@
mStatusBar.showKeyguardImpl();
verify(mStatusBarStateController).setState(
eq(StatusBarState.KEYGUARD), eq(false) /* force */);
-
- // If useFullscreenUserSwitcher is true, state is set to FULLSCREEN_USER_SWITCHER.
- when(mUserSwitcherController.useFullscreenUserSwitcher()).thenReturn(true);
- mStatusBar.showKeyguardImpl();
- verify(mStatusBarStateController).setState(
- eq(StatusBarState.FULLSCREEN_USER_SWITCHER), eq(false) /* force */);
}
@Test
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/util/drawable/DrawableSizeTest.kt b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
new file mode 100644
index 0000000..ac357ea
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/util/drawable/DrawableSizeTest.kt
@@ -0,0 +1,73 @@
+package com.android.systemui.util.drawable
+
+import android.content.res.Resources
+import android.graphics.Bitmap
+import android.graphics.drawable.BitmapDrawable
+import android.graphics.drawable.ShapeDrawable
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.google.common.truth.Truth.assertThat
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+class DrawableSizeTest : SysuiTestCase() {
+
+ lateinit var resources: Resources
+
+ @Before
+ fun setUp() {
+ resources = context.resources
+ }
+
+ @Test
+ fun testDownscaleToSize_drawableZeroSize_unchanged() {
+ val drawable = ShapeDrawable()
+ val result = DrawableSize.downscaleToSize(resources, drawable, 100, 100)
+ assertThat(result).isSameInstanceAs(drawable)
+ }
+
+ @Test
+ fun testDownscaleToSize_drawableSmallerThanRequirement_unchanged() {
+ val drawable = BitmapDrawable(resources,
+ Bitmap.createBitmap(
+ resources.displayMetrics,
+ 150,
+ 150,
+ Bitmap.Config.ARGB_8888
+ )
+ )
+ val result = DrawableSize.downscaleToSize(resources, drawable, 300, 300)
+ assertThat(result).isSameInstanceAs(drawable)
+ }
+
+ @Test
+ fun testDownscaleToSize_drawableLargerThanRequirementWithDensity_resized() {
+ // This bitmap would actually fail to resize if the method doesn't check for
+ // bitmap dimensions inside drawable.
+ val drawable = BitmapDrawable(resources,
+ Bitmap.createBitmap(
+ resources.displayMetrics,
+ 150,
+ 75,
+ Bitmap.Config.ARGB_8888
+ )
+ )
+
+ val result = DrawableSize.downscaleToSize(resources, drawable, 75, 75)
+ assertThat(result).isNotSameInstanceAs(drawable)
+ assertThat(result.intrinsicWidth).isEqualTo(75)
+ assertThat(result.intrinsicHeight).isEqualTo(37)
+ }
+
+ @Test
+ fun testDownscaleToSize_drawableAnimated_unchanged() {
+ val drawable = resources.getDrawable(android.R.drawable.stat_sys_download,
+ resources.newTheme())
+ val result = DrawableSize.downscaleToSize(resources, drawable, 1, 1)
+ assertThat(result).isSameInstanceAs(drawable)
+ }
+}
\ No newline at end of file
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..ca37a40 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/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 6938e25..c9903ea 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -43,6 +43,7 @@
import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IPreviewImageProcessorImpl;
+import android.hardware.camera2.extension.IProcessResultImpl;
import android.hardware.camera2.extension.IRequestCallback;
import android.hardware.camera2.extension.IRequestProcessorImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
@@ -90,6 +91,7 @@
import androidx.camera.extensions.impl.PreviewExtenderImpl;
import androidx.camera.extensions.impl.PreviewExtenderImpl.ProcessorType;
import androidx.camera.extensions.impl.PreviewImageProcessorImpl;
+import androidx.camera.extensions.impl.ProcessResultImpl;
import androidx.camera.extensions.impl.RequestUpdateProcessorImpl;
import androidx.camera.extensions.impl.advanced.AdvancedExtenderImpl;
import androidx.camera.extensions.impl.advanced.AutoAdvancedExtenderImpl;
@@ -124,14 +126,17 @@
private static final String LATEST_VERSION = "1.2.0";
private static final String NON_INIT_VERSION_PREFIX = "1.0";
private static final String ADVANCED_VERSION_PREFIX = "1.2";
- private static final String[] SUPPORTED_VERSION_PREFIXES = {ADVANCED_VERSION_PREFIX,
- "1.1", NON_INIT_VERSION_PREFIX};
+ private static final String RESULTS_VERSION_PREFIX = "1.3";
+ private static final String[] SUPPORTED_VERSION_PREFIXES = {RESULTS_VERSION_PREFIX,
+ ADVANCED_VERSION_PREFIX, "1.1", NON_INIT_VERSION_PREFIX};
private static final boolean EXTENSIONS_PRESENT = checkForExtensions();
private static final String EXTENSIONS_VERSION = EXTENSIONS_PRESENT ?
(new ExtensionVersionImpl()).checkApiVersion(LATEST_VERSION) : null;
private static final boolean ADVANCED_API_SUPPORTED = checkForAdvancedAPI();
private static final boolean INIT_API_SUPPORTED = EXTENSIONS_PRESENT &&
(!EXTENSIONS_VERSION.startsWith(NON_INIT_VERSION_PREFIX));
+ private static final boolean RESULT_API_SUPPORTED = EXTENSIONS_PRESENT &&
+ (EXTENSIONS_VERSION.startsWith(RESULTS_VERSION_PREFIX));
private HashMap<String, CameraCharacteristics> mCharacteristicsHashMap = new HashMap<>();
private HashMap<String, Long> mMetadataVendorIdMap = new HashMap<>();
@@ -1242,7 +1247,7 @@
}
if (processor != null) {
- return new PreviewImageProcessorImplStub(processor);
+ return new PreviewImageProcessorImplStub(processor, mCameraId);
}
return null;
@@ -1332,7 +1337,7 @@
public ICaptureProcessorImpl getCaptureProcessor() {
CaptureProcessorImpl captureProcessor = mImageExtender.getCaptureProcessor();
if (captureProcessor != null) {
- return new CaptureProcessorImplStub(captureProcessor);
+ return new CaptureProcessorImplStub(captureProcessor, mCameraId);
}
return null;
@@ -1390,13 +1395,97 @@
return null;
}
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureRequestKeys() {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureRequest.Key> supportedCaptureKeys =
+ mImageExtender.getAvailableCaptureRequestKeys();
+
+ if ((supportedCaptureKeys != null) && !supportedCaptureKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
+ mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int requestKeyTags [] = new int[supportedCaptureKeys.size()];
+ int i = 0;
+ for (CaptureRequest.Key key : supportedCaptureKeys) {
+ requestKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_REQUEST_KEYS, requestKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public CameraMetadataNative getAvailableCaptureResultKeys() {
+ if (RESULT_API_SUPPORTED) {
+ List<CaptureResult.Key> supportedResultKeys =
+ mImageExtender.getAvailableCaptureResultKeys();
+
+ if ((supportedResultKeys != null) && !supportedResultKeys.isEmpty()) {
+ CameraMetadataNative ret = new CameraMetadataNative();
+ long vendorId = mMetadataVendorIdMap.containsKey(mCameraId) ?
+ mMetadataVendorIdMap.get(mCameraId) : Long.MAX_VALUE;
+ ret.setVendorId(vendorId);
+ int resultKeyTags [] = new int[supportedResultKeys.size()];
+ int i = 0;
+ for (CaptureResult.Key key : supportedResultKeys) {
+ resultKeyTags[i++] = CameraMetadataNative.getTag(key.getName(), vendorId);
+ }
+ ret.set(CameraCharacteristics.REQUEST_AVAILABLE_RESULT_KEYS, resultKeyTags);
+
+ return ret;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private class ProcessResultCallback implements ProcessResultImpl {
+ private final IProcessResultImpl mProcessResult;
+ private final String mCameraId;
+
+ private ProcessResultCallback(IProcessResultImpl processResult, String cameraId) {
+ mProcessResult = processResult;
+ mCameraId = cameraId;
+ }
+
+ @Override
+ public void onCaptureCompleted(long shutterTimestamp,
+ List<Pair<CaptureResult.Key, Object>> result) {
+ if (result == null) {
+ Log.e(TAG, "Invalid capture result received!");
+ }
+
+ CameraMetadataNative captureResults = new CameraMetadataNative();
+ if (mMetadataVendorIdMap.containsKey(mCameraId)) {
+ captureResults.setVendorId(mMetadataVendorIdMap.get(mCameraId));
+ }
+ for (Pair<CaptureResult.Key, Object> pair : result) {
+ captureResults.set(pair.first, pair.second);
+ }
+
+ try {
+ mProcessResult.onCaptureCompleted(shutterTimestamp, captureResults);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote client doesn't respond to capture results!");
+ }
+ }
}
private class CaptureProcessorImplStub extends ICaptureProcessorImpl.Stub {
private final CaptureProcessorImpl mCaptureProcessor;
+ private final String mCameraId;
- public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor) {
+ public CaptureProcessorImplStub(CaptureProcessorImpl captureProcessor, String cameraId) {
mCaptureProcessor = captureProcessor;
+ mCameraId = cameraId;
}
@Override
@@ -1415,7 +1504,7 @@
}
@Override
- public void process(List<CaptureBundle> captureList) {
+ public void process(List<CaptureBundle> captureList, IProcessResultImpl resultCallback) {
HashMap<Integer, Pair<Image, TotalCaptureResult>> captureMap = new HashMap<>();
for (CaptureBundle captureBundle : captureList) {
captureMap.put(captureBundle.stage, new Pair<> (
@@ -1424,7 +1513,14 @@
captureBundle.sequenceId)));
}
if (!captureMap.isEmpty()) {
- mCaptureProcessor.process(captureMap);
+ if ((resultCallback != null) && (RESULT_API_SUPPORTED)) {
+ mCaptureProcessor.process(captureMap, new ProcessResultCallback(resultCallback,
+ mCameraId), null /*executor*/);
+ } else if (resultCallback == null) {
+ mCaptureProcessor.process(captureMap);
+ } else {
+ Log.e(TAG, "Process requests with capture results are not supported!");
+ }
} else {
Log.e(TAG, "Process request with absent capture stages!");
}
@@ -1433,9 +1529,11 @@
private class PreviewImageProcessorImplStub extends IPreviewImageProcessorImpl.Stub {
private final PreviewImageProcessorImpl mProcessor;
+ private final String mCameraId;
- public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor) {
+ public PreviewImageProcessorImplStub(PreviewImageProcessorImpl processor, String cameraId) {
mProcessor = processor;
+ mCameraId = cameraId;
}
@Override
@@ -1455,9 +1553,17 @@
@Override
public void process(android.hardware.camera2.extension.ParcelImage image,
- CameraMetadataNative result, int sequenceId) {
- mProcessor.process(new ExtensionImage(image),
- new TotalCaptureResult(result, sequenceId));
+ CameraMetadataNative result, int sequenceId, IProcessResultImpl resultCallback) {
+ if ((resultCallback != null) && RESULT_API_SUPPORTED) {
+ mProcessor.process(new ExtensionImage(image),
+ new TotalCaptureResult(result, sequenceId),
+ new ProcessResultCallback(resultCallback, mCameraId), null /*executor*/);
+ } else if (resultCallback == null) {
+ mProcessor.process(new ExtensionImage(image),
+ new TotalCaptureResult(result, sequenceId));
+ } else {
+
+ }
}
}
diff --git a/proto/src/camera.proto b/proto/src/camera.proto
index 0338b93..2d62f32 100644
--- a/proto/src/camera.proto
+++ b/proto/src/camera.proto
@@ -65,4 +65,6 @@
// The dynamic range profile of the stream
optional int32 dynamic_range_profile = 14;
+ // The stream use case
+ optional int32 stream_use_case = 15;
}
diff --git a/services/Android.bp b/services/Android.bp
index af70692..0189303 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -16,6 +16,13 @@
// "-Xep:AndroidFrameworkBinderIdentity:ERROR",
"-Xep:AndroidFrameworkCompatChange:ERROR",
// "-Xep:AndroidFrameworkUid:ERROR",
+ "-Xep:SelfEquals:ERROR",
+ "-Xep:NullTernary:ERROR",
+ "-Xep:TryFailThrowable:ERROR",
+ "-Xep:HashtableContains:ERROR",
+ "-Xep:FormatString:ERROR",
+ "-Xep:ArrayHashCode:ERROR",
+ "-Xep:SelfAssignment:ERROR",
// NOTE: only enable to generate local patchfiles
// "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster",
// "-XepPatchLocation:/tmp/refaster/",
@@ -102,6 +109,7 @@
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
+ ":services.wallpapereffectsgeneration-sources",
":services.wifi-sources",
],
visibility: ["//visibility:private"],
@@ -158,6 +166,7 @@
"services.usage",
"services.usb",
"services.voiceinteraction",
+ "services.wallpapereffectsgeneration",
"services.wifi",
"service-blobstore",
"service-jobscheduler",
@@ -181,10 +190,6 @@
name: "libandroid_servers",
defaults: ["libservices.core-libs"],
whole_static_libs: ["libservices.core"],
- required: [
- // TODO: remove after NetworkStatsService is moved to the mainline module.
- "libcom_android_net_module_util_jni",
- ],
}
platform_compat_config {
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/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
index 6744ea8..803177b 100644
--- a/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
+++ b/services/accessibility/java/com/android/server/accessibility/SystemActionPerformer.java
@@ -295,6 +295,21 @@
case AccessibilityService.GLOBAL_ACTION_KEYCODE_HEADSETHOOK :
sendDownAndUpKeyEvents(KeyEvent.KEYCODE_HEADSETHOOK);
return true;
+ case AccessibilityService.GLOBAL_ACTION_DPAD_UP:
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_UP);
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_DPAD_DOWN:
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_DOWN);
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_DPAD_LEFT:
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_LEFT);
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_DPAD_RIGHT:
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_RIGHT);
+ return true;
+ case AccessibilityService.GLOBAL_ACTION_DPAD_CENTER:
+ sendDownAndUpKeyEvents(KeyEvent.KEYCODE_DPAD_CENTER);
+ return true;
default:
Slog.e(TAG, "Invalid action id: " + actionId);
return false;
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 051281c..fc95cdd 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -1614,8 +1614,8 @@
@NonNull IBinder appCallback, @NonNull IResultReceiver receiver)
throws RemoteException {
final int userId = UserHandle.getCallingUserId();
- activityToken = Objects.requireNonNull(activityToken, "activityToken");
- appCallback = Objects.requireNonNull(appCallback, "appCallback");
+ Objects.requireNonNull(activityToken, "activityToken");
+ Objects.requireNonNull(appCallback, "appCallback");
boolean restored = false;
synchronized (mLock) {
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/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index c3ab2a7..cef0e83 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -35,10 +35,10 @@
import static com.android.server.companion.AssociationStore.CHANGE_TYPE_UPDATED_ADDRESS_UNCHANGED;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.checkCallerCanManageCompanionDevice;
-import static com.android.server.companion.PermissionsUtils.enforceCallerCanInteractWithUserId;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
import static com.android.server.companion.PermissionsUtils.enforceCallerCanManageCompanionDevice;
import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOr;
+import static com.android.server.companion.PermissionsUtils.enforceCallerIsSystemOrCanInteractWithUserId;
import static com.android.server.companion.RolesUtils.addRoleHolderForAssociation;
import static com.android.server.companion.RolesUtils.removeRoleHolderForAssociation;
@@ -459,7 +459,7 @@
@Override
public List<AssociationInfo> getAllAssociationsForUser(int userId) throws RemoteException {
- enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId);
enforceCallerCanManageCompanionDevice(getContext(), "getAllAssociationsForUser");
return mAssociationStore.getAssociationsForUser(userId);
@@ -468,7 +468,7 @@
@Override
public void addOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
int userId) {
- enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId);
enforceCallerCanManageCompanionDevice(getContext(),
"addOnAssociationsChangedListener");
@@ -478,7 +478,7 @@
@Override
public void removeOnAssociationsChangedListener(IOnAssociationsChangedListener listener,
int userId) {
- enforceCallerCanInteractWithUserId(getContext(), userId);
+ enforceCallerIsSystemOrCanInteractWithUserId(getContext(), userId);
enforceCallerCanManageCompanionDevice(
getContext(), "removeOnAssociationsChangedListener");
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/PermissionsUtils.java b/services/companion/java/com/android/server/companion/PermissionsUtils.java
index 7ebe33e..0e593e1 100644
--- a/services/companion/java/com/android/server/companion/PermissionsUtils.java
+++ b/services/companion/java/com/android/server/companion/PermissionsUtils.java
@@ -114,6 +114,12 @@
context.enforceCallingPermission(INTERACT_ACROSS_USERS, null);
}
+ static void enforceCallerIsSystemOrCanInteractWithUserId(@NonNull Context context, int userId) {
+ if (getCallingUid() == SYSTEM_UID) return;
+
+ enforceCallerCanInteractWithUserId(context, userId);
+ }
+
static boolean checkCallerIsSystemOr(@UserIdInt int userId, @NonNull String packageName) {
final int callingUid = getCallingUid();
if (callingUid == SYSTEM_UID) return true;
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/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
index bb49ba0..75acf81 100644
--- a/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
+++ b/services/companion/java/com/android/server/companion/virtual/GenericWindowPolicyController.java
@@ -39,7 +39,6 @@
import java.util.List;
import java.util.Set;
-import java.util.function.Consumer;
/**
@@ -62,7 +61,6 @@
private final ArraySet<ComponentName> mAllowedActivities;
@Nullable
private final ArraySet<ComponentName> mBlockedActivities;
- private Consumer<ActivityInfo> mActivityBlockedCallback;
@NonNull
final ArraySet<Integer> mRunningUids = new ArraySet<>();
@@ -83,12 +81,10 @@
@NonNull ArraySet<UserHandle> allowedUsers,
@Nullable Set<ComponentName> allowedActivities,
@Nullable Set<ComponentName> blockedActivities,
- @NonNull ActivityListener activityListener,
- @NonNull Consumer<ActivityInfo> activityBlockedCallback) {
+ @NonNull ActivityListener activityListener) {
mAllowedUsers = allowedUsers;
mAllowedActivities = allowedActivities == null ? null : new ArraySet<>(allowedActivities);
mBlockedActivities = blockedActivities == null ? null : new ArraySet<>(blockedActivities);
- mActivityBlockedCallback = activityBlockedCallback;
setInterestedWindowFlags(windowFlags, systemWindowFlags);
mActivityListener = activityListener;
}
@@ -100,7 +96,6 @@
for (int i = 0; i < activityCount; i++) {
final ActivityInfo aInfo = activities.get(i);
if (!canContainActivity(aInfo, /* windowFlags= */ 0, /* systemWindowFlags= */ 0)) {
- mActivityBlockedCallback.accept(aInfo);
return false;
}
}
@@ -110,11 +105,7 @@
@Override
public boolean keepActivityOnWindowFlagsChanged(ActivityInfo activityInfo, int windowFlags,
int systemWindowFlags) {
- if (!canContainActivity(activityInfo, windowFlags, systemWindowFlags)) {
- mActivityBlockedCallback.accept(activityInfo);
- return false;
- }
- return true;
+ return canContainActivity(activityInfo, windowFlags, systemWindowFlags);
}
@Override
diff --git a/services/companion/java/com/android/server/companion/virtual/InputController.java b/services/companion/java/com/android/server/companion/virtual/InputController.java
index e6bfd1f..9d4b50b 100644
--- a/services/companion/java/com/android/server/companion/virtual/InputController.java
+++ b/services/companion/java/com/android/server/companion/virtual/InputController.java
@@ -30,7 +30,6 @@
import android.hardware.input.VirtualMouseScrollEvent;
import android.hardware.input.VirtualTouchEvent;
import android.os.IBinder;
-import android.os.IInputConstants;
import android.os.RemoteException;
import android.util.ArrayMap;
import android.util.Slog;
@@ -43,6 +42,7 @@
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
@@ -76,13 +76,6 @@
private final DisplayManagerInternal mDisplayManagerInternal;
private final InputManagerInternal mInputManagerInternal;
- /**
- * Because the pointer is a singleton, it can only be targeted at one display at a time. Because
- * multiple mice could be concurrently registered, mice that are associated with a different
- * display than the current target display should not be allowed to affect the current target.
- */
- @VisibleForTesting int mActivePointerDisplayId;
-
InputController(@NonNull Object lock) {
this(lock, new NativeWrapper());
}
@@ -91,18 +84,21 @@
InputController(@NonNull Object lock, @NonNull NativeWrapper nativeWrapper) {
mLock = lock;
mNativeWrapper = nativeWrapper;
- mActivePointerDisplayId = Display.INVALID_DISPLAY;
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
mInputManagerInternal = LocalServices.getService(InputManagerInternal.class);
}
void close() {
synchronized (mLock) {
- for (InputDeviceDescriptor inputDeviceDescriptor : mInputDeviceDescriptors.values()) {
- mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+ final Iterator<Map.Entry<IBinder, InputDeviceDescriptor>> iterator =
+ mInputDeviceDescriptors.entrySet().iterator();
+ if (iterator.hasNext()) {
+ final Map.Entry<IBinder, InputDeviceDescriptor> entry = iterator.next();
+ final IBinder token = entry.getKey();
+ final InputDeviceDescriptor inputDeviceDescriptor = entry.getValue();
+ iterator.remove();
+ closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
- mInputDeviceDescriptors.clear();
- resetMouseValuesLocked();
}
}
@@ -150,8 +146,6 @@
new InputDeviceDescriptor(fd, binderDeathRecipient,
InputDeviceDescriptor.TYPE_MOUSE, displayId, phys));
mInputManagerInternal.setVirtualMousePointerDisplayId(displayId);
- mInputManagerInternal.setPointerAcceleration(1);
- mActivePointerDisplayId = displayId;
}
try {
deviceToken.linkToDeath(binderDeathRecipient, /* flags= */ 0);
@@ -197,23 +191,44 @@
throw new IllegalArgumentException(
"Could not unregister input device for given token");
}
- token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
- mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
- InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
-
- // Reset values to the default if all virtual mice are unregistered, or set display
- // id if there's another mouse (choose the most recent).
- if (inputDeviceDescriptor.isMouse()) {
- updateMouseValuesLocked();
- }
+ closeInputDeviceDescriptorLocked(token, inputDeviceDescriptor);
}
}
@GuardedBy("mLock")
- private void updateMouseValuesLocked() {
+ private void closeInputDeviceDescriptorLocked(IBinder token,
+ InputDeviceDescriptor inputDeviceDescriptor) {
+ token.unlinkToDeath(inputDeviceDescriptor.getDeathRecipient(), /* flags= */ 0);
+ mNativeWrapper.closeUinput(inputDeviceDescriptor.getFileDescriptor());
+ InputManager.getInstance().removeUniqueIdAssociation(inputDeviceDescriptor.getPhys());
+
+ // Reset values to the default if all virtual mice are unregistered, or set display
+ // id if there's another mouse (choose the most recent). The inputDeviceDescriptor must be
+ // removed from the mInputDeviceDescriptors instance variable prior to this point.
+ if (inputDeviceDescriptor.isMouse()) {
+ if (mInputManagerInternal.getVirtualMousePointerDisplayId()
+ == inputDeviceDescriptor.getDisplayId()) {
+ updateActivePointerDisplayIdLocked();
+ }
+ }
+ }
+
+ void setShowPointerIcon(boolean visible, int displayId) {
+ mInputManagerInternal.setPointerIconVisible(visible, displayId);
+ }
+
+ void setPointerAcceleration(float pointerAcceleration, int displayId) {
+ mInputManagerInternal.setPointerAcceleration(pointerAcceleration, displayId);
+ }
+
+ void setDisplayEligibilityForPointerCapture(boolean isEligible, int displayId) {
+ mInputManagerInternal.setDisplayEligibilityForPointerCapture(displayId, isEligible);
+ }
+
+ @GuardedBy("mLock")
+ private void updateActivePointerDisplayIdLocked() {
InputDeviceDescriptor mostRecentlyCreatedMouse = null;
- for (InputDeviceDescriptor otherInputDeviceDescriptor :
- mInputDeviceDescriptors.values()) {
+ for (InputDeviceDescriptor otherInputDeviceDescriptor : mInputDeviceDescriptors.values()) {
if (otherInputDeviceDescriptor.isMouse()) {
if (mostRecentlyCreatedMouse == null
|| (otherInputDeviceDescriptor.getCreationOrderNumber()
@@ -225,20 +240,12 @@
if (mostRecentlyCreatedMouse != null) {
mInputManagerInternal.setVirtualMousePointerDisplayId(
mostRecentlyCreatedMouse.getDisplayId());
- mActivePointerDisplayId = mostRecentlyCreatedMouse.getDisplayId();
} else {
- // All mice have been unregistered; reset all values.
- resetMouseValuesLocked();
+ // All mice have been unregistered
+ mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
}
}
- private void resetMouseValuesLocked() {
- mInputManagerInternal.setVirtualMousePointerDisplayId(Display.INVALID_DISPLAY);
- mInputManagerInternal.setPointerAcceleration(
- IInputConstants.DEFAULT_POINTER_ACCELERATION);
- mActivePointerDisplayId = Display.INVALID_DISPLAY;
- }
-
private static String createPhys(@PhysType String type) {
return String.format("virtual%s:%d", type, sNextPhysId.getAndIncrement());
}
@@ -269,7 +276,8 @@
throw new IllegalArgumentException(
"Could not send button event to input device for given token");
}
- if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ if (inputDeviceDescriptor.getDisplayId()
+ != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -300,7 +308,8 @@
throw new IllegalArgumentException(
"Could not send relative event to input device for given token");
}
- if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ if (inputDeviceDescriptor.getDisplayId()
+ != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -317,7 +326,8 @@
throw new IllegalArgumentException(
"Could not send scroll event to input device for given token");
}
- if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ if (inputDeviceDescriptor.getDisplayId()
+ != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -334,7 +344,8 @@
throw new IllegalArgumentException(
"Could not get cursor position for input device for given token");
}
- if (inputDeviceDescriptor.getDisplayId() != mActivePointerDisplayId) {
+ if (inputDeviceDescriptor.getDisplayId()
+ != mInputManagerInternal.getVirtualMousePointerDisplayId()) {
throw new IllegalStateException(
"Display id associated with this mouse is not currently targetable");
}
@@ -354,7 +365,6 @@
fout.println(" type: " + inputDeviceDescriptor.getType());
fout.println(" phys: " + inputDeviceDescriptor.getPhys());
}
- fout.println(" Active mouse display id: " + mActivePointerDisplayId);
}
}
diff --git a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
index 98a5ec1..47e218b 100644
--- a/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
+++ b/services/companion/java/com/android/server/companion/virtual/VirtualDeviceImpl.java
@@ -34,12 +34,9 @@
import android.companion.virtual.VirtualDeviceParams;
import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ActivityInfo;
import android.graphics.Point;
import android.graphics.PointF;
import android.hardware.display.DisplayManager;
-import android.hardware.input.InputManagerInternal;
import android.hardware.input.VirtualKeyEvent;
import android.hardware.input.VirtualMouseButtonEvent;
import android.hardware.input.VirtualMouseRelativeEvent;
@@ -58,9 +55,8 @@
import android.util.SparseArray;
import android.window.DisplayWindowPolicyController;
+import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
-import com.android.internal.app.BlockedAppActivity;
-import com.android.server.LocalServices;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -86,6 +82,9 @@
private final VirtualDeviceParams mParams;
private final Map<Integer, PowerManager.WakeLock> mPerDisplayWakelocks = new ArrayMap<>();
private final IVirtualDeviceActivityListener mActivityListener;
+ // The default setting for showing the pointer on new displays.
+ @GuardedBy("mVirtualDeviceLock")
+ private boolean mDefaultShowPointerIcon = true;
private ActivityListener createListenerAdapter(int displayId) {
return new ActivityListener() {
@@ -385,6 +384,25 @@
}
}
+ @Override // Binder call
+ public void setShowPointerIcon(boolean showPointerIcon) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
+ "Permission required to unregister this input device");
+
+ final long binderToken = Binder.clearCallingIdentity();
+ try {
+ synchronized (mVirtualDeviceLock) {
+ mDefaultShowPointerIcon = showPointerIcon;
+ for (int displayId : mVirtualDisplayIds) {
+ mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(binderToken);
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
fout.println(" VirtualDevice: ");
@@ -395,6 +413,7 @@
for (int id : mVirtualDisplayIds) {
fout.println(" " + id);
}
+ fout.println(" mDefaultShowPointerIcon: " + mDefaultShowPointerIcon);
}
mInputController.dump(fout);
}
@@ -406,23 +425,23 @@
"Virtual device already have a virtual display with ID " + displayId);
}
mVirtualDisplayIds.add(displayId);
+ mInputController.setShowPointerIcon(mDefaultShowPointerIcon, displayId);
+ mInputController.setPointerAcceleration(1f, displayId);
+ mInputController.setDisplayEligibilityForPointerCapture(/* isEligible= */ false,
+ displayId);
// Since we're being called in the middle of the display being created, we post a
// task to grab the wakelock instead of doing it synchronously here, to avoid
// reentrancy problems.
mContext.getMainThreadHandler().post(() -> addWakeLockForDisplay(displayId));
- LocalServices.getService(
- InputManagerInternal.class).setDisplayEligibilityForPointerCapture(displayId,
- false);
final GenericWindowPolicyController dwpc =
new GenericWindowPolicyController(FLAG_SECURE,
SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS,
getAllowedUserHandles(),
mParams.getAllowedActivities(),
mParams.getBlockedActivities(),
- createListenerAdapter(displayId),
- activityInfo -> onActivityBlocked(displayId, activityInfo));
+ createListenerAdapter(displayId));
mWindowPolicyControllers.put(displayId, dwpc);
return dwpc;
}
@@ -445,16 +464,6 @@
}
}
- private void onActivityBlocked(int displayId, ActivityInfo activityInfo) {
- Intent intent = BlockedAppActivity.createStreamingBlockedIntent(
- UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo,
- mAssociationInfo.getDisplayName());
- mContext.startActivityAsUser(
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
- ActivityOptions.makeBasic().setLaunchDisplayId(displayId).toBundle(),
- mContext.getUser());
- }
-
private ArraySet<UserHandle> getAllowedUserHandles() {
ArraySet<UserHandle> result = new ArraySet<>();
DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class);
@@ -485,9 +494,6 @@
mPerDisplayWakelocks.remove(displayId);
}
mVirtualDisplayIds.remove(displayId);
- LocalServices.getService(
- InputManagerInternal.class).setDisplayEligibilityForPointerCapture(
- displayId, true);
mWindowPolicyControllers.remove(displayId);
}
}
diff --git a/services/core/Android.bp b/services/core/Android.bp
index 1106fe7..561009f 100644
--- a/services/core/Android.bp
+++ b/services/core/Android.bp
@@ -134,6 +134,7 @@
"app-compat-annotations",
"framework-tethering.stubs.module_lib",
"service-permission.stubs.system_server",
+ "service-supplementalprocess.stubs.system_server",
],
required: [
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index db510cb..91d2f55 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -32,7 +32,9 @@
import android.util.PackageUtils;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IBinaryTransparencyService;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.File;
import java.io.FileDescriptor;
@@ -51,11 +53,15 @@
public class BinaryTransparencyService extends SystemService {
private static final String TAG = "TransparencyService";
- private static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
- private static final String VBMETA_DIGEST_UNAVAILABLE = "vbmeta-digest-unavailable";
- private static final String SYSPROP_NAME_VBETA_DIGEST = "ro.boot.vbmeta.digest";
+ @VisibleForTesting
+ static final String VBMETA_DIGEST_UNINITIALIZED = "vbmeta-digest-uninitialized";
+ @VisibleForTesting
+ static final String VBMETA_DIGEST_UNAVAILABLE = "vbmeta-digest-unavailable";
+ @VisibleForTesting
+ static final String SYSPROP_NAME_VBETA_DIGEST = "ro.boot.vbmeta.digest";
- private static final String BINARY_HASH_ERROR = "SHA256HashError";
+ @VisibleForTesting
+ static final String BINARY_HASH_ERROR = "SHA256HashError";
private final Context mContext;
private String mVbmetaDigest;
@@ -373,6 +379,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 +444,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 +480,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/BootReceiver.java b/services/core/java/com/android/server/BootReceiver.java
index 48f5b51..a0575cf 100644
--- a/services/core/java/com/android/server/BootReceiver.java
+++ b/services/core/java/com/android/server/BootReceiver.java
@@ -484,7 +484,9 @@
HashMap<String, Long> timestamps = readTimestamps();
try {
if (proto) {
- db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ if (recordFileTimestamp(tombstone, timestamps)) {
+ db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
+ }
} else {
final String headers = getBootHeadersToLogAndUpdate();
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
@@ -526,16 +528,10 @@
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
- long fileTime = file.lastModified();
- if (fileTime <= 0) return; // File does not exist
-
- if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
- return; // Already logged this particular file
+ if (!recordFileTimestamp(file, timestamps)) {
+ return;
}
- timestamps.put(filename, fileTime);
-
-
String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED);
String text = headers + fileContents + footers;
// Create an additional report for system server native crashes, with a special tag.
@@ -548,6 +544,19 @@
addTextToDropBox(db, tag, text, filename, maxSize);
}
+ private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) {
+ final long fileTime = file.lastModified();
+ if (fileTime <= 0) return false; // File does not exist
+
+ final String filename = file.getPath();
+ if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
+ return false; // Already logged this particular file
+ }
+
+ timestamps.put(filename, fileTime);
+ return true;
+ }
+
private static void addTextToDropBox(DropBoxManager db, String tag, String text,
String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
diff --git a/services/core/java/com/android/server/Dumpable.java b/services/core/java/com/android/server/Dumpable.java
deleted file mode 100644
index 004f923..0000000
--- a/services/core/java/com/android/server/Dumpable.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright (C) 2020 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;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.util.IndentingPrintWriter;
-
-/**
- * Interface used to dump {@link SystemServer} state that is not associated with any service.
- *
- * <p>See {@link SystemServer.SystemServerDumper} for usage example.
- */
-// TODO(b/149254050): replace / merge with package android.util.Dumpable (it would require
-// exporting IndentingPrintWriter as @SystemApi) and/or changing the method to use a prefix
-public interface Dumpable {
-
- /**
- * Dumps the state.
- */
- void dump(@NonNull IndentingPrintWriter pw, @Nullable String[] args);
-
- /**
- * Gets the name of the dumpable.
- *
- * <p>If not overridden, will return the simple class name.
- */
- default String getDumpableName() {
- return Dumpable.this.getClass().getSimpleName();
- }
-}
diff --git a/services/core/java/com/android/server/SystemServerInitThreadPool.java b/services/core/java/com/android/server/SystemServerInitThreadPool.java
index 53b6605..63e7563 100644
--- a/services/core/java/com/android/server/SystemServerInitThreadPool.java
+++ b/services/core/java/com/android/server/SystemServerInitThreadPool.java
@@ -19,7 +19,7 @@
import android.annotation.NonNull;
import android.os.Build;
import android.os.Process;
-import android.util.IndentingPrintWriter;
+import android.util.Dumpable;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
@@ -28,6 +28,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.utils.TimingsTraceAndSlog;
+import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -196,7 +197,12 @@
}
@Override
- public void dump(IndentingPrintWriter pw, String[] args) {
+ public String getDumpableName() {
+ return SystemServerInitThreadPool.class.getSimpleName();
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
synchronized (LOCK) {
pw.printf("has instance: %b\n", (sInstance != null));
}
diff --git a/services/core/java/com/android/server/SystemServiceManager.java b/services/core/java/com/android/server/SystemServiceManager.java
index ce30f03..12e438d 100644
--- a/services/core/java/com/android/server/SystemServiceManager.java
+++ b/services/core/java/com/android/server/SystemServiceManager.java
@@ -27,8 +27,8 @@
import android.os.Trace;
import android.os.UserHandle;
import android.util.ArraySet;
+import android.util.Dumpable;
import android.util.EventLog;
-import android.util.IndentingPrintWriter;
import android.util.Slog;
import android.util.SparseArray;
@@ -44,6 +44,7 @@
import dalvik.system.PathClassLoader;
import java.io.File;
+import java.io.PrintWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
@@ -509,8 +510,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 +584,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 +607,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 +615,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;
@@ -684,7 +688,12 @@
}
@Override
- public void dump(IndentingPrintWriter pw, String[] args) {
+ public String getDumpableName() {
+ return SystemServiceManager.class.getSimpleName();
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
pw.printf("Current phase: %d\n", mCurrentPhase);
synchronized (mTargetUsers) {
if (mCurrentUser != null) {
@@ -708,14 +717,13 @@
}
}
final int startedLen = mServices.size();
+ String prefix = " ";
if (startedLen > 0) {
pw.printf("%d started services:\n", startedLen);
- pw.increaseIndent();
for (int i = 0; i < startedLen; i++) {
final SystemService service = mServices.get(i);
- pw.println(service.getClass().getCanonicalName());
+ pw.print(prefix); pw.println(service.getClass().getCanonicalName());
}
- pw.decreaseIndent();
} else {
pw.println("No started services");
}
diff --git a/services/core/java/com/android/server/TEST_MAPPING b/services/core/java/com/android/server/TEST_MAPPING
index 3288ca8..b6b3618 100644
--- a/services/core/java/com/android/server/TEST_MAPPING
+++ b/services/core/java/com/android/server/TEST_MAPPING
@@ -30,6 +30,10 @@
}
],
"file_patterns": ["SensorPrivacyService\\.java"]
+ },
+ {
+ "name": "BinaryTransparencyServiceTest",
+ "file_patterns": ["BinaryTransparencyService\\.java"]
}
],
"presubmit-large": [
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/Watchdog.java b/services/core/java/com/android/server/Watchdog.java
index ac20a08..8b48d0f 100644
--- a/services/core/java/com/android/server/Watchdog.java
+++ b/services/core/java/com/android/server/Watchdog.java
@@ -16,12 +16,17 @@
package com.android.server;
+import static com.android.server.Watchdog.HandlerCheckerAndTimeout.withCustomTimeout;
+import static com.android.server.Watchdog.HandlerCheckerAndTimeout.withDefaultTimeout;
+
import android.app.IActivityController;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.database.ContentObserver;
import android.hidl.manager.V1_0.IServiceManager;
+import android.net.Uri;
import android.os.Binder;
import android.os.Build;
import android.os.Debug;
@@ -35,12 +40,15 @@
import android.os.ServiceManager;
import android.os.SystemClock;
import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.provider.Settings;
import android.sysprop.WatchdogProperties;
import android.util.EventLog;
import android.util.Log;
import android.util.Slog;
import android.util.SparseArray;
+import com.android.internal.os.BackgroundThread;
import com.android.internal.os.ProcessCpuTracker;
import com.android.internal.os.ZygoteConnectionConstants;
import com.android.internal.util.FrameworkStatsLog;
@@ -61,10 +69,13 @@
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
+import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
-/** This class calls its monitor every minute. Killing this process if they don't return **/
+/**
+ * This class calls its monitor every minute. Killing this process if they don't return
+ **/
public class Watchdog {
static final String TAG = "Watchdog";
@@ -79,9 +90,7 @@
// can trigger the watchdog.
// Note 2: The debug value is already below the wait time in ZygoteConnection. Wrapped
// applications may not work with a debug build. CTS will fail.
- private static final long DEFAULT_TIMEOUT =
- (DB ? 10 * 1000 : 60 * 1000) * Build.HW_TIMEOUT_MULTIPLIER;
- private static final long CHECK_INTERVAL = DEFAULT_TIMEOUT / 2;
+ private static final long DEFAULT_TIMEOUT = DB ? 10 * 1000 : 60 * 1000;
// These are temporally ordered: larger values as lateness increases
private static final int COMPLETED = 0;
@@ -156,34 +165,71 @@
private final Object mLock = new Object();
/* This handler will be used to post message back onto the main thread */
- private final ArrayList<HandlerChecker> mHandlerCheckers = new ArrayList<>();
+ private final ArrayList<HandlerCheckerAndTimeout> mHandlerCheckers = new ArrayList<>();
private final HandlerChecker mMonitorChecker;
private ActivityManagerService mActivity;
-
private IActivityController mController;
private boolean mAllowRestart = true;
+ // We start with DEFAULT_TIMEOUT. This will then be update with the timeout values from Settings
+ // once the settings provider is initialized.
+ private volatile long mWatchdogTimeoutMillis = DEFAULT_TIMEOUT;
private final List<Integer> mInterestingJavaPids = new ArrayList<>();
-
private final TraceErrorLogger mTraceErrorLogger;
+ /** Holds a checker and its timeout. */
+ static final class HandlerCheckerAndTimeout {
+ private final HandlerChecker mHandler;
+ private final Optional<Long> mCustomTimeoutMillis;
+
+ private HandlerCheckerAndTimeout(HandlerChecker checker, Optional<Long> timeoutMillis) {
+ this.mHandler = checker;
+ this.mCustomTimeoutMillis = timeoutMillis;
+ }
+
+ HandlerChecker checker() {
+ return mHandler;
+ }
+
+ /** Returns the timeout. */
+ Optional<Long> customTimeoutMillis() {
+ return mCustomTimeoutMillis;
+ }
+
+ /**
+ * Creates a checker with the default timeout. The timeout will use the default value which
+ * is configurable server-side.
+ */
+ static HandlerCheckerAndTimeout withDefaultTimeout(HandlerChecker checker) {
+ return new HandlerCheckerAndTimeout(checker, Optional.empty());
+ }
+
+ /**
+ * Creates a checker with a custom timeout. The timeout overrides the default value and will
+ * always be used.
+ */
+ static HandlerCheckerAndTimeout withCustomTimeout(
+ HandlerChecker checker, long timeoutMillis) {
+ return new HandlerCheckerAndTimeout(checker, Optional.of(timeoutMillis));
+ }
+ }
+
/**
* Used for checking status of handle threads and scheduling monitor callbacks.
*/
public final class HandlerChecker implements Runnable {
private final Handler mHandler;
private final String mName;
- private final long mWaitMax;
private final ArrayList<Monitor> mMonitors = new ArrayList<Monitor>();
private final ArrayList<Monitor> mMonitorQueue = new ArrayList<Monitor>();
+ private long mWaitMax;
private boolean mCompleted;
private Monitor mCurrentMonitor;
private long mStartTime;
private int mPauseCount;
- HandlerChecker(Handler handler, String name, long waitMaxMillis) {
+ HandlerChecker(Handler handler, String name) {
mHandler = handler;
mName = name;
- mWaitMax = waitMaxMillis;
mCompleted = true;
}
@@ -193,7 +239,13 @@
mMonitorQueue.add(monitor);
}
- public void scheduleCheckLocked() {
+ /**
+ * Schedules a run on the handler thread.
+ *
+ * @param handlerCheckerTimeoutMillis the timeout to use for this run
+ */
+ public void scheduleCheckLocked(long handlerCheckerTimeoutMillis) {
+ mWaitMax = handlerCheckerTimeoutMillis;
if (mCompleted) {
// Safe to update monitors in queue, Handler is not in the middle of work
mMonitors.addAll(mMonitorQueue);
@@ -222,10 +274,6 @@
mHandler.postAtFrontOfQueue(this);
}
- boolean isOverdueLocked() {
- return (!mCompleted) && (SystemClock.uptimeMillis() > mStartTime + mWaitMax);
- }
-
public int getCompletionStateLocked() {
if (mCompleted) {
return COMPLETED;
@@ -336,36 +384,37 @@
private Watchdog() {
mThread = new Thread(this::run, "watchdog");
+
// Initialize handler checkers for each common thread we want to check. Note
// that we are not currently checking the background thread, since it can
// potentially hold longer running operations with no guarantees about the timeliness
// of operations there.
-
+ //
// The shared foreground thread is the main checker. It is where we
// will also dispatch monitor checks and do other work.
mMonitorChecker = new HandlerChecker(FgThread.getHandler(),
- "foreground thread", DEFAULT_TIMEOUT);
- mHandlerCheckers.add(mMonitorChecker);
+ "foreground thread");
+ mHandlerCheckers.add(withDefaultTimeout(mMonitorChecker));
// Add checker for main thread. We only do a quick check since there
// can be UI running on the thread.
- mHandlerCheckers.add(new HandlerChecker(new Handler(Looper.getMainLooper()),
- "main thread", DEFAULT_TIMEOUT));
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(new Handler(Looper.getMainLooper()), "main thread")));
// Add checker for shared UI thread.
- mHandlerCheckers.add(new HandlerChecker(UiThread.getHandler(),
- "ui thread", DEFAULT_TIMEOUT));
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(UiThread.getHandler(), "ui thread")));
// And also check IO thread.
- mHandlerCheckers.add(new HandlerChecker(IoThread.getHandler(),
- "i/o thread", DEFAULT_TIMEOUT));
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(IoThread.getHandler(), "i/o thread")));
// And the display thread.
- mHandlerCheckers.add(new HandlerChecker(DisplayThread.getHandler(),
- "display thread", DEFAULT_TIMEOUT));
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(DisplayThread.getHandler(), "display thread")));
// And the animation thread.
- mHandlerCheckers.add(new HandlerChecker(AnimationThread.getHandler(),
- "animation thread", DEFAULT_TIMEOUT));
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(AnimationThread.getHandler(), "animation thread")));
// And the surface animation thread.
- mHandlerCheckers.add(new HandlerChecker(SurfaceAnimationThread.getHandler(),
- "surface animation thread", DEFAULT_TIMEOUT));
-
+ mHandlerCheckers.add(withDefaultTimeout(
+ new HandlerChecker(SurfaceAnimationThread.getHandler(),
+ "surface animation thread")));
// Initialize monitor for Binder threads.
addMonitor(new BinderThreadMonitor());
@@ -397,6 +446,62 @@
android.Manifest.permission.REBOOT, null);
}
+ private static class SettingsObserver extends ContentObserver {
+ private final Uri mUri = Settings.Global.getUriFor(Settings.Global.WATCHDOG_TIMEOUT_MILLIS);
+ private final Context mContext;
+ private final Watchdog mWatchdog;
+
+ SettingsObserver(Context context, Watchdog watchdog) {
+ super(BackgroundThread.getHandler());
+ mContext = context;
+ mWatchdog = watchdog;
+ // Always kick once to ensure that we match current state
+ onChange();
+ }
+
+ @Override
+ public void onChange(boolean selfChange, Uri uri, int userId) {
+ if (mUri.equals(uri)) {
+ onChange();
+ }
+ }
+
+ public void onChange() {
+ try {
+ mWatchdog.updateWatchdogTimeout(Settings.Global.getLong(
+ mContext.getContentResolver(),
+ Settings.Global.WATCHDOG_TIMEOUT_MILLIS, DEFAULT_TIMEOUT));
+ } catch (RuntimeException e) {
+ Slog.e(TAG, "Exception while reading settings " + e.getMessage(), e);
+ }
+ }
+ }
+
+ /**
+ * Register an observer to listen to settings.
+ *
+ * It needs to be called after the settings service is initialized.
+ */
+ public void registerSettingsObserver(Context context) {
+ context.getContentResolver().registerContentObserver(
+ Settings.Global.getUriFor(Settings.Global.WATCHDOG_TIMEOUT_MILLIS),
+ false,
+ new SettingsObserver(context, this),
+ UserHandle.USER_SYSTEM);
+ }
+
+ /**
+ * Updates watchdog timeout values.
+ */
+ void updateWatchdogTimeout(long timeoutMillis) {
+ // See the notes on DEFAULT_TIMEOUT.
+ if (!DB && timeoutMillis <= ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS) {
+ timeoutMillis = ZygoteConnectionConstants.WRAPPED_PID_TIMEOUT_MILLIS + 1;
+ }
+ mWatchdogTimeoutMillis = timeoutMillis;
+ Slog.i(TAG, "Watchdog timeout updated to " + mWatchdogTimeoutMillis + " millis");
+ }
+
private static boolean isInterestingJavaProcess(String processName) {
return processName.equals(StorageManagerService.sMediaStoreAuthorityProcessName)
|| processName.equals("com.android.phone");
@@ -446,13 +551,17 @@
}
public void addThread(Handler thread) {
- addThread(thread, DEFAULT_TIMEOUT);
+ synchronized (mLock) {
+ final String name = thread.getLooper().getThread().getName();
+ mHandlerCheckers.add(withDefaultTimeout(new HandlerChecker(thread, name)));
+ }
}
public void addThread(Handler thread, long timeoutMillis) {
synchronized (mLock) {
final String name = thread.getLooper().getThread().getName();
- mHandlerCheckers.add(new HandlerChecker(thread, name, timeoutMillis));
+ mHandlerCheckers.add(
+ withCustomTimeout(new HandlerChecker(thread, name), timeoutMillis));
}
}
@@ -471,9 +580,10 @@
*/
public void pauseWatchingCurrentThread(String reason) {
synchronized (mLock) {
- for (HandlerChecker hc : mHandlerCheckers) {
- if (Thread.currentThread().equals(hc.getThread())) {
- hc.pauseLocked(reason);
+ for (HandlerCheckerAndTimeout hc : mHandlerCheckers) {
+ HandlerChecker checker = hc.checker();
+ if (Thread.currentThread().equals(checker.getThread())) {
+ checker.pauseLocked(reason);
}
}
}
@@ -493,9 +603,10 @@
*/
public void resumeWatchingCurrentThread(String reason) {
synchronized (mLock) {
- for (HandlerChecker hc : mHandlerCheckers) {
- if (Thread.currentThread().equals(hc.getThread())) {
- hc.resumeLocked(reason);
+ for (HandlerCheckerAndTimeout hc : mHandlerCheckers) {
+ HandlerChecker checker = hc.checker();
+ if (Thread.currentThread().equals(checker.getThread())) {
+ checker.resumeLocked(reason);
}
}
}
@@ -516,17 +627,17 @@
private int evaluateCheckerCompletionLocked() {
int state = COMPLETED;
for (int i=0; i<mHandlerCheckers.size(); i++) {
- HandlerChecker hc = mHandlerCheckers.get(i);
+ HandlerChecker hc = mHandlerCheckers.get(i).checker();
state = Math.max(state, hc.getCompletionStateLocked());
}
return state;
}
- private ArrayList<HandlerChecker> getBlockedCheckersLocked() {
+ private ArrayList<HandlerChecker> getCheckersWithStateLocked(int completionState) {
ArrayList<HandlerChecker> checkers = new ArrayList<HandlerChecker>();
for (int i=0; i<mHandlerCheckers.size(); i++) {
- HandlerChecker hc = mHandlerCheckers.get(i);
- if (hc.isOverdueLocked()) {
+ HandlerChecker hc = mHandlerCheckers.get(i).checker();
+ if (hc.getCompletionStateLocked() == completionState) {
checkers.add(hc);
}
}
@@ -595,20 +706,28 @@
private void run() {
boolean waitedHalf = false;
+
while (true) {
List<HandlerChecker> blockedCheckers = Collections.emptyList();
String subject = "";
boolean allowRestart = true;
int debuggerWasConnected = 0;
boolean doWaitedHalfDump = false;
+ // The value of mWatchdogTimeoutMillis might change while we are executing the loop.
+ // We store the current value to use a consistent value for all handlers.
+ final long watchdogTimeoutMillis = mWatchdogTimeoutMillis;
+ final long checkIntervalMillis = watchdogTimeoutMillis / 2;
final ArrayList<Integer> pids;
synchronized (mLock) {
- long timeout = CHECK_INTERVAL;
+ long timeout = checkIntervalMillis;
// Make sure we (re)spin the checkers that have become idle within
// this wait-and-check interval
for (int i=0; i<mHandlerCheckers.size(); i++) {
- HandlerChecker hc = mHandlerCheckers.get(i);
- hc.scheduleCheckLocked();
+ HandlerCheckerAndTimeout hc = mHandlerCheckers.get(i);
+ // We pick the watchdog to apply every time we reschedule the checkers. The
+ // default timeout might have changed since the last run.
+ hc.checker().scheduleCheckLocked(hc.customTimeoutMillis()
+ .orElse(watchdogTimeoutMillis * Build.HW_TIMEOUT_MULTIPLIER));
}
if (debuggerWasConnected > 0) {
@@ -633,7 +752,7 @@
if (Debug.isDebuggerConnected()) {
debuggerWasConnected = 2;
}
- timeout = CHECK_INTERVAL - (SystemClock.uptimeMillis() - start);
+ timeout = checkIntervalMillis - (SystemClock.uptimeMillis() - start);
}
final int waitState = evaluateCheckerCompletionLocked();
@@ -649,6 +768,8 @@
Slog.i(TAG, "WAITED_HALF");
waitedHalf = true;
// We've waited half, but we'd need to do the stack trace dump w/o the lock.
+ blockedCheckers = getCheckersWithStateLocked(WAITED_HALF);
+ subject = describeCheckersLocked(blockedCheckers);
pids = new ArrayList<>(mInterestingJavaPids);
doWaitedHalfDump = true;
} else {
@@ -656,90 +777,27 @@
}
} else {
// something is overdue!
- blockedCheckers = getBlockedCheckersLocked();
+ blockedCheckers = getCheckersWithStateLocked(OVERDUE);
subject = describeCheckersLocked(blockedCheckers);
allowRestart = mAllowRestart;
pids = new ArrayList<>(mInterestingJavaPids);
}
} // END synchronized (mLock)
- if (doWaitedHalfDump) {
- // Get critical event log before logging the half watchdog so that it doesn't
- // occur in the log.
- String criticalEvents =
- CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
- CriticalEventLog.getInstance().logHalfWatchdog(subject);
+ // If we got here, that means that the system is most likely hung.
+ //
+ // First collect stack traces from all threads of the system process.
+ //
+ // Then, if we reached the full timeout, kill this process so that the system will
+ // restart. If we reached half of the timeout, just log some information and continue.
+ logWatchog(doWaitedHalfDump, subject, pids);
- // We've waited half the deadlock-detection interval. Pull a stack
- // trace and wait another half.
- ActivityManagerService.dumpStackTraces(pids, null, null,
- getInterestingNativePids(), null, subject, criticalEvents);
+ if (doWaitedHalfDump) {
+ // We have waited for only half of the timeout, we continue to wait for the duration
+ // of the full timeout before killing the process.
continue;
}
- // If we got here, that means that the system is most likely hung.
- // First collect stack traces from all threads of the system process.
- // Then kill this process so that the system will restart.
- EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
-
- final UUID errorId = mTraceErrorLogger.generateErrorId();
- if (mTraceErrorLogger.isAddErrorIdEnabled()) {
- mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
- mTraceErrorLogger.addSubjectToTrace(subject, errorId);
- }
-
- // Log the atom as early as possible since it is used as a mechanism to trigger
- // Perfetto. Ideally, the Perfetto trace capture should happen as close to the
- // point in time when the Watchdog happens as possible.
- FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
-
- // Get critical event log before logging the watchdog so that it doesn't occur in the
- // log.
- String criticalEvents =
- CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
- CriticalEventLog.getInstance().logWatchdog(subject, errorId);
-
- long anrTime = SystemClock.uptimeMillis();
- StringBuilder report = new StringBuilder();
- report.append(MemoryPressureUtil.currentPsiState());
- ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
- StringWriter tracesFileException = new StringWriter();
- final File stack = ActivityManagerService.dumpStackTraces(
- pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
- tracesFileException, subject, criticalEvents);
-
- // Give some extra time to make sure the stack traces get written.
- // The system's been hanging for a minute, another second or two won't hurt much.
- SystemClock.sleep(5000);
-
- processCpuTracker.update();
- report.append(processCpuTracker.printCurrentState(anrTime));
- report.append(tracesFileException.getBuffer());
-
- // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the kernel log
- doSysRq('w');
- doSysRq('l');
-
- // Try to add the error to the dropbox, but assuming that the ActivityManager
- // itself may be deadlocked. (which has happened, causing this statement to
- // deadlock and the watchdog as a whole to be ineffective)
- Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
- public void run() {
- // If a watched thread hangs before init() is called, we don't have a
- // valid mActivity. So we can't log the error to dropbox.
- if (mActivity != null) {
- mActivity.addErrorToDropBox(
- "watchdog", null, "system_server", null, null, null,
- null, report.toString(), stack, null, null, null,
- errorId);
- }
- }
- };
- dropboxThread.start();
- try {
- dropboxThread.join(2000); // wait up to 2 seconds for it to return.
- } catch (InterruptedException ignored) {}
-
IActivityController controller;
synchronized (mLock) {
controller = mController;
@@ -785,6 +843,74 @@
}
}
+ private void logWatchog(boolean halfWatchdog, String subject, ArrayList<Integer> pids) {
+ // Get critical event log before logging the half watchdog so that it doesn't
+ // occur in the log.
+ String criticalEvents =
+ CriticalEventLog.getInstance().logLinesForSystemServerTraceFile();
+ final UUID errorId = mTraceErrorLogger.generateErrorId();
+ if (mTraceErrorLogger.isAddErrorIdEnabled()) {
+ mTraceErrorLogger.addErrorIdToTrace("system_server", errorId);
+ mTraceErrorLogger.addSubjectToTrace(subject, errorId);
+ }
+
+ final String dropboxTag;
+ if (halfWatchdog) {
+ dropboxTag = "pre_watchdog";
+ CriticalEventLog.getInstance().logHalfWatchdog(subject);
+ } else {
+ dropboxTag = "watchdog";
+ CriticalEventLog.getInstance().logWatchdog(subject, errorId);
+ EventLog.writeEvent(EventLogTags.WATCHDOG, subject);
+ // Log the atom as early as possible since it is used as a mechanism to trigger
+ // Perfetto. Ideally, the Perfetto trace capture should happen as close to the
+ // point in time when the Watchdog happens as possible.
+ FrameworkStatsLog.write(FrameworkStatsLog.SYSTEM_SERVER_WATCHDOG_OCCURRED, subject);
+ }
+
+ long anrTime = SystemClock.uptimeMillis();
+ StringBuilder report = new StringBuilder();
+ report.append(MemoryPressureUtil.currentPsiState());
+ ProcessCpuTracker processCpuTracker = new ProcessCpuTracker(false);
+ StringWriter tracesFileException = new StringWriter();
+ final File stack = ActivityManagerService.dumpStackTraces(
+ pids, processCpuTracker, new SparseArray<>(), getInterestingNativePids(),
+ tracesFileException, subject, criticalEvents);
+ // Give some extra time to make sure the stack traces get written.
+ // The system's been hanging for a whlie, another second or two won't hurt much.
+ SystemClock.sleep(5000);
+ processCpuTracker.update();
+ report.append(processCpuTracker.printCurrentState(anrTime));
+ report.append(tracesFileException.getBuffer());
+
+ if (!halfWatchdog) {
+ // Trigger the kernel to dump all blocked threads, and backtraces on all CPUs to the
+ // kernel log
+ doSysRq('w');
+ doSysRq('l');
+ }
+
+ // Try to add the error to the dropbox, but assuming that the ActivityManager
+ // itself may be deadlocked. (which has happened, causing this statement to
+ // deadlock and the watchdog as a whole to be ineffective)
+ Thread dropboxThread = new Thread("watchdogWriteToDropbox") {
+ public void run() {
+ // If a watched thread hangs before init() is called, we don't have a
+ // valid mActivity. So we can't log the error to dropbox.
+ if (mActivity != null) {
+ mActivity.addErrorToDropBox(
+ dropboxTag, null, "system_server", null, null, null,
+ null, report.toString(), stack, null, null, null,
+ errorId);
+ }
+ }
+ };
+ dropboxThread.start();
+ try {
+ dropboxThread.join(2000); // wait up to 2 seconds for it to return.
+ } catch (InterruptedException ignored) { }
+ }
+
private void doSysRq(char c) {
try {
FileWriter sysrq_trigger = new FileWriter("/proc/sysrq-trigger");
diff --git a/services/core/java/com/android/server/adb/AdbService.java b/services/core/java/com/android/server/adb/AdbService.java
index 2845fbf..6667d1b 100644
--- a/services/core/java/com/android/server/adb/AdbService.java
+++ b/services/core/java/com/android/server/adb/AdbService.java
@@ -311,14 +311,15 @@
}
/**
- * @return true if the device supports secure ADB over Wi-Fi.
+ * @return true if the device supports secure ADB over Wi-Fi or Ethernet.
* @hide
*/
@Override
public boolean isAdbWifiSupported() {
mContext.enforceCallingPermission(
android.Manifest.permission.MANAGE_DEBUGGING, "AdbService");
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI);
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI) ||
+ mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_ETHERNET);
}
/**
diff --git a/services/core/java/com/android/server/am/ActivityManagerConstants.java b/services/core/java/com/android/server/am/ActivityManagerConstants.java
index bcb1be3..940ad73 100644
--- a/services/core/java/com/android/server/am/ActivityManagerConstants.java
+++ b/services/core/java/com/android/server/am/ActivityManagerConstants.java
@@ -123,6 +123,7 @@
static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE = "kill_bg_restricted_cached_idle";
static final String KEY_KILL_BG_RESTRICTED_CACHED_IDLE_SETTLE_TIME =
"kill_bg_restricted_cached_idle_settle_time";
+ static final String KEY_ENABLE_COMPONENT_ALIAS = "enable_experimental_component_alias";
static final String KEY_COMPONENT_ALIAS_OVERRIDES = "component_alias_overrides";
private static final int DEFAULT_MAX_CACHED_PROCESSES = 32;
@@ -199,6 +200,7 @@
* Whether or not to enable the extra delays to service restarts on memory pressure.
*/
private static final boolean DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE = true;
+ private static final boolean DEFAULT_ENABLE_COMPONENT_ALIAS = false;
private static final String DEFAULT_COMPONENT_ALIAS_OVERRIDES = "";
// Flag stored in the DeviceConfig API.
@@ -595,6 +597,12 @@
DEFAULT_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE;
/**
+ * Whether to enable "component alias" experimental feature. This can only be enabled
+ * on userdebug or eng builds.
+ */
+ volatile boolean mEnableComponentAlias = DEFAULT_ENABLE_COMPONENT_ALIAS;
+
+ /**
* Defines component aliases. Format
* ComponentName ":" ComponentName ( "," ComponentName ":" ComponentName )*
*/
@@ -831,6 +839,7 @@
case KEY_ENABLE_EXTRA_SERVICE_RESTART_DELAY_ON_MEM_PRESSURE:
updateEnableExtraServiceRestartDelayOnMemPressure();
break;
+ case KEY_ENABLE_COMPONENT_ALIAS:
case KEY_COMPONENT_ALIAS_OVERRIDES:
updateComponentAliases();
break;
@@ -1269,11 +1278,15 @@
}
private void updateComponentAliases() {
+ mEnableComponentAlias = DeviceConfig.getBoolean(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
+ KEY_ENABLE_COMPONENT_ALIAS,
+ DEFAULT_ENABLE_COMPONENT_ALIAS);
mComponentAliasOverrides = DeviceConfig.getString(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER,
KEY_COMPONENT_ALIAS_OVERRIDES,
DEFAULT_COMPONENT_ALIAS_OVERRIDES);
- mService.mComponentAliasResolver.update(mComponentAliasOverrides);
+ mService.mComponentAliasResolver.update(mEnableComponentAlias, mComponentAliasOverrides);
}
private void updateProcessKillTimeout() {
@@ -1512,6 +1525,8 @@
pw.print("="); pw.println(mPushMessagingOverQuotaBehavior);
pw.print(" "); pw.print(KEY_FGS_ALLOW_OPT_OUT);
pw.print("="); pw.println(mFgsAllowOptOut);
+ pw.print(" "); pw.print(KEY_ENABLE_COMPONENT_ALIAS);
+ pw.print("="); pw.println(mEnableComponentAlias);
pw.print(" "); pw.print(KEY_COMPONENT_ALIAS_OVERRIDES);
pw.print("="); pw.println(mComponentAliasOverrides);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 442b9de..0310b0f 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)) {
@@ -8051,7 +8066,8 @@
// Load the component aliases.
t.traceBegin("componentAlias");
- mComponentAliasResolver.onSystemReady(mConstants.mComponentAliasOverrides);
+ mComponentAliasResolver.onSystemReady(mConstants.mEnableComponentAlias,
+ mConstants.mComponentAliasOverrides);
t.traceEnd(); // componentAlias
t.traceEnd(); // PhaseActivityManagerReady
@@ -12783,7 +12799,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 +12873,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 +13367,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 +16090,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 +17906,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/am/ComponentAliasResolver.java b/services/core/java/com/android/server/am/ComponentAliasResolver.java
index 23553a7..aef7a6c 100644
--- a/services/core/java/com/android/server/am/ComponentAliasResolver.java
+++ b/services/core/java/com/android/server/am/ComponentAliasResolver.java
@@ -30,6 +30,7 @@
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.os.Binder;
+import android.os.Build;
import android.os.ServiceManager;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -56,6 +57,8 @@
* "quick & dirty". For example, to define aliases, we use a regular intent filter and meta-data
* in the manifest, instead of adding proper tags/attributes to AndroidManifest.xml.
*
+ * Because it's an experimental feature, it can't be enabled on a user build.
+ *
* Also, for now, aliases can be defined across any packages, but in the final version, there'll
* be restrictions:
* - We probably should only allow either privileged or preinstalled apps.
@@ -78,6 +81,9 @@
private final Context mContext;
@GuardedBy("mLock")
+ private boolean mEnabledByDeviceConfig;
+
+ @GuardedBy("mLock")
private boolean mEnabled;
@GuardedBy("mLock")
@@ -141,7 +147,7 @@
/**
* Call this on systemRead().
*/
- public void onSystemReady(String overrides) {
+ public void onSystemReady(boolean enabledByDeviceConfig, String overrides) {
synchronized (mLock) {
mPlatformCompat = (PlatformCompat) ServiceManager.getService(
Context.PLATFORM_COMPAT_SERVICE);
@@ -149,19 +155,21 @@
mCompatChangeListener);
}
if (DEBUG) Slog.d(TAG, "Compat listener set.");
- update(overrides);
+ update(enabledByDeviceConfig, overrides);
}
/**
* (Re-)loads aliases from <meta-data> and the device config override.
*/
- public void update(String overrides) {
+ public void update(boolean enabledByDeviceConfig, String overrides) {
synchronized (mLock) {
if (mPlatformCompat == null) {
return; // System not ready.
}
- final boolean enabled = mPlatformCompat.isChangeEnabledByPackageName(
- USE_EXPERIMENTAL_COMPONENT_ALIAS, "android", UserHandle.USER_SYSTEM);
+ final boolean enabled = Build.isDebuggable()
+ && (enabledByDeviceConfig
+ || mPlatformCompat.isChangeEnabledByPackageName(
+ USE_EXPERIMENTAL_COMPONENT_ALIAS, "android", UserHandle.USER_SYSTEM));
if (enabled != mEnabled) {
Slog.i(TAG, (enabled ? "Enabling" : "Disabling") + " component aliases...");
if (enabled) {
@@ -172,6 +180,7 @@
}
}
mEnabled = enabled;
+ mEnabledByDeviceConfig = enabledByDeviceConfig;
mOverrideString = overrides;
if (mEnabled) {
@@ -184,7 +193,7 @@
private void refresh() {
synchronized (mLock) {
- update(mOverrideString);
+ update(mEnabledByDeviceConfig, mOverrideString);
}
}
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index e4c0846..481b6dd 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -2034,6 +2034,11 @@
}
}
+ /**
+ * Tell WindowManager we're ready to unfreeze the screen, at its leisure. Note that there is
+ * likely a lot going on, and WM won't unfreeze until the drawing is all done, so
+ * the actual unfreeze may still not happen for a long time; this is expected.
+ */
@VisibleForTesting
void unfreezeScreen() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
diff --git a/services/core/java/com/android/server/app/GameManagerService.java b/services/core/java/com/android/server/app/GameManagerService.java
index 3c9d29d..551773e 100644
--- a/services/core/java/com/android/server/app/GameManagerService.java
+++ b/services/core/java/com/android/server/app/GameManagerService.java
@@ -20,6 +20,11 @@
import static android.content.Intent.ACTION_PACKAGE_CHANGED;
import static android.content.Intent.ACTION_PACKAGE_REMOVED;
+import static com.android.internal.R.styleable.GameModeConfig_allowGameAngleDriver;
+import static com.android.internal.R.styleable.GameModeConfig_allowGameDownscaling;
+import static com.android.internal.R.styleable.GameModeConfig_allowGameFpsOverride;
+import static com.android.internal.R.styleable.GameModeConfig_supportsBatteryGameMode;
+import static com.android.internal.R.styleable.GameModeConfig_supportsPerformanceGameMode;
import static com.android.server.wm.CompatModePackages.DOWNSCALED;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_30;
import static com.android.server.wm.CompatModePackages.DOWNSCALE_35;
@@ -54,6 +59,10 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
import android.hardware.power.Mode;
import android.net.Uri;
import android.os.Binder;
@@ -71,8 +80,10 @@
import android.provider.DeviceConfig;
import android.provider.DeviceConfig.Properties;
import android.util.ArrayMap;
+import android.util.AttributeSet;
import android.util.KeyValueListParser;
import android.util.Slog;
+import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
@@ -84,7 +95,11 @@
import com.android.server.SystemService;
import com.android.server.SystemService.TargetUser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@@ -425,12 +440,20 @@
public static final String METADATA_BATTERY_MODE_ENABLE =
"com.android.app.gamemode.battery.enabled";
+ /**
+ * Metadata that allows a game to specify all intervention information with an XML file in
+ * the application field.
+ */
+ public static final String METADATA_GAME_MODE_CONFIG = "android.game_mode_config";
+
+ private static final String GAME_MODE_CONFIG_NODE_NAME = "game-mode-config";
private final String mPackageName;
private final ArrayMap<Integer, GameModeConfiguration> mModeConfigs;
private boolean mPerfModeOptedIn;
private boolean mBatteryModeOptedIn;
private boolean mAllowDownscale;
private boolean mAllowAngle;
+ private boolean mAllowFpsOverride;
GamePackageConfiguration(String packageName, int userId) {
mPackageName = packageName;
@@ -438,18 +461,21 @@
try {
final ApplicationInfo ai = mPackageManager.getApplicationInfoAsUser(packageName,
PackageManager.GET_META_DATA, userId);
- if (ai.metaData != null) {
- mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
- mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
- mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
- mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
- } else {
- mPerfModeOptedIn = false;
- mBatteryModeOptedIn = false;
- mAllowDownscale = true;
- mAllowAngle = true;
+ if (!parseInterventionFromXml(ai, packageName)) {
+ if (ai.metaData != null) {
+ mPerfModeOptedIn = ai.metaData.getBoolean(METADATA_PERFORMANCE_MODE_ENABLE);
+ mBatteryModeOptedIn = ai.metaData.getBoolean(METADATA_BATTERY_MODE_ENABLE);
+ mAllowDownscale = ai.metaData.getBoolean(METADATA_WM_ALLOW_DOWNSCALE, true);
+ mAllowAngle = ai.metaData.getBoolean(METADATA_ANGLE_ALLOW_ANGLE, true);
+ } else {
+ mPerfModeOptedIn = false;
+ mBatteryModeOptedIn = false;
+ mAllowDownscale = true;
+ mAllowAngle = true;
+ mAllowFpsOverride = true;
+ }
}
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (NameNotFoundException e) {
// Not all packages are installed, hence ignore those that are not installed yet.
Slog.v(TAG, "Failed to get package metadata");
}
@@ -469,6 +495,53 @@
}
}
+ private boolean parseInterventionFromXml(ApplicationInfo ai, String packageName) {
+ boolean xmlFound = false;
+ try (XmlResourceParser parser = ai.loadXmlMetaData(mPackageManager,
+ METADATA_GAME_MODE_CONFIG)) {
+ if (parser == null) {
+ Slog.v(TAG, "No " + METADATA_GAME_MODE_CONFIG
+ + " meta-data found for package " + mPackageName);
+ } else {
+ xmlFound = true;
+ final Resources resources = mPackageManager.getResourcesForApplication(
+ packageName);
+ final AttributeSet attributeSet = Xml.asAttributeSet(parser);
+ int type;
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.START_TAG) {
+ // Do nothing
+ }
+
+ boolean isStartingTagGameModeConfig =
+ GAME_MODE_CONFIG_NODE_NAME.equals(parser.getName());
+ if (!isStartingTagGameModeConfig) {
+ Slog.w(TAG, "Meta-data does not start with "
+ + GAME_MODE_CONFIG_NODE_NAME
+ + " tag");
+ } else {
+ final TypedArray array = resources.obtainAttributes(attributeSet,
+ com.android.internal.R.styleable.GameModeConfig);
+ mPerfModeOptedIn = array.getBoolean(
+ GameModeConfig_supportsPerformanceGameMode, false);
+ mBatteryModeOptedIn = array.getBoolean(
+ GameModeConfig_supportsBatteryGameMode,
+ false);
+ mAllowDownscale = array.getBoolean(GameModeConfig_allowGameDownscaling,
+ true);
+ mAllowAngle = array.getBoolean(GameModeConfig_allowGameAngleDriver, true);
+ mAllowFpsOverride = array.getBoolean(GameModeConfig_allowGameFpsOverride,
+ true);
+ array.recycle();
+ }
+ }
+ } catch (NameNotFoundException | XmlPullParserException | IOException ex) {
+ Slog.e(TAG, "Error while parsing XML meta-data for "
+ + METADATA_GAME_MODE_CONFIG);
+ }
+ return xmlFound;
+ }
+
/**
* GameModeConfiguration contains all the values for all the interventions associated with
* a game mode.
@@ -497,7 +570,8 @@
mScaling = !mAllowDownscale || willGamePerformOptimizations(mGameMode)
? DEFAULT_SCALING : parser.getString(SCALING_KEY, DEFAULT_SCALING);
- mFps = parser.getString(FPS_KEY, DEFAULT_FPS);
+ mFps = mAllowFpsOverride && !willGamePerformOptimizations(mGameMode)
+ ? parser.getString(FPS_KEY, DEFAULT_FPS) : DEFAULT_FPS;
// We only want to use ANGLE if:
// - We're allowed to use ANGLE (the app hasn't opted out via the manifest) AND
// - The app has not opted in to performing the work itself AND
@@ -691,7 +765,7 @@
try {
return mPackageManager.getPackageUidAsUser(packageName, userId)
== Binder.getCallingUid();
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (NameNotFoundException e) {
return false;
}
}
@@ -855,7 +929,7 @@
*/
@Override
@RequiresPermission(Manifest.permission.MANAGE_GAME_MODE)
- public @GameMode boolean getAngleEnabled(String packageName, int userId)
+ public @GameMode boolean isAngleEnabled(String packageName, int userId)
throws SecurityException {
final int gameMode = getGameMode(packageName, userId);
if (gameMode == GameManager.GAME_MODE_UNSUPPORTED) {
@@ -1413,7 +1487,7 @@
if (applicationInfo.category != ApplicationInfo.CATEGORY_GAME) {
return;
}
- } catch (PackageManager.NameNotFoundException e) {
+ } catch (NameNotFoundException e) {
// Ignore the exception.
}
switch (intent.getAction()) {
diff --git a/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java b/services/core/java/com/android/server/app/GameServiceProviderInstanceImpl.java
index af1dd33..4eba771 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;
@@ -50,6 +51,7 @@
import com.android.internal.infra.AndroidFuture;
import com.android.internal.infra.ServiceConnector;
import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService;
import java.util.List;
@@ -62,6 +64,18 @@
private static final int CREATE_GAME_SESSION_TIMEOUT_MS = 10_000;
private static final boolean DEBUG = false;
+ private final TaskSystemBarsListener mTaskSystemBarsVisibilityListener =
+ new TaskSystemBarsListener() {
+ @Override
+ public void onTransientSystemBarsVisibilityChanged(
+ int taskId,
+ boolean visible,
+ boolean wereRevealedFromSwipeOnSystemBar) {
+ GameServiceProviderInstanceImpl.this.onTransientSystemBarsVisibilityChanged(
+ taskId, visible, wereRevealedFromSwipeOnSystemBar);
+ }
+ };
+
private final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
@@ -203,6 +217,8 @@
} catch (RemoteException e) {
Slog.w(TAG, "Failed to register task stack listener", e);
}
+
+ mWindowManagerInternal.registerTaskSystemBarsListener(mTaskSystemBarsVisibilityListener);
}
@GuardedBy("mLock")
@@ -218,8 +234,11 @@
Slog.w(TAG, "Failed to unregister task stack listener", e);
}
+ mWindowManagerInternal.unregisterTaskSystemBarsListener(
+ mTaskSystemBarsVisibilityListener);
+
for (GameSessionRecord gameSessionRecord : mGameSessions.values()) {
- destroyGameSessionFromRecord(gameSessionRecord);
+ destroyGameSessionFromRecordLocked(gameSessionRecord);
}
mGameSessions.clear();
@@ -307,6 +326,37 @@
}
}
+ private void onTransientSystemBarsVisibilityChanged(
+ int taskId,
+ boolean visible,
+ boolean wereRevealedFromSwipeOnSystemBar) {
+ if (visible && !wereRevealedFromSwipeOnSystemBar) {
+ return;
+ }
+
+ GameSessionRecord gameSessionRecord;
+ synchronized (mLock) {
+ gameSessionRecord = mGameSessions.get(taskId);
+ }
+
+ if (gameSessionRecord == null) {
+ return;
+ }
+
+ IGameSession gameSession = gameSessionRecord.getGameSession();
+ if (gameSession == null) {
+ return;
+ }
+
+ try {
+ gameSession.onTransientSystemBarVisibilityFromRevealGestureChanged(visible);
+ } catch (RemoteException ex) {
+ Slog.w(TAG,
+ "Failed to send transient system bars visibility from reveal gesture for task: "
+ + taskId);
+ }
+ }
+
private void createGameSession(int taskId) {
synchronized (mLock) {
createGameSessionLocked(taskId);
@@ -423,7 +473,7 @@
}
try {
- mWindowManagerInternal.addTaskOverlay(
+ mWindowManagerInternal.addTrustedTaskOverlay(
taskId,
createGameSessionResult.getSurfacePackage());
} catch (IllegalArgumentException ex) {
@@ -461,14 +511,15 @@
}
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 {
- mWindowManagerInternal.removeTaskOverlay(
+ mWindowManagerInternal.removeTrustedTaskOverlay(
gameSessionRecord.getTaskId(),
surfacePackage);
} catch (IllegalArgumentException ex) {
@@ -537,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/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
index 366718c..90aefe0 100644
--- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java
+++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java
@@ -35,6 +35,8 @@
import android.app.IActivityManager;
import android.app.StatsManager;
import android.app.StatsManager.StatsPullAtomCallback;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -78,6 +80,7 @@
import java.io.File;
import java.io.FileDescriptor;
+import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -111,6 +114,7 @@
private final PackageManagerInternal mPackageManagerInternal;
private final IActivityManager mIActivityManager;
private final UserManager mUserManager;
+ private final StorageStatsManager mStorageStatsManager;
@GuardedBy("mLock")
private final SparseArray<Map<String, UserLevelState>> mUserStates = new SparseArray<>();
@@ -147,6 +151,7 @@
mPackageManagerInternal = injector.getPackageManagerInternal();
mIActivityManager = injector.getActivityManager();
mUserManager = injector.getUserManager();
+ mStorageStatsManager = injector.getStorageStatsManager();
mGlobalLevelHibernationDiskStore = injector.getGlobalLevelDiskStore();
mBackgroundExecutor = injector.getBackgroundExecutor();
mOatArtifactDeletionEnabled = injector.isOatArtifactDeletionEnabled();
@@ -217,7 +222,7 @@
*/
boolean isHibernatingForUser(String packageName, int userId) {
String methodName = "isHibernatingForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return false;
}
getContext().enforceCallingOrSelfPermission(
@@ -230,8 +235,10 @@
}
final Map<String, UserLevelState> packageStates = mUserStates.get(userId);
final UserLevelState pkgState = packageStates.get(packageName);
- if (pkgState == null) {
- Slog.e(TAG, String.format("Package %s is not installed for user %s",
+ if (pkgState == null
+ || !mPackageManagerInternal.canQueryPackage(
+ Binder.getCallingUid(), packageName)) {
+ Slog.e(TAG, TextUtils.formatSimple("Package %s is not installed for user %s",
packageName, userId));
return false;
}
@@ -246,7 +253,7 @@
* @param packageName package to check
*/
boolean isHibernatingGlobally(String packageName) {
- if (!checkHibernationEnabled("isHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return false;
}
getContext().enforceCallingOrSelfPermission(
@@ -254,7 +261,9 @@
"Caller does not have MANAGE_APP_HIBERNATION permission.");
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
- if (state == null) {
+ if (state == null
+ || !mPackageManagerInternal.canQueryPackage(
+ Binder.getCallingUid(), packageName)) {
// This API can be legitimately called before installation finishes as part of
// dex optimization, so we just return false here.
return false;
@@ -272,7 +281,7 @@
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
String methodName = "setHibernatingForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -285,8 +294,10 @@
}
final Map<String, UserLevelState> packageStates = mUserStates.get(realUserId);
final UserLevelState pkgState = packageStates.get(packageName);
- if (pkgState == null) {
- Slog.e(TAG, String.format("Package %s is not installed for user %s",
+ if (pkgState == null
+ || !mPackageManagerInternal.canQueryPackage(
+ Binder.getCallingUid(), packageName)) {
+ Slog.e(TAG, TextUtils.formatSimple("Package %s is not installed for user %s",
packageName, realUserId));
return;
}
@@ -297,7 +308,8 @@
pkgState.hibernated = isHibernating;
if (isHibernating) {
- mBackgroundExecutor.execute(() -> hibernatePackageForUser(packageName, realUserId));
+ mBackgroundExecutor.execute(
+ () -> hibernatePackageForUser(packageName, realUserId, pkgState));
} else {
mBackgroundExecutor.execute(
() -> unhibernatePackageForUser(packageName, realUserId));
@@ -326,7 +338,7 @@
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
- if (!checkHibernationEnabled("setHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -334,8 +346,11 @@
"Caller does not have MANAGE_APP_HIBERNATION permission.");
synchronized (mLock) {
GlobalLevelState state = mGlobalHibernationStates.get(packageName);
- if (state == null) {
- Slog.e(TAG, String.format("Package %s is not installed for any user", packageName));
+ if (state == null
+ || !mPackageManagerInternal.canQueryPackage(
+ Binder.getCallingUid(), packageName)) {
+ Slog.e(TAG, TextUtils.formatSimple(
+ "Package %s is not installed for any user", packageName));
return;
}
if (state.hibernated != isHibernating) {
@@ -359,7 +374,7 @@
@NonNull List<String> getHibernatingPackagesForUser(int userId) {
ArrayList<String> hibernatingPackages = new ArrayList<>();
String methodName = "getHibernatingPackagesForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return hibernatingPackages;
}
getContext().enforceCallingOrSelfPermission(
@@ -372,6 +387,12 @@
}
Map<String, UserLevelState> userStates = mUserStates.get(userId);
for (UserLevelState state : userStates.values()) {
+ String packageName = state.packageName;
+ if (!mPackageManagerInternal.canQueryPackage(
+ Binder.getCallingUid(), packageName)) {
+ // Package is not visible to caller
+ continue;
+ }
if (state.hibernated) {
hibernatingPackages.add(state.packageName);
}
@@ -390,6 +411,9 @@
@Nullable Set<String> packageNames, int userId) {
Map<String, HibernationStats> statsMap = new ArrayMap<>();
String methodName = "getHibernationStatsForUser";
+ if (!sIsServiceEnabled) {
+ return statsMap;
+ }
getContext().enforceCallingOrSelfPermission(
android.Manifest.permission.MANAGE_APP_HIBERNATION,
"Caller does not have MANAGE_APP_HIBERNATION permission.");
@@ -412,8 +436,9 @@
+ "the package was uninstalled? ", pkgName, userId));
continue;
}
- HibernationStats stats = new HibernationStats(
- mGlobalHibernationStates.get(pkgName).savedByte);
+ long diskBytesSaved = mGlobalHibernationStates.get(pkgName).savedByte
+ + userPackageStates.get(pkgName).savedByte;
+ HibernationStats stats = new HibernationStats(diskBytesSaved);
statsMap.put(pkgName, stats);
}
}
@@ -424,16 +449,28 @@
* Put an app into hibernation for a given user, allowing user-level optimizations to occur. Do
* not hold {@link #mLock} while calling this to avoid deadlock scenarios.
*/
- private void hibernatePackageForUser(@NonNull String packageName, int userId) {
+ private void hibernatePackageForUser(@NonNull String packageName, int userId,
+ UserLevelState state) {
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "hibernatePackage");
final long caller = Binder.clearCallingIdentity();
try {
+ ApplicationInfo info = mIPackageManager.getApplicationInfo(
+ packageName, PACKAGE_MATCH_FLAGS, userId);
+ StorageStats stats = mStorageStatsManager.queryStatsForPackage(
+ info.storageUuid, packageName, new UserHandle(userId));
mIActivityManager.forceStopPackage(packageName, userId);
mIPackageManager.deleteApplicationCacheFilesAsUser(packageName, userId,
null /* observer */);
+ synchronized (mLock) {
+ state.savedByte = stats.getCacheBytes();
+ }
} catch (RemoteException e) {
throw new IllegalStateException(
"Failed to hibernate due to manager not being available", e);
+ } catch (PackageManager.NameNotFoundException e) {
+ Slog.e(TAG, "Package name not found when querying storage stats", e);
+ } catch (IOException e) {
+ Slog.e(TAG, "Storage device not found", e);
} finally {
Binder.restoreCallingIdentity(caller);
Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
@@ -677,6 +714,7 @@
for (String key : properties.getKeyset()) {
if (TextUtils.equals(KEY_APP_HIBERNATION_ENABLED, key)) {
sIsServiceEnabled = isDeviceConfigAppHibernationEnabled();
+ Slog.d(TAG, "App hibernation changed to enabled=" + sIsServiceEnabled);
break;
}
}
@@ -721,13 +759,6 @@
return true;
}
- private boolean checkHibernationEnabled(String methodName) {
- if (!sIsServiceEnabled) {
- Slog.w(TAG, String.format("Attempted to call %s on unsupported device.", methodName));
- }
- return sIsServiceEnabled;
- }
-
private void dump(PrintWriter pw) {
// Check usage stats permission since hibernation indirectly informs usage.
if (!DumpUtils.checkDumpAndUsageStatsPermission(getContext(), TAG, pw)) return;
@@ -923,6 +954,8 @@
UserManager getUserManager();
+ StorageStatsManager getStorageStatsManager();
+
Executor getBackgroundExecutor();
UsageStatsManagerInternal getUsageStatsManagerInternal();
@@ -972,6 +1005,11 @@
}
@Override
+ public StorageStatsManager getStorageStatsManager() {
+ return mContext.getSystemService(StorageStatsManager.class);
+ }
+
+ @Override
public Executor getBackgroundExecutor() {
return mScheduledExecutorService;
}
diff --git a/services/core/java/com/android/server/apphibernation/UserLevelState.java b/services/core/java/com/android/server/apphibernation/UserLevelState.java
index 68c363c..6a489d2 100644
--- a/services/core/java/com/android/server/apphibernation/UserLevelState.java
+++ b/services/core/java/com/android/server/apphibernation/UserLevelState.java
@@ -28,6 +28,8 @@
public String packageName;
public boolean hibernated;
+ // Saved bytes from user level hibernation.
+ public long savedByte;
@CurrentTimeMillisLong
public long lastUnhibernatedMs;
@@ -36,6 +38,7 @@
UserLevelState(UserLevelState state) {
packageName = state.packageName;
hibernated = state.hibernated;
+ savedByte = state.savedByte;
lastUnhibernatedMs = state.lastUnhibernatedMs;
}
@@ -44,6 +47,7 @@
return "UserLevelState{"
+ "packageName='" + packageName + '\''
+ ", hibernated=" + hibernated + '\''
+ + ", savedByte=" + savedByte + '\''
+ ", lastUnhibernated=" + DATE_FORMAT.format(lastUnhibernatedMs)
+ '}';
}
diff --git a/services/core/java/com/android/server/attention/AttentionManagerService.java b/services/core/java/com/android/server/attention/AttentionManagerService.java
index a139589..d2fa386 100644
--- a/services/core/java/com/android/server/attention/AttentionManagerService.java
+++ b/services/core/java/com/android/server/attention/AttentionManagerService.java
@@ -22,6 +22,7 @@
import static android.provider.DeviceConfig.NAMESPACE_ATTENTION_MANAGER_SERVICE;
import static android.service.attention.AttentionService.ATTENTION_FAILURE_CANCELLED;
import static android.service.attention.AttentionService.ATTENTION_FAILURE_UNKNOWN;
+import static android.service.attention.AttentionService.PROXIMITY_UNKNOWN;
import android.Manifest;
import android.annotation.NonNull;
@@ -29,6 +30,7 @@
import android.app.ActivityThread;
import android.attention.AttentionManagerInternal;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -57,6 +59,7 @@
import android.service.attention.AttentionService.AttentionSuccessCodes;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
+import android.service.attention.IProximityCallback;
import android.text.TextUtils;
import android.util.Slog;
@@ -134,6 +137,15 @@
@GuardedBy("mLock")
AttentionCheck mCurrentAttentionCheck;
+ /**
+ * A proxy for relaying proximity information between the Attention Service and the client.
+ * The proxy will be initialized when the client calls onStartProximityUpdates and will be
+ * disabled only when the client calls onStopProximityUpdates.
+ */
+ @VisibleForTesting
+ @GuardedBy("mLock")
+ ProximityUpdate mCurrentProximityUpdate;
+
public AttentionManagerService(Context context) {
this(context, (PowerManager) context.getSystemService(Context.POWER_SERVICE),
new Object(), null);
@@ -315,6 +327,77 @@
}
}
+ /**
+ * Requests the continuous updates of proximity signal via the provided callback,
+ * until the given callback is stopped.
+ *
+ * Calling this multiple times for duplicate requests will be no-ops, returning true.
+ *
+ * @return {@code true} if the framework was able to dispatch the request
+ */
+ @VisibleForTesting
+ boolean onStartProximityUpdates(ProximityCallbackInternal callbackInternal) {
+ Objects.requireNonNull(callbackInternal);
+ if (!mIsServiceEnabled) {
+ Slog.w(LOG_TAG, "Trying to call onProximityUpdate() on an unsupported device.");
+ return false;
+ }
+
+ if (!isServiceAvailable()) {
+ Slog.w(LOG_TAG, "Service is not available at this moment.");
+ return false;
+ }
+
+ // don't allow proximity request in screen off state.
+ // This behavior might change in the future.
+ if (!mPowerManager.isInteractive()) {
+ Slog.w(LOG_TAG, "Proximity Service is unavailable during screen off at this moment.");
+ return false;
+ }
+
+ synchronized (mLock) {
+ // schedule shutting down the connection if no one resets this timer
+ freeIfInactiveLocked();
+
+ // lazily start the service, which should be very lightweight to start
+ bindLocked();
+
+ /*
+ Prevent spamming with multiple requests, only one at a time is allowed.
+ If there are use-cases for keeping track of multiple requests, we
+ can refactor ProximityUpdate object to keep track of multiple internal callbacks.
+ */
+ if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
+ if (mCurrentProximityUpdate.mCallbackInternal == callbackInternal) {
+ Slog.w(LOG_TAG, "Provided callback is already registered. Skipping.");
+ return true;
+ } else {
+ // reject the new request since the old request is still alive.
+ Slog.w(LOG_TAG, "New proximity update cannot be processed because there is "
+ + "already an ongoing update");
+ return false;
+ }
+ }
+ mCurrentProximityUpdate = new ProximityUpdate(callbackInternal);
+ return mCurrentProximityUpdate.startUpdates();
+ }
+ }
+
+ /** Cancels the specified proximity registration. */
+ @VisibleForTesting
+ void onStopProximityUpdates(ProximityCallbackInternal callbackInternal) {
+ synchronized (mLock) {
+ if (mCurrentProximityUpdate == null
+ || !mCurrentProximityUpdate.mCallbackInternal.equals(callbackInternal)
+ || !mCurrentProximityUpdate.mStartedUpdates) {
+ Slog.w(LOG_TAG, "Cannot stop a non-current callback");
+ return;
+ }
+ mCurrentProximityUpdate.cancelUpdates();
+ mCurrentProximityUpdate = null;
+ }
+ }
+
@GuardedBy("mLock")
@VisibleForTesting
protected void freeIfInactiveLocked() {
@@ -390,15 +473,18 @@
ipw.println("Class=" + mComponentName.getClassName());
ipw.decreaseIndent();
}
- ipw.println("binding=" + mBinding);
- ipw.println("current attention check:");
synchronized (mLock) {
+ ipw.println("binding=" + mBinding);
+ ipw.println("current attention check:");
if (mCurrentAttentionCheck != null) {
mCurrentAttentionCheck.dump(ipw);
}
if (mAttentionCheckCacheBuffer != null) {
mAttentionCheckCacheBuffer.dump(ipw);
}
+ if (mCurrentProximityUpdate != null) {
+ mCurrentProximityUpdate.dump(ipw);
+ }
}
}
@@ -417,6 +503,17 @@
public void cancelAttentionCheck(AttentionCallbackInternal callbackInternal) {
AttentionManagerService.this.cancelAttentionCheck(callbackInternal);
}
+
+ @Override
+ public boolean onStartProximityUpdates(
+ ProximityCallbackInternal callback) {
+ return AttentionManagerService.this.onStartProximityUpdates(callback);
+ }
+
+ @Override
+ public void onStopProximityUpdates(ProximityCallbackInternal callback) {
+ AttentionManagerService.this.onStopProximityUpdates(callback);
+ }
}
@VisibleForTesting
@@ -536,6 +633,71 @@
}
}
+ @VisibleForTesting
+ final class ProximityUpdate {
+ private final ProximityCallbackInternal mCallbackInternal;
+ private final IProximityCallback mIProximityCallback;
+ private boolean mStartedUpdates;
+
+ ProximityUpdate(ProximityCallbackInternal callbackInternal) {
+ mCallbackInternal = callbackInternal;
+ mIProximityCallback = new IProximityCallback.Stub() {
+ @Override
+ public void onProximityUpdate(double distance) {
+ synchronized (mLock) {
+ mCallbackInternal.onProximityUpdate(distance);
+ freeIfInactiveLocked();
+ }
+ }
+ };
+ }
+
+ boolean startUpdates() {
+ synchronized (mLock) {
+ if (mStartedUpdates) {
+ Slog.w(LOG_TAG, "Already registered to a proximity service.");
+ return false;
+ }
+ if (mService == null) {
+ Slog.w(LOG_TAG,
+ "There is no service bound. Proximity update request rejected.");
+ return false;
+ }
+ try {
+ mService.onStartProximityUpdates(mIProximityCallback);
+ mStartedUpdates = true;
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ void cancelUpdates() {
+ synchronized (mLock) {
+ if (mStartedUpdates) {
+ if (mService == null) {
+ mStartedUpdates = false;
+ return;
+ }
+ try {
+ mService.onStopProximityUpdates();
+ mStartedUpdates = false;
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+ }
+ }
+ }
+ }
+
+ void dump(IndentingPrintWriter ipw) {
+ ipw.increaseIndent();
+ ipw.println("is StartedUpdates=" + mStartedUpdates);
+ ipw.decreaseIndent();
+ }
+ }
+
private void appendResultToAttentionCacheBuffer(AttentionCheckCache cache) {
synchronized (mLock) {
if (mAttentionCheckCacheBuffer == null) {
@@ -593,6 +755,18 @@
mCurrentAttentionCheck.mCallbackInternal.onFailure(ATTENTION_FAILURE_UNKNOWN);
}
}
+ if (mCurrentProximityUpdate != null && mCurrentProximityUpdate.mStartedUpdates) {
+ if (mService != null) {
+ try {
+ mService.onStartProximityUpdates(mCurrentProximityUpdate.mIProximityCallback);
+ } catch (RemoteException e) {
+ Slog.e(LOG_TAG, "Cannot call into the AttentionService", e);
+ }
+ } else {
+ mCurrentProximityUpdate.cancelUpdates();
+ mCurrentProximityUpdate = null;
+ }
+ }
}
@VisibleForTesting
@@ -609,7 +783,9 @@
switch (msg.what) {
// Do not occupy resources when not in use - unbind proactively.
case CHECK_CONNECTION_EXPIRATION: {
- cancelAndUnbindLocked();
+ synchronized (mLock) {
+ cancelAndUnbindLocked();
+ }
}
break;
@@ -653,10 +829,15 @@
@GuardedBy("mLock")
private void cancelAndUnbindLocked() {
synchronized (mLock) {
- if (mCurrentAttentionCheck == null) {
+ if (mCurrentAttentionCheck == null && mCurrentProximityUpdate == null) {
return;
}
- cancel();
+ if (mCurrentAttentionCheck != null) {
+ cancel();
+ }
+ if (mCurrentProximityUpdate != null) {
+ mCurrentProximityUpdate.cancelUpdates();
+ }
if (mService == null) {
return;
}
@@ -702,7 +883,9 @@
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())) {
- cancelAndUnbindLocked();
+ synchronized (mLock) {
+ cancelAndUnbindLocked();
+ }
}
}
}
@@ -730,8 +913,27 @@
}
}
+ class TestableProximityCallbackInternal extends ProximityCallbackInternal {
+ private double mLastCallbackCode = PROXIMITY_UNKNOWN;
+
+ @Override
+ public void onProximityUpdate(double distance) {
+ mLastCallbackCode = distance;
+ }
+
+ public void reset() {
+ mLastCallbackCode = PROXIMITY_UNKNOWN;
+ }
+
+ public double getLastCallbackCode() {
+ return mLastCallbackCode;
+ }
+ }
+
final TestableAttentionCallbackInternal mTestableAttentionCallback =
new TestableAttentionCallbackInternal();
+ final TestableProximityCallbackInternal mTestableProximityCallback =
+ new TestableProximityCallbackInternal();
@Override
public int onCommand(@Nullable final String cmd) {
@@ -749,6 +951,10 @@
return cmdCallCheckAttention();
case "cancelCheckAttention":
return cmdCallCancelAttention();
+ case "onStartProximityUpdates":
+ return cmdCallOnStartProximityUpdates();
+ case "onStopProximityUpdates":
+ return cmdCallOnStopProximityUpdates();
default:
throw new IllegalArgumentException("Invalid argument");
}
@@ -758,6 +964,8 @@
return cmdClearTestableAttentionService();
case "getLastTestCallbackCode":
return cmdGetLastTestCallbackCode();
+ case "getLastTestProximityCallbackCode":
+ return cmdGetLastTestProximityCallbackCode();
default:
return handleDefaultCommands(cmd);
}
@@ -782,6 +990,7 @@
private int cmdClearTestableAttentionService() {
sTestAttentionServicePackage = "";
mTestableAttentionCallback.reset();
+ mTestableProximityCallback.reset();
resetStates();
return 0;
}
@@ -800,6 +1009,20 @@
return 0;
}
+ private int cmdCallOnStartProximityUpdates() {
+ final PrintWriter out = getOutPrintWriter();
+ boolean calledSuccessfully = onStartProximityUpdates(mTestableProximityCallback);
+ out.println(calledSuccessfully ? "true" : "false");
+ return 0;
+ }
+
+ private int cmdCallOnStopProximityUpdates() {
+ final PrintWriter out = getOutPrintWriter();
+ onStopProximityUpdates(mTestableProximityCallback);
+ out.println("true");
+ return 0;
+ }
+
private int cmdResolveAttentionServiceComponent() {
final PrintWriter out = getOutPrintWriter();
ComponentName resolvedComponent = resolveAttentionService(mContext);
@@ -813,7 +1036,16 @@
return 0;
}
+ private int cmdGetLastTestProximityCallbackCode() {
+ final PrintWriter out = getOutPrintWriter();
+ out.println(mTestableProximityCallback.getLastCallbackCode());
+ return 0;
+ }
+
private void resetStates() {
+ synchronized (mLock) {
+ mCurrentProximityUpdate = null;
+ }
mComponentName = resolveAttentionService(mContext);
}
@@ -844,11 +1076,24 @@
+ " (to see the result, call getLastTestCallbackCode)");
out.println(" := false, otherwise");
out.println(" call cancelCheckAttention: Cancels check attention");
+ out.println(" call onStartProximityUpdates: Calls onStartProximityUpdates");
+ out.println(" ---returns:");
+ out.println(
+ " := true, if the request was successfully dispatched to the service "
+ + "implementation."
+ + " (to see the result, call getLastTestProximityCallbackCode)");
+ out.println(" := false, otherwise");
+ out.println(" call onStopProximityUpdates: Cancels proximity updates");
out.println(" getLastTestCallbackCode");
out.println(" ---returns:");
out.println(
" := An integer, representing the last callback code received from the "
+ "bounded implementation. If none, it will return -1");
+ out.println(" getLastTestProximityCallbackCode");
+ out.println(" ---returns:");
+ out.println(
+ " := A double, representing the last proximity value received from the "
+ + "bounded implementation. If none, it will return -1.0");
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceBroker.java b/services/core/java/com/android/server/audio/AudioDeviceBroker.java
index 65be5f0..daf3561 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;
@@ -500,12 +500,11 @@
}
}
- /*package*/ void setWiredDeviceConnectionState(int type,
- @AudioService.ConnectionState int state, String address, String name,
- String caller) {
+ /*package*/ void setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
+ @AudioService.ConnectionState int state, String caller) {
//TODO move logging here just like in setBluetooth* methods
synchronized (mDeviceStateLock) {
- mDeviceInventory.setWiredDeviceConnectionState(type, state, address, name, caller);
+ mDeviceInventory.setWiredDeviceConnectionState(attributes, state, caller);
}
}
@@ -531,12 +530,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 +567,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 +640,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;
@@ -1013,11 +1012,9 @@
}
}
- /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
- String deviceName) {
+ /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect) {
synchronized (mDeviceStateLock) {
- return mDeviceInventory.handleDeviceConnection(connect, device, address, deviceName,
- false /*for test*/);
+ return mDeviceInventory.handleDeviceConnection(attributes, connect, false /*for test*/);
}
}
diff --git a/services/core/java/com/android/server/audio/AudioDeviceInventory.java b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
index 4ae1bd37..0e29041 100644
--- a/services/core/java/com/android/server/audio/AudioDeviceInventory.java
+++ b/services/core/java/com/android/server/audio/AudioDeviceInventory.java
@@ -230,19 +230,15 @@
* A class just for packaging up a set of connection parameters.
*/
/*package*/ class WiredDeviceConnectionState {
- public final int mType;
+ public final AudioDeviceAttributes mAttributes;
public final @AudioService.ConnectionState int mState;
- public final String mAddress;
- public final String mName;
public final String mCaller;
public boolean mForTest = false;
- /*package*/ WiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
- String address, String name, String caller) {
- mType = type;
+ /*package*/ WiredDeviceConnectionState(AudioDeviceAttributes attributes,
+ @AudioService.ConnectionState int state, String caller) {
+ mAttributes = attributes;
mState = state;
- mAddress = address;
- mName = name;
mCaller = caller;
}
}
@@ -280,11 +276,10 @@
synchronized (mDevicesLock) {
//TODO iterate on mApmConnectedDevices instead once it handles all device types
for (DeviceInfo di : mConnectedDevices.values()) {
- mAudioSystem.setDeviceConnectionState(
- di.mDeviceType,
- AudioSystem.DEVICE_STATE_AVAILABLE,
+ mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(di.mDeviceType,
di.mDeviceAddress,
- di.mDeviceName,
+ di.mDeviceName),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
di.mDeviceCodecFormat);
}
}
@@ -519,41 +514,45 @@
/*package*/ void onSetWiredDeviceConnectionState(
AudioDeviceInventory.WiredDeviceConnectionState wdcs) {
+ int type = wdcs.mAttributes.getInternalType();
+
AudioService.sDeviceLogger.log(new AudioServiceEvents.WiredDevConnectEvent(wdcs));
MediaMetrics.Item mmi = new MediaMetrics.Item(mMetricsId
+ "onSetWiredDeviceConnectionState")
- .set(MediaMetrics.Property.ADDRESS, wdcs.mAddress)
- .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(wdcs.mType))
+ .set(MediaMetrics.Property.ADDRESS, wdcs.mAttributes.getAddress())
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(type))
.set(MediaMetrics.Property.STATE,
wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED
? MediaMetrics.Value.DISCONNECTED : MediaMetrics.Value.CONNECTED);
synchronized (mDevicesLock) {
if ((wdcs.mState == AudioService.CONNECTION_STATE_DISCONNECTED)
- && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
+ && DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) {
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/,
"onSetWiredDeviceConnectionState state DISCONNECTED");
}
- if (!handleDeviceConnection(wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED,
- wdcs.mType, wdcs.mAddress, wdcs.mName, wdcs.mForTest)) {
+ if (!handleDeviceConnection(wdcs.mAttributes,
+ wdcs.mState == AudioService.CONNECTION_STATE_CONNECTED, wdcs.mForTest)) {
// change of connection state failed, bailout
mmi.set(MediaMetrics.Property.EARLY_RETURN, "change of connection state failed")
.record();
return;
}
if (wdcs.mState != AudioService.CONNECTION_STATE_DISCONNECTED) {
- if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(wdcs.mType)) {
+ if (DEVICE_OVERRIDE_A2DP_ROUTE_ON_PLUG_SET.contains(type)) {
mDeviceBroker.setBluetoothA2dpOnInt(false, false /*fromA2dp*/,
"onSetWiredDeviceConnectionState state not DISCONNECTED");
}
- mDeviceBroker.checkMusicActive(wdcs.mType, wdcs.mCaller);
+ mDeviceBroker.checkMusicActive(type, wdcs.mCaller);
}
- if (wdcs.mType == AudioSystem.DEVICE_OUT_HDMI) {
+ if (type == AudioSystem.DEVICE_OUT_HDMI) {
mDeviceBroker.checkVolumeCecOnHdmiConnection(wdcs.mState, wdcs.mCaller);
}
- sendDeviceConnectionIntent(wdcs.mType, wdcs.mState, wdcs.mAddress, wdcs.mName);
- updateAudioRoutes(wdcs.mType, wdcs.mState);
+ sendDeviceConnectionIntent(type, wdcs.mState,
+ wdcs.mAttributes.getAddress(), wdcs.mAttributes.getName());
+ updateAudioRoutes(type, wdcs.mState);
}
mmi.record();
}
@@ -572,12 +571,12 @@
return;
}
// Toggle HDMI to retrigger broadcast with proper formats.
- setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, "", "",
- "android"); // disconnect
- setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_HDMI,
- AudioSystem.DEVICE_STATE_AVAILABLE, "", "",
- "android"); // reconnect
+ setWiredDeviceConnectionState(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, "android"); // disconnect
+ setWiredDeviceConnectionState(
+ new AudioDeviceAttributes(AudioSystem.DEVICE_OUT_HDMI, ""),
+ AudioSystem.DEVICE_STATE_AVAILABLE, "android"); // reconnect
}
mmi.record();
}
@@ -707,16 +706,17 @@
/**
* Implements the communication with AudioSystem to (dis)connect a device in the native layers
+ * @param attributes the attributes of the device
* @param connect true if connection
- * @param device the device type
- * @param address the address of the device
- * @param deviceName human-readable name of device
* @param isForTesting if true, not calling AudioSystem for the connection as this is
* just for testing
* @return false if an error was reported by AudioSystem
*/
- /*package*/ boolean handleDeviceConnection(boolean connect, int device, String address,
- String deviceName, boolean isForTesting) {
+ /*package*/ boolean handleDeviceConnection(AudioDeviceAttributes attributes, boolean connect,
+ boolean isForTesting) {
+ int device = attributes.getInternalType();
+ String address = attributes.getAddress();
+ String deviceName = attributes.getName();
if (AudioService.DEBUG_DEVICES) {
Slog.i(TAG, "handleDeviceConnection(" + connect + " dev:"
+ Integer.toHexString(device) + " address:" + address
@@ -743,9 +743,8 @@
if (isForTesting) {
res = AudioSystem.AUDIO_STATUS_OK;
} else {
- res = mAudioSystem.setDeviceConnectionState(device,
- AudioSystem.DEVICE_STATE_AVAILABLE, address, deviceName,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ res = mAudioSystem.setDeviceConnectionState(attributes,
+ AudioSystem.DEVICE_STATE_AVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
}
if (res != AudioSystem.AUDIO_STATUS_OK) {
final String reason = "not connecting device 0x" + Integer.toHexString(device)
@@ -762,9 +761,8 @@
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
return true;
} else if (!connect && isConnected) {
- mAudioSystem.setDeviceConnectionState(device,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, address, deviceName,
- AudioSystem.AUDIO_FORMAT_DEFAULT);
+ mAudioSystem.setDeviceConnectionState(attributes,
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, AudioSystem.AUDIO_FORMAT_DEFAULT);
// always remove even if disconnection failed
mConnectedDevices.remove(deviceKey);
mmi.set(MediaMetrics.Property.STATE, MediaMetrics.Value.CONNECTED).record();
@@ -941,13 +939,13 @@
return delay;
}
- /*package*/ int setWiredDeviceConnectionState(int type, @AudioService.ConnectionState int state,
- String address, String name, String caller) {
+ /*package*/ int setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
+ @AudioService.ConnectionState int state, String caller) {
synchronized (mDevicesLock) {
- int delay = checkSendBecomingNoisyIntentInt(type, state, AudioSystem.DEVICE_NONE);
+ int delay = checkSendBecomingNoisyIntentInt(
+ attributes.getInternalType(), state, AudioSystem.DEVICE_NONE);
mDeviceBroker.postSetWiredDeviceConnectionState(
- new WiredDeviceConnectionState(type, state, address, name, caller),
- delay);
+ new WiredDeviceConnectionState(attributes, state, caller), delay);
return delay;
}
}
@@ -955,8 +953,7 @@
/*package*/ void setTestDeviceConnectionState(@NonNull AudioDeviceAttributes device,
@AudioService.ConnectionState int state) {
final WiredDeviceConnectionState connection = new WiredDeviceConnectionState(
- device.getInternalType(), state, device.getAddress(),
- "test device", "com.android.server.audio");
+ device, state, "com.android.server.audio");
connection.mForTest = true;
onSetWiredDeviceConnectionState(connection);
}
@@ -972,8 +969,9 @@
mDeviceBroker.setBluetoothA2dpOnInt(true, true /*fromA2dp*/, eventSource);
// at this point there could be another A2DP device already connected in APM, but it
// doesn't matter as this new one will overwrite the previous one
- final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- AudioSystem.DEVICE_STATE_AVAILABLE, address, name, a2dpCodec);
+ final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address, name),
+ AudioSystem.DEVICE_STATE_AVAILABLE, a2dpCodec);
// TODO: log in MediaMetrics once distinction between connection failure and
// double connection is made.
@@ -1035,8 +1033,9 @@
// device to remove was visible by APM, update APM
mDeviceBroker.clearAvrcpAbsoluteVolumeSupported();
- final int res = mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "", a2dpCodec);
+ final int res = mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, address),
+ AudioSystem.DEVICE_STATE_UNAVAILABLE, a2dpCodec);
if (res != AudioSystem.AUDIO_STATUS_OK) {
AudioService.sDeviceLogger.log(new AudioEventLogger.StringEvent(
@@ -1074,8 +1073,9 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcAvailable(String address) {
- mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
- AudioSystem.DEVICE_STATE_AVAILABLE, address, "",
+ mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
@@ -1085,8 +1085,9 @@
@GuardedBy("mDevicesLock")
private void makeA2dpSrcUnavailable(String address) {
- mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
+ mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address),
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_IN_BLUETOOTH_A2DP, address));
@@ -1099,8 +1100,9 @@
AudioSystem.DEVICE_OUT_HEARING_AID);
mDeviceBroker.postSetHearingAidVolumeIndex(hearingAidVolIndex, streamType);
- mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
- AudioSystem.DEVICE_STATE_AVAILABLE, address, name,
+ mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_HEARING_AID, address, name),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address),
@@ -1122,8 +1124,9 @@
@GuardedBy("mDevicesLock")
private void makeHearingAidDeviceUnavailable(String address) {
- mAudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_HEARING_AID,
- AudioSystem.DEVICE_STATE_UNAVAILABLE, address, "",
+ mAudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_HEARING_AID, address),
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(
DeviceInfo.makeDeviceListKey(AudioSystem.DEVICE_OUT_HEARING_AID, address));
@@ -1140,14 +1143,14 @@
private void makeLeAudioDeviceAvailable(String address, String name, int streamType, int device,
String eventSource) {
if (device != AudioSystem.DEVICE_NONE) {
-
/* Audio Policy sees Le Audio similar to A2DP. Let's make sure
* AUDIO_POLICY_FORCE_NO_BT_A2DP is not set
*/
mDeviceBroker.setBluetoothA2dpOnInt(true, false /*fromA2dp*/, eventSource);
- AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_AVAILABLE,
- address, name, AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address, name),
+ AudioSystem.DEVICE_STATE_AVAILABLE,
+ AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.put(DeviceInfo.makeDeviceListKey(device, address),
new DeviceInfo(device, name, address, AudioSystem.AUDIO_FORMAT_DEFAULT));
mDeviceBroker.postAccessoryPlugMediaUnmute(device);
@@ -1168,8 +1171,9 @@
@GuardedBy("mDevicesLock")
private void makeLeAudioDeviceUnavailable(String address, int device) {
if (device != AudioSystem.DEVICE_NONE) {
- AudioSystem.setDeviceConnectionState(device, AudioSystem.DEVICE_STATE_UNAVAILABLE,
- address, "", AudioSystem.AUDIO_FORMAT_DEFAULT);
+ AudioSystem.setDeviceConnectionState(new AudioDeviceAttributes(device, address),
+ AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ AudioSystem.AUDIO_FORMAT_DEFAULT);
mConnectedDevices.remove(DeviceInfo.makeDeviceListKey(device, address));
}
diff --git a/services/core/java/com/android/server/audio/AudioService.java b/services/core/java/com/android/server/audio/AudioService.java
index 407d42e..05955c3 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;
@@ -6371,23 +6371,23 @@
/**
* see AudioManager.setWiredDeviceConnectionState()
*/
- public void setWiredDeviceConnectionState(int type,
- @ConnectionState int state, String address, String name,
- String caller) {
+ public void setWiredDeviceConnectionState(AudioDeviceAttributes attributes,
+ @ConnectionState int state, String caller) {
enforceModifyAudioRoutingPermission();
if (state != CONNECTION_STATE_CONNECTED
&& state != CONNECTION_STATE_DISCONNECTED) {
throw new IllegalArgumentException("Invalid state " + state);
}
new MediaMetrics.Item(mMetricsId + "setWiredDeviceConnectionState")
- .set(MediaMetrics.Property.ADDRESS, address)
+ .set(MediaMetrics.Property.ADDRESS, attributes.getAddress())
.set(MediaMetrics.Property.CLIENT_NAME, caller)
- .set(MediaMetrics.Property.DEVICE, AudioSystem.getDeviceName(type))
- .set(MediaMetrics.Property.NAME, name)
+ .set(MediaMetrics.Property.DEVICE,
+ AudioSystem.getDeviceName(attributes.getInternalType()))
+ .set(MediaMetrics.Property.NAME, attributes.getName())
.set(MediaMetrics.Property.STATE,
state == CONNECTION_STATE_CONNECTED ? "connected" : "disconnected")
.record();
- mDeviceBroker.setWiredDeviceConnectionState(type, state, address, name, caller);
+ mDeviceBroker.setWiredDeviceConnectionState(attributes, state, caller);
}
/** @see AudioManager#setTestDeviceConnectionState(AudioDeviceAttributes, boolean) */
@@ -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/AudioServiceEvents.java b/services/core/java/com/android/server/audio/AudioServiceEvents.java
index 3137fa5..3225274 100644
--- a/services/core/java/com/android/server/audio/AudioServiceEvents.java
+++ b/services/core/java/com/android/server/audio/AudioServiceEvents.java
@@ -116,10 +116,11 @@
@Override
public String eventToString() {
return new StringBuilder("setWiredDeviceConnectionState(")
- .append(" type:").append(Integer.toHexString(mState.mType))
+ .append(" type:").append(
+ Integer.toHexString(mState.mAttributes.getInternalType()))
.append(" state:").append(AudioSystem.deviceStateToString(mState.mState))
- .append(" addr:").append(mState.mAddress)
- .append(" name:").append(mState.mName)
+ .append(" addr:").append(mState.mAttributes.getAddress())
+ .append(" name:").append(mState.mAttributes.getName())
.append(") from ").append(mState.mCaller).toString();
}
}
diff --git a/services/core/java/com/android/server/audio/AudioSystemAdapter.java b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
index a2ba76b..f572261 100644
--- a/services/core/java/com/android/server/audio/AudioSystemAdapter.java
+++ b/services/core/java/com/android/server/audio/AudioSystemAdapter.java
@@ -258,19 +258,16 @@
}
/**
- * Same as {@link AudioSystem#setDeviceConnectionState(int, int, String, String, int)}
- * @param device
+ * Same as {@link AudioSystem#setDeviceConnectionState(AudioDeviceAttributes, int, int)}
+ * @param attributes
* @param state
- * @param deviceAddress
- * @param deviceName
* @param codecFormat
* @return
*/
- public int setDeviceConnectionState(int device, int state, String deviceAddress,
- String deviceName, int codecFormat) {
+ public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state,
+ int codecFormat) {
invalidateRoutingCache();
- return AudioSystem.setDeviceConnectionState(device, state, deviceAddress, deviceName,
- codecFormat);
+ return AudioSystem.setDeviceConnectionState(attributes, state, codecFormat);
}
/**
diff --git a/services/core/java/com/android/server/audio/BtHelper.java b/services/core/java/com/android/server/audio/BtHelper.java
index 47f31d5..3491cd5 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
@@ -587,8 +595,9 @@
String btDeviceName = getName(btDevice);
boolean result = false;
if (isActive) {
- result |= mDeviceBroker.handleDeviceConnection(isActive, audioDevice.getInternalType(),
- audioDevice.getAddress(), btDeviceName);
+ result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
+ audioDevice.getInternalType(), audioDevice.getAddress(), btDeviceName),
+ isActive);
} else {
int[] outDeviceTypes = {
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO,
@@ -596,13 +605,15 @@
AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT
};
for (int outDeviceType : outDeviceTypes) {
- result |= mDeviceBroker.handleDeviceConnection(
- isActive, outDeviceType, audioDevice.getAddress(), btDeviceName);
+ result |= mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
+ outDeviceType, audioDevice.getAddress(), btDeviceName),
+ isActive);
}
}
// handleDeviceConnection() && result to make sure the method get executed
- result = mDeviceBroker.handleDeviceConnection(
- isActive, inDevice, audioDevice.getAddress(), btDeviceName) && result;
+ result = mDeviceBroker.handleDeviceConnection(new AudioDeviceAttributes(
+ inDevice, audioDevice.getAddress(), btDeviceName),
+ isActive) && result;
return result;
}
diff --git a/services/core/java/com/android/server/camera/CameraServiceProxy.java b/services/core/java/com/android/server/camera/CameraServiceProxy.java
index 1b0341c..e0b768d 100644
--- a/services/core/java/com/android/server/camera/CameraServiceProxy.java
+++ b/services/core/java/com/android/server/camera/CameraServiceProxy.java
@@ -808,6 +808,7 @@
streamProtos[i].histogramBins = streamStats.getHistogramBins();
streamProtos[i].histogramCounts = streamStats.getHistogramCounts();
streamProtos[i].dynamicRangeProfile = streamStats.getDynamicRangeProfile();
+ streamProtos[i].streamUseCase = streamStats.getStreamUseCase();
if (CameraServiceProxy.DEBUG) {
String histogramTypeName =
@@ -828,7 +829,8 @@
+ Arrays.toString(streamProtos[i].histogramBins)
+ ", histogramCounts "
+ Arrays.toString(streamProtos[i].histogramCounts)
- + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile);
+ + ", dynamicRangeProfile " + streamProtos[i].dynamicRangeProfile
+ + ", streamUseCase " + streamProtos[i].streamUseCase);
}
}
}
diff --git a/services/core/java/com/android/server/clipboard/ClipboardService.java b/services/core/java/com/android/server/clipboard/ClipboardService.java
index 28508f4..eb2f80b 100644
--- a/services/core/java/com/android/server/clipboard/ClipboardService.java
+++ b/services/core/java/com/android/server/clipboard/ClipboardService.java
@@ -722,7 +722,7 @@
clipboard.primaryClipListeners.getBroadcastItem(i)
.dispatchPrimaryClipChanged();
}
- } catch (RemoteException e) {
+ } catch (RemoteException | SecurityException e) {
// The RemoteCallbackList will take care of removing
// the dead object for us.
}
@@ -1090,6 +1090,10 @@
&& mAutofillInternal.isAugmentedAutofillServiceForUser(uid, userId)) {
return;
}
+ if (mPm.checkPermission(Manifest.permission.SUPPRESS_CLIPBOARD_ACCESS_NOTIFICATION,
+ callingPackage) == PackageManager.PERMISSION_GRANTED) {
+ return;
+ }
// Don't notify if already notified for this uid and clip.
if (clipboard.mNotifiedUids.get(uid)) {
return;
diff --git a/services/core/java/com/android/server/display/AutomaticBrightnessController.java b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
index 1b55257..162eb0e 100644
--- a/services/core/java/com/android/server/display/AutomaticBrightnessController.java
+++ b/services/core/java/com/android/server/display/AutomaticBrightnessController.java
@@ -201,6 +201,10 @@
// Controls High Brightness Mode.
private HighBrightnessModeController mHbmController;
+ // Throttles (caps) maximum allowed brightness
+ private BrightnessThrottler mBrightnessThrottler;
+ private boolean mIsBrightnessThrottled;
+
// Context-sensitive brightness configurations require keeping track of the foreground app's
// package name and category, which is done by registering a TaskStackListener to call back to
// us onTaskStackChanged, and then using the ActivityTaskManager to get the foreground app's
@@ -226,7 +230,7 @@
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController,
+ HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
this(new Injector(), callbacks, looper, sensorManager, lightSensor,
@@ -235,8 +239,8 @@
lightSensorRate, initialLightSensorRate, brighteningLightDebounceConfig,
darkeningLightDebounceConfig, resetAmbientLuxAfterWarmUpConfig,
ambientBrightnessThresholds, screenBrightnessThresholds, context,
- hbmController, idleModeBrightnessMapper, ambientLightHorizonShort,
- ambientLightHorizonLong
+ hbmController, brightnessThrottler, idleModeBrightnessMapper,
+ ambientLightHorizonShort, ambientLightHorizonLong
);
}
@@ -249,7 +253,7 @@
long brighteningLightDebounceConfig, long darkeningLightDebounceConfig,
boolean resetAmbientLuxAfterWarmUpConfig, HysteresisLevels ambientBrightnessThresholds,
HysteresisLevels screenBrightnessThresholds, Context context,
- HighBrightnessModeController hbmController,
+ HighBrightnessModeController hbmController, BrightnessThrottler brightnessThrottler,
BrightnessMappingStrategy idleModeBrightnessMapper, int ambientLightHorizonShort,
int ambientLightHorizonLong) {
mInjector = injector;
@@ -291,6 +295,7 @@
mForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mPendingForegroundAppCategory = ApplicationInfo.CATEGORY_UNDEFINED;
mHbmController = hbmController;
+ mBrightnessThrottler = brightnessThrottler;
mInteractiveModeBrightnessMapper = interactiveModeBrightnessMapper;
mIdleModeBrightnessMapper = idleModeBrightnessMapper;
// Initialize to active (normal) screen brightness mode
@@ -365,6 +370,13 @@
prepareBrightnessAdjustmentSample();
}
changed |= setLightSensorEnabled(enable && !dozing);
+
+ if (mIsBrightnessThrottled != mBrightnessThrottler.isThrottled()) {
+ // Maximum brightness has changed, so recalculate display brightness.
+ mIsBrightnessThrottled = mBrightnessThrottler.isThrottled();
+ changed = true;
+ }
+
if (changed) {
updateAutoBrightness(false /*sendUpdate*/, userInitiatedChange);
}
@@ -855,8 +867,11 @@
// Clamps values with float range [0.0-1.0]
private float clampScreenBrightness(float value) {
- return MathUtils.constrain(value,
- mHbmController.getCurrentBrightnessMin(), mHbmController.getCurrentBrightnessMax());
+ final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+ mBrightnessThrottler.getBrightnessCap());
+ final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+ mBrightnessThrottler.getBrightnessCap());
+ return MathUtils.constrain(value, minBrightness, maxBrightness);
}
private void prepareBrightnessAdjustmentSample() {
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 3494342..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);
@@ -974,7 +981,7 @@
lightSensorRate, initialLightSensorRate, brighteningLightDebounce,
darkeningLightDebounce, autoBrightnessResetAmbientLuxAfterWarmUp,
ambientBrightnessThresholds, screenBrightnessThresholds, mContext,
- mHbmController, mIdleModeBrightnessMapper,
+ mHbmController, mBrightnessThrottler, mIdleModeBrightnessMapper,
mDisplayDeviceConfig.getAmbientHorizonShort(),
mDisplayDeviceConfig.getAmbientHorizonLong());
} else {
@@ -1347,6 +1354,29 @@
mBrightnessReasonTemp.setReason(BrightnessReason.REASON_MANUAL);
}
+ // Now that a desired brightness has been calculated, apply brightness throttling. The
+ // dimming and low power transformations that follow can only dim brightness further.
+ //
+ // We didn't do this earlier through brightness clamping because we need to know both
+ // unthrottled (unclamped/ideal) and throttled brightness levels for subsequent operations.
+ // Note throttling effectively changes the allowed brightness range, so, similarly to HBM,
+ // we broadcast this change through setting.
+ final float unthrottledBrightnessState = brightnessState;
+ if (mBrightnessThrottler.isThrottled()) {
+ brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
+ mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
+ if (!mAppliedThrottling) {
+ // Brightness throttling is needed, so do so quickly.
+ // Later, when throttling is removed, we let other mechanisms decide on speed.
+ slowChange = false;
+ updateScreenBrightnessSetting = true;
+ }
+ mAppliedThrottling = true;
+ } else if (mAppliedThrottling) {
+ mAppliedThrottling = false;
+ updateScreenBrightnessSetting = true;
+ }
+
if (updateScreenBrightnessSetting) {
// Tell the rest of the system about the new brightness in case we had to change it
// for things like auto-brightness or high-brightness-mode. Note that we do this
@@ -1393,20 +1423,6 @@
mAppliedLowPower = false;
}
- // Apply brightness throttling after applying all other transforms
- final float unthrottledBrightnessState = brightnessState;
- if (mBrightnessThrottler.isThrottled()) {
- brightnessState = Math.min(brightnessState, mBrightnessThrottler.getBrightnessCap());
- mBrightnessReasonTemp.addModifier(BrightnessReason.MODIFIER_THROTTLED);
- if (!mAppliedThrottling) {
- slowChange = false;
- }
- mAppliedThrottling = true;
- } else if (mAppliedThrottling) {
- slowChange = false;
- mAppliedThrottling = false;
- }
-
// The current brightness to use has been calculated at this point, and HbmController should
// be notified so that it can accurately calculate HDR or HBM levels. We specifically do it
// here instead of having HbmController listen to the brightness setting because certain
@@ -1656,6 +1672,10 @@
private boolean saveBrightnessInfo(float brightness, float adjustedBrightness) {
synchronized (mCachedBrightnessInfo) {
+ final float minBrightness = Math.min(mHbmController.getCurrentBrightnessMin(),
+ mBrightnessThrottler.getBrightnessCap());
+ final float maxBrightness = Math.min(mHbmController.getCurrentBrightnessMax(),
+ mBrightnessThrottler.getBrightnessCap());
boolean changed = false;
changed |=
@@ -1666,10 +1686,10 @@
adjustedBrightness);
changed |=
mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMin,
- mHbmController.getCurrentBrightnessMin());
+ minBrightness);
changed |=
mCachedBrightnessInfo.checkAndSetFloat(mCachedBrightnessInfo.brightnessMax,
- mHbmController.getCurrentBrightnessMax());
+ maxBrightness);
changed |=
mCachedBrightnessInfo.checkAndSetInt(mCachedBrightnessInfo.hbmMode,
mHbmController.getHighBrightnessMode());
@@ -1700,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 65ec1c0..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,
@@ -385,6 +393,16 @@
R.bool.config_cecTvSendStandbyOnSleepDisabled_allowed,
R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
+ Setting setMenuLanguage = registerSetting(
+ HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
+ R.bool.config_cecSetMenuLanguage_userConfigurable);
+ setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_ENABLED,
+ R.bool.config_cecSetMenuLanguageEnabled_allowed,
+ R.bool.config_cecSetMenuLanguageEnabled_default);
+ setMenuLanguage.registerValue(HdmiControlManager.SET_MENU_LANGUAGE_DISABLED,
+ R.bool.config_cecSetMenuLanguageDisabled_allowed,
+ R.bool.config_cecSetMenuLanguageDisabled_default);
+
Setting rcProfileTv = registerSetting(
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
R.bool.config_cecRcProfileTv_userConfigurable);
@@ -697,6 +715,8 @@
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
return STORAGE_SHARED_PREFS;
+ case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
+ return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
return STORAGE_SHARED_PREFS;
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
@@ -768,6 +788,8 @@
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP:
return setting.getName();
+ case HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE:
+ return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV:
return setting.getName();
case HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU:
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
index c674ffe..454a76a 100755
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevice.java
@@ -901,17 +901,12 @@
protected int handleVendorCommandWithId(HdmiCecMessage message) {
byte[] params = message.getParams();
int vendorId = HdmiUtils.threeBytesToInt(params);
- if (vendorId == mService.getVendorId()) {
- if (!mService.invokeVendorCommandListenersOnReceived(
- mDeviceType, message.getSource(), message.getDestination(), params, true)) {
- return Constants.ABORT_REFUSED;
- }
- } else if (message.getDestination() != Constants.ADDR_BROADCAST
- && message.getSource() != Constants.ADDR_UNREGISTERED) {
- Slog.v(TAG, "Wrong direct vendor command. Replying with <Feature Abort>");
- return Constants.ABORT_UNRECOGNIZED_OPCODE;
- } else {
+ if (message.getDestination() == Constants.ADDR_BROADCAST
+ || message.getSource() == Constants.ADDR_UNREGISTERED) {
Slog.v(TAG, "Wrong broadcast vendor command. Ignoring");
+ } else if (!mService.invokeVendorCommandListenersOnReceived(
+ mDeviceType, message.getSource(), message.getDestination(), params, true)) {
+ return Constants.ABORT_REFUSED;
}
return Constants.HANDLED;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
index f413fbd..0edcea5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -50,9 +50,6 @@
public class HdmiCecLocalDevicePlayback extends HdmiCecLocalDeviceSource {
private static final String TAG = "HdmiCecLocalDevicePlayback";
- private static final boolean SET_MENU_LANGUAGE =
- HdmiProperties.set_menu_language_enabled().orElse(false);
-
// How long to wait after hotplug out before possibly going to Standby.
@VisibleForTesting
static final long STANDBY_AFTER_HOTPLUG_OUT_DELAY_MS = 30_000;
@@ -388,7 +385,9 @@
@Constants.HandleMessageResult
protected int handleSetMenuLanguage(HdmiCecMessage message) {
assertRunOnServiceThread();
- if (!SET_MENU_LANGUAGE) {
+ if (mService.getHdmiCecConfig().getIntValue(
+ HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE)
+ == HdmiControlManager.SET_MENU_LANGUAGE_DISABLED) {
return Constants.ABORT_UNRECOGNIZED_OPCODE;
}
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
index afcd3dd..1ce36b1 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDeviceTv.java
@@ -41,7 +41,10 @@
import android.hardware.hdmi.HdmiTimerRecordSources;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
-import android.media.AudioSystem;
+import android.media.AudioDescriptor;
+import android.media.AudioDeviceAttributes;
+import android.media.AudioDeviceInfo;
+import android.media.AudioProfile;
import android.media.tv.TvInputInfo;
import android.media.tv.TvInputManager.TvInputCallback;
import android.util.Slog;
@@ -58,6 +61,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
+import java.util.stream.Collectors;
/**
* Represent a logical device of type TV residing in Android system.
@@ -816,12 +820,23 @@
HdmiLogger.debug("Set Arc Status[old:%b new:%b]", mArcEstablished, enabled);
boolean oldStatus = mArcEstablished;
- // 1. Enable/disable ARC circuit.
- enableAudioReturnChannel(enabled);
- // 2. Notify arc status to audio service.
- notifyArcStatusToAudioService(enabled);
- // 3. Update arc status;
- mArcEstablished = enabled;
+ if (enabled) {
+ RequestSadAction action = new RequestSadAction(
+ this, Constants.ADDR_AUDIO_SYSTEM,
+ new RequestSadAction.RequestSadCallback() {
+ @Override
+ public void onRequestSadDone(List<byte[]> supportedSads) {
+ enableAudioReturnChannel(enabled);
+ notifyArcStatusToAudioService(enabled, supportedSads);
+ mArcEstablished = enabled;
+ }
+ });
+ addAndStartAction(action);
+ } else {
+ enableAudioReturnChannel(enabled);
+ notifyArcStatusToAudioService(enabled, new ArrayList<>());
+ mArcEstablished = enabled;
+ }
return oldStatus;
}
@@ -843,11 +858,15 @@
return mService.isConnected(portId);
}
- private void notifyArcStatusToAudioService(boolean enabled) {
+ private void notifyArcStatusToAudioService(boolean enabled, List<byte[]> supportedSads) {
// Note that we don't set any name to ARC.
- mService.getAudioManager().setWiredDeviceConnectionState(
- AudioSystem.DEVICE_OUT_HDMI_ARC,
- enabled ? 1 : 0, "", "");
+ AudioDeviceAttributes attributes = new AudioDeviceAttributes(
+ AudioDeviceAttributes.ROLE_OUTPUT, AudioDeviceInfo.TYPE_HDMI_ARC, "", "",
+ new ArrayList<AudioProfile>(), supportedSads.stream()
+ .map(sad -> new AudioDescriptor(AudioDescriptor.STANDARD_EDID,
+ AudioProfile.AUDIO_ENCAPSULATION_TYPE_NONE, sad))
+ .collect(Collectors.toList()));
+ mService.getAudioManager().setWiredDeviceConnectionState(attributes, enabled ? 1 : 0);
}
/**
diff --git a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
index 6f7473d..57fe9e6 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecStandbyModeHandler.java
@@ -133,6 +133,7 @@
addHandler(Constants.MESSAGE_SET_OSD_NAME, mBypasser);
addHandler(Constants.MESSAGE_DEVICE_VENDOR_ID, mBypasser);
addHandler(Constants.MESSAGE_REPORT_POWER_STATUS, mBypasser);
+ addHandler(Constants.MESSAGE_GIVE_FEATURES, mBypasser);
addHandler(Constants.MESSAGE_USER_CONTROL_PRESSED, mUserControlProcessedHandler);
diff --git a/services/core/java/com/android/server/hdmi/HdmiControlService.java b/services/core/java/com/android/server/hdmi/HdmiControlService.java
index 8391e0b..8ac2331 100644
--- a/services/core/java/com/android/server/hdmi/HdmiControlService.java
+++ b/services/core/java/com/android/server/hdmi/HdmiControlService.java
@@ -1701,11 +1701,11 @@
class VendorCommandListenerRecord implements IBinder.DeathRecipient {
private final IHdmiVendorCommandListener mListener;
- private final int mDeviceType;
+ private final int mVendorId;
- public VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int deviceType) {
+ VendorCommandListenerRecord(IHdmiVendorCommandListener listener, int vendorId) {
mListener = listener;
- mDeviceType = deviceType;
+ mVendorId = vendorId;
}
@Override
@@ -2191,10 +2191,10 @@
}
@Override
- public void addVendorCommandListener(final IHdmiVendorCommandListener listener,
- final int deviceType) {
+ public void addVendorCommandListener(
+ final IHdmiVendorCommandListener listener, final int vendorId) {
initBinderCall();
- HdmiControlService.this.addVendorCommandListener(listener, deviceType);
+ HdmiControlService.this.addVendorCommandListener(listener, vendorId);
}
@Override
@@ -3354,8 +3354,9 @@
mStandbyMessageReceived = false;
}
- private void addVendorCommandListener(IHdmiVendorCommandListener listener, int deviceType) {
- VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, deviceType);
+ @VisibleForTesting
+ void addVendorCommandListener(IHdmiVendorCommandListener listener, int vendorId) {
+ VendorCommandListenerRecord record = new VendorCommandListenerRecord(listener, vendorId);
try {
listener.asBinder().linkToDeath(record, 0);
} catch (RemoteException e) {
@@ -3374,8 +3375,14 @@
return false;
}
for (VendorCommandListenerRecord record : mVendorCommandListenerRecords) {
- if (record.mDeviceType != deviceType) {
- continue;
+ if (hasVendorId) {
+ int vendorId =
+ ((params[0] & 0xFF) << 16)
+ + ((params[1] & 0xFF) << 8)
+ + (params[2] & 0xFF);
+ if (record.mVendorId != vendorId) {
+ continue;
+ }
}
try {
record.mListener.onReceived(srcAddress, destAddress, params, hasVendorId);
diff --git a/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
new file mode 100644
index 0000000..dbd3f35
--- /dev/null
+++ b/services/core/java/com/android/server/input/GestureMonitorSpyWindow.java
@@ -0,0 +1,95 @@
+/*
+ * 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.input;
+
+import static android.os.InputConstants.DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+
+import android.os.IBinder;
+import android.view.InputApplicationHandle;
+import android.view.InputChannel;
+import android.view.InputMonitor;
+import android.view.InputWindowHandle;
+import android.view.SurfaceControl;
+import android.view.WindowManager;
+
+/**
+ * An internal implementation of an {@link InputMonitor} that uses a spy window.
+ *
+ * This spy window is a layer container in the SurfaceFlinger hierarchy that does not have any
+ * graphical buffer, but can still receive input. It is parented to the DisplayContent so
+ * that it can spy on any pointer events that start in the DisplayContent bounds. When the
+ * object is constructed, it will add itself to SurfaceFlinger.
+ */
+class GestureMonitorSpyWindow {
+ final InputApplicationHandle mApplicationHandle;
+ final InputWindowHandle mWindowHandle;
+
+ // The token, InputChannel, and SurfaceControl are owned by this object.
+ final IBinder mMonitorToken;
+ final InputChannel mClientChannel;
+ final SurfaceControl mInputSurface;
+
+ GestureMonitorSpyWindow(IBinder token, String name, int displayId, int pid, int uid,
+ SurfaceControl sc, InputChannel inputChannel) {
+ mMonitorToken = token;
+ mClientChannel = inputChannel;
+ mInputSurface = sc;
+
+ mApplicationHandle = new InputApplicationHandle(null, name,
+ DEFAULT_DISPATCHING_TIMEOUT_MILLIS);
+ mWindowHandle = new InputWindowHandle(mApplicationHandle, displayId);
+
+ mWindowHandle.name = name;
+ mWindowHandle.token = mClientChannel.getToken();
+ mWindowHandle.layoutParamsType = WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY;
+ mWindowHandle.layoutParamsFlags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+ mWindowHandle.dispatchingTimeoutMillis = DEFAULT_DISPATCHING_TIMEOUT_MILLIS;
+ mWindowHandle.visible = true;
+ mWindowHandle.focusable = false;
+ mWindowHandle.hasWallpaper = false;
+ mWindowHandle.paused = false;
+ mWindowHandle.ownerPid = pid;
+ mWindowHandle.ownerUid = uid;
+ mWindowHandle.inputFeatures = WindowManager.LayoutParams.INPUT_FEATURE_SPY;
+ mWindowHandle.scaleFactor = 1.0f;
+ mWindowHandle.trustedOverlay = true;
+ 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.setPosition(mInputSurface, 0, 0);
+ t.setCrop(mInputSurface, null /* crop to parent surface */);
+ t.show(mInputSurface);
+
+ t.apply();
+ }
+
+ void remove() {
+ final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
+ t.hide(mInputSurface);
+ t.remove(mInputSurface);
+ t.apply();
+
+ mClientChannel.dispose();
+ }
+
+ String dump() {
+ return "name='" + mWindowHandle.name + "', inputChannelToken="
+ + mClientChannel.getToken() + " displayId=" + mWindowHandle.displayId;
+ }
+}
diff --git a/services/core/java/com/android/server/input/InputManagerService.java b/services/core/java/com/android/server/input/InputManagerService.java
index 783a88c..940c25c 100644
--- a/services/core/java/com/android/server/input/InputManagerService.java
+++ b/services/core/java/com/android/server/input/InputManagerService.java
@@ -41,7 +41,6 @@
import android.content.res.XmlResourceParser;
import android.database.ContentObserver;
import android.graphics.PointF;
-import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.hardware.display.DisplayViewport;
import android.hardware.input.IInputDevicesChangedListener;
@@ -64,6 +63,7 @@
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IInputConstants;
import android.os.IVibratorStateListener;
import android.os.InputEventInjectionResult;
import android.os.InputEventInjectionSync;
@@ -196,8 +196,9 @@
private final Object mTabletModeLock = new Object();
// List of currently registered tablet mode changed listeners by process id
+ @GuardedBy("mTabletModeLock")
private final SparseArray<TabletModeChangedListenerRecord> mTabletModeChangedListeners =
- new SparseArray<>(); // guarded by mTabletModeLock
+ new SparseArray<>();
private final List<TabletModeChangedListenerRecord> mTempTabletModeChangedListenersToNotify =
new ArrayList<>();
@@ -215,40 +216,39 @@
private final PersistentDataStore mDataStore = new PersistentDataStore();
// List of currently registered input devices changed listeners by process id.
- private Object mInputDevicesLock = new Object();
+ private final Object mInputDevicesLock = new Object();
@GuardedBy("mInputDevicesLock")
- private boolean mInputDevicesChangedPending; // guarded by mInputDevicesLock
+ private boolean mInputDevicesChangedPending;
@GuardedBy("mInputDevicesLock")
private InputDevice[] mInputDevices = new InputDevice[0];
+ @GuardedBy("mInputDevicesLock")
private final SparseArray<InputDevicesChangedListenerRecord> mInputDevicesChangedListeners =
- new SparseArray<InputDevicesChangedListenerRecord>(); // guarded by mInputDevicesLock
+ new SparseArray<>();
private final ArrayList<InputDevicesChangedListenerRecord>
- mTempInputDevicesChangedListenersToNotify =
- new ArrayList<InputDevicesChangedListenerRecord>(); // handler thread only
- private final ArrayList<InputDevice>
- mTempFullKeyboards = new ArrayList<InputDevice>(); // handler thread only
+ mTempInputDevicesChangedListenersToNotify = new ArrayList<>(); // handler thread only
+ private final ArrayList<InputDevice> mTempFullKeyboards =
+ new ArrayList<>(); // handler thread only
private boolean mKeyboardLayoutNotificationShown;
private Toast mSwitchedKeyboardLayoutToast;
// State for vibrator tokens.
- private Object mVibratorLock = new Object();
- private Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<IBinder, VibratorToken>();
+ private final Object mVibratorLock = new Object();
+ private final Map<IBinder, VibratorToken> mVibratorTokens = new ArrayMap<>();
private int mNextVibratorTokenValue;
// List of currently registered vibrator state changed listeners by device id.
@GuardedBy("mVibratorLock")
private final SparseArray<RemoteCallbackList<IVibratorStateListener>> mVibratorStateListeners =
- new SparseArray<RemoteCallbackList<IVibratorStateListener>>();
+ new SparseArray<>();
// List of vibrator states by device id.
@GuardedBy("mVibratorLock")
private final SparseBooleanArray mIsVibrating = new SparseBooleanArray();
- private Object mLightLock = new Object();
+ private final Object mLightLock = new Object();
// State for light tokens. A light token marks a lights manager session, it is generated
// by light session open() and deleted in session close().
// When lights session requests light states, the token will be used to find the light session.
@GuardedBy("mLightLock")
- private final ArrayMap<IBinder, LightSession> mLightSessions =
- new ArrayMap<IBinder, LightSession>();
+ private final ArrayMap<IBinder, LightSession> mLightSessions = new ArrayMap<>();
// State for lid switch
// Lock for the lid switch state. Held when triggering callbacks to guarantee lid switch events
@@ -257,25 +257,42 @@
// events that occur at the same time are delivered after the callback has returned.
private final Object mLidSwitchLock = new Object();
@GuardedBy("mLidSwitchLock")
- private List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
+ private final List<LidSwitchCallback> mLidSwitchCallbacks = new ArrayList<>();
// State for the currently installed input filter.
final Object mInputFilterLock = new Object();
- IInputFilter mInputFilter; // guarded by mInputFilterLock
- InputFilterHost mInputFilterHost; // guarded by mInputFilterLock
+ @GuardedBy("mInputFilterLock")
+ IInputFilter mInputFilter;
+ @GuardedBy("mInputFilterLock")
+ InputFilterHost mInputFilterHost;
// The associations of input devices to displays by port. Maps from input device port (String)
// to display id (int). Currently only accessed by InputReader.
private final Map<String, Integer> mStaticAssociations;
private final Object mAssociationsLock = new Object();
@GuardedBy("mAssociationLock")
- private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<String, Integer>();
+ private final Map<String, Integer> mRuntimeAssociations = new ArrayMap<>();
@GuardedBy("mAssociationLock")
private final Map<String, String> mUniqueIdAssociations = new ArrayMap<>();
- private final Object mPointerDisplayIdLock = new Object();
+
+ private final Object mAdditionalDisplayInputPropertiesLock = new Object();
+
// Forces the MouseCursorController to target a specific display id.
- @GuardedBy("mPointerDisplayIdLock")
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
private int mOverriddenPointerDisplayId = Display.INVALID_DISPLAY;
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private final SparseArray<AdditionalDisplayInputProperties> mAdditionalDisplayInputProperties =
+ new SparseArray<>();
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private int mIconType = PointerIcon.TYPE_NOT_SPECIFIED;
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private PointerIcon mIcon;
+
+
+ // Holds all the registered gesture monitors that are implemented as spy windows. The spy
+ // windows are mapped by their InputChannel tokens.
+ @GuardedBy("mInputMonitors")
+ final Map<IBinder, GestureMonitorSpyWindow> mInputMonitors = new HashMap<>();
private static native long nativeInit(InputManagerService service,
Context context, MessageQueue messageQueue);
@@ -295,11 +312,12 @@
int locationKeyCode);
private static native InputChannel nativeCreateInputChannel(long ptr, String name);
private static native InputChannel nativeCreateInputMonitor(long ptr, int displayId,
- boolean isGestureMonitor, String name, int pid);
+ String name, int pid);
private static native void nativeRemoveInputChannel(long ptr, IBinder connectionToken);
private static native void nativePilferPointers(long ptr, IBinder token);
private static native void nativeSetInputFilterEnabled(long ptr, boolean enable);
- private static native void nativeSetInTouchMode(long ptr, boolean inTouchMode);
+ private static native boolean nativeSetInTouchMode(long ptr, boolean inTouchMode, int pid,
+ int uid, boolean hasPermission);
private static native void nativeSetMaximumObscuringOpacityForTouch(long ptr, float opacity);
private static native void nativeSetBlockUntrustedTouchesMode(long ptr, int mode);
private static native int nativeInjectInputEvent(long ptr, InputEvent event,
@@ -574,38 +592,27 @@
nativeReloadDeviceAliases(mPtr);
}
- /** Rotates CCW by `delta` 90-degree increments. */
- private static void rotateBounds(Rect inOutBounds, int parentW, int parentH, int delta) {
- int rdelta = ((delta % 4) + 4) % 4;
- int origLeft = inOutBounds.left;
- switch (rdelta) {
- case 0:
- return;
- case 1:
- inOutBounds.left = inOutBounds.top;
- inOutBounds.top = parentW - inOutBounds.right;
- inOutBounds.right = inOutBounds.bottom;
- inOutBounds.bottom = parentW - origLeft;
- return;
- case 2:
- inOutBounds.left = parentW - inOutBounds.right;
- inOutBounds.right = parentW - origLeft;
- return;
- case 3:
- inOutBounds.left = parentH - inOutBounds.bottom;
- inOutBounds.bottom = inOutBounds.right;
- inOutBounds.right = parentH - inOutBounds.top;
- inOutBounds.top = origLeft;
- return;
- }
- }
-
private void setDisplayViewportsInternal(List<DisplayViewport> viewports) {
- final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ final DisplayViewport[] vArray = new DisplayViewport[viewports.size()];
for (int i = viewports.size() - 1; i >= 0; --i) {
vArray[i] = viewports.get(i);
}
- nativeSetDisplayViewports(mPtr, vArray);
+ nativeSetDisplayViewports(mPtr, vArray);
+
+ if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+ final AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
+ if (properties != null) {
+ updatePointerIconVisibleLocked(properties.pointerIconVisible);
+ updatePointerAccelerationLocked(properties.pointerAcceleration);
+ return;
+ }
+ }
+ updatePointerIconVisibleLocked(
+ AdditionalDisplayInputProperties.DEFAULT_POINTER_ICON_VISIBLE);
+ updatePointerAccelerationLocked(IInputConstants.DEFAULT_POINTER_ACCELERATION);
+ }
}
/**
@@ -712,37 +719,77 @@
throw new IllegalArgumentException("displayId must >= 0.");
}
- return nativeCreateInputMonitor(mPtr, displayId, false /* isGestureMonitor */,
- inputChannelName, Binder.getCallingPid());
+ return nativeCreateInputMonitor(mPtr, displayId, inputChannelName, Binder.getCallingPid());
+ }
+
+ @NonNull
+ private InputChannel createSpyWindowGestureMonitor(IBinder monitorToken, String name,
+ int displayId, int pid, int uid) {
+ final SurfaceControl sc = mWindowManagerCallbacks.createSurfaceForGestureMonitor(name,
+ displayId);
+ if (sc == null) {
+ throw new IllegalArgumentException(
+ "Could not create gesture monitor surface on display: " + displayId);
+ }
+ final InputChannel channel = createInputChannel(name);
+
+ try {
+ monitorToken.linkToDeath(() -> removeSpyWindowGestureMonitor(channel.getToken()), 0);
+ } catch (RemoteException e) {
+ Slog.i(TAG, "Client died before '" + name + "' could be created.");
+ return null;
+ }
+ synchronized (mInputMonitors) {
+ mInputMonitors.put(channel.getToken(),
+ new GestureMonitorSpyWindow(monitorToken, name, displayId, pid, uid, sc,
+ channel));
+ }
+
+ final InputChannel outInputChannel = new InputChannel();
+ channel.copyTo(outInputChannel);
+ return outInputChannel;
+ }
+
+ private void removeSpyWindowGestureMonitor(IBinder inputChannelToken) {
+ final GestureMonitorSpyWindow monitor;
+ synchronized (mInputMonitors) {
+ monitor = mInputMonitors.remove(inputChannelToken);
+ }
+ removeInputChannel(inputChannelToken);
+ if (monitor == null) return;
+ monitor.remove();
}
/**
* Creates an input monitor that will receive pointer events for the purposes of system-wide
* gesture interpretation.
*
- * @param inputChannelName The input channel name.
+ * @param requestedName The input channel name.
* @param displayId Target display id.
* @return The input channel.
*/
@Override // Binder call
- public InputMonitor monitorGestureInput(String inputChannelName, int displayId) {
+ public InputMonitor monitorGestureInput(IBinder monitorToken, @NonNull String requestedName,
+ int displayId) {
if (!checkCallingPermission(android.Manifest.permission.MONITOR_INPUT,
- "monitorInputRegion()")) {
+ "monitorGestureInput()")) {
throw new SecurityException("Requires MONITOR_INPUT permission");
}
- Objects.requireNonNull(inputChannelName, "inputChannelName must not be null.");
+ Objects.requireNonNull(requestedName, "name must not be null.");
+ Objects.requireNonNull(monitorToken, "token must not be null.");
if (displayId < Display.DEFAULT_DISPLAY) {
throw new IllegalArgumentException("displayId must >= 0.");
}
+ final String name = "[Gesture Monitor] " + requestedName;
final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
- InputChannel inputChannel = nativeCreateInputMonitor(
- mPtr, displayId, true /*isGestureMonitor*/, inputChannelName, pid);
- InputMonitorHost host = new InputMonitorHost(inputChannel.getToken());
- return new InputMonitor(inputChannel, host);
+ final InputChannel inputChannel =
+ createSpyWindowGestureMonitor(monitorToken, name, displayId, pid, uid);
+ return new InputMonitor(inputChannel, new InputMonitorHost(inputChannel.getToken()));
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -820,12 +867,16 @@
* other apps, when they become focused.
*
* When input dispatches focus to the apps, the touch mode state
- * will be sent together with the focus change.
+ * will be sent together with the focus change (but each one in its own event).
*
- * @param inTouchMode true if the device is in touch mode.
+ * @param inTouchMode true if the device is in touch mode
+ * @param pid the pid of the process that requested to switch touch mode state
+ * @param uid the uid of the process that requested to switch touch mode state
+ * @param hasPermission if set to {@code true} then no further authorization will be performed
+ * @return {@code true} if the touch mode was successfully changed, {@code false} otherwise
*/
- public void setInTouchMode(boolean inTouchMode) {
- nativeSetInTouchMode(mPtr, inTouchMode);
+ public boolean setInTouchMode(boolean inTouchMode, int pid, int uid, boolean hasPermission) {
+ return nativeSetInTouchMode(mPtr, inTouchMode, pid, uid, hasPermission);
}
@Override // Binder call
@@ -882,9 +933,7 @@
@Override // Binder call
public InputDevice getInputDevice(int deviceId) {
synchronized (mInputDevicesLock) {
- final int count = mInputDevices.length;
- for (int i = 0; i < count; i++) {
- final InputDevice inputDevice = mInputDevices[i];
+ for (final InputDevice inputDevice : mInputDevices) {
if (inputDevice.getId() == deviceId) {
return inputDevice;
}
@@ -1067,23 +1116,19 @@
return null;
}
final List<KeyboardLayout> layouts = new ArrayList<>();
- visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- // Only select a default when we know the layout is appropriate. For now, this
- // means its a custom layout for a specific keyboard.
- if (layout.getVendorId() != d.getVendorId()
- || layout.getProductId() != d.getProductId()) {
- return;
- }
- final LocaleList locales = layout.getLocales();
- final int numLocales = locales.size();
- for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
- if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) {
- layouts.add(layout);
- break;
- }
+ visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> {
+ // Only select a default when we know the layout is appropriate. For now, this
+ // means it's a custom layout for a specific keyboard.
+ if (layout.getVendorId() != d.getVendorId()
+ || layout.getProductId() != d.getProductId()) {
+ return;
+ }
+ final LocaleList locales = layout.getLocales();
+ final int numLocales = locales.size();
+ for (int localeIndex = 0; localeIndex < numLocales; ++localeIndex) {
+ if (isCompatibleLocale(systemLocale, locales.get(localeIndex))) {
+ layouts.add(layout);
+ break;
}
}
});
@@ -1283,13 +1328,8 @@
private void updateKeyboardLayouts() {
// Scan all input devices state for keyboard layouts that have been uninstalled.
final HashSet<String> availableKeyboardLayouts = new HashSet<String>();
- visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- availableKeyboardLayouts.add(layout.getDescriptor());
- }
- });
+ visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) ->
+ availableKeyboardLayouts.add(layout.getDescriptor()));
synchronized (mDataStore) {
try {
mDataStore.removeUninstalledKeyboardLayouts(availableKeyboardLayouts);
@@ -1316,14 +1356,8 @@
@Override // Binder call
public KeyboardLayout[] getKeyboardLayouts() {
- final ArrayList<KeyboardLayout> list = new ArrayList<KeyboardLayout>();
- visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- list.add(layout);
- }
- });
+ final ArrayList<KeyboardLayout> list = new ArrayList<>();
+ visitAllKeyboardLayouts((resources, keyboardLayoutResId, layout) -> list.add(layout));
return list.toArray(new KeyboardLayout[list.size()]);
}
@@ -1331,10 +1365,10 @@
public KeyboardLayout[] getKeyboardLayoutsForInputDevice(
final InputDeviceIdentifier identifier) {
final String[] enabledLayoutDescriptors =
- getEnabledKeyboardLayoutsForInputDevice(identifier);
+ getEnabledKeyboardLayoutsForInputDevice(identifier);
final ArrayList<KeyboardLayout> enabledLayouts =
- new ArrayList<KeyboardLayout>(enabledLayoutDescriptors.length);
- final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<KeyboardLayout>();
+ new ArrayList<>(enabledLayoutDescriptors.length);
+ final ArrayList<KeyboardLayout> potentialLayouts = new ArrayList<>();
visitAllKeyboardLayouts(new KeyboardLayoutVisitor() {
boolean mHasSeenDeviceSpecificLayout;
@@ -1382,13 +1416,8 @@
"keyboardLayoutDescriptor must not be null");
final KeyboardLayout[] result = new KeyboardLayout[1];
- visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- result[0] = layout;
- }
- });
+ visitKeyboardLayout(keyboardLayoutDescriptor,
+ (resources, keyboardLayoutResId, layout) -> result[0] = layout);
if (result[0] == null) {
Slog.w(TAG, "Could not get keyboard layout with descriptor '"
+ keyboardLayoutDescriptor + "'.");
@@ -1420,7 +1449,7 @@
| PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE);
visitKeyboardLayoutsInPackage(pm, receiver, d.keyboardLayoutName, 0, visitor);
- } catch (NameNotFoundException ex) {
+ } catch (NameNotFoundException ignored) {
}
}
}
@@ -1450,11 +1479,10 @@
try {
Resources resources = pm.getResourcesForApplication(receiver.applicationInfo);
- XmlResourceParser parser = resources.getXml(configResId);
- try {
+ try (XmlResourceParser parser = resources.getXml(configResId)) {
XmlUtils.beginDocument(parser, "keyboard-layouts");
- for (;;) {
+ while (true) {
XmlUtils.nextElement(parser);
String element = parser.getName();
if (element == null) {
@@ -1462,22 +1490,22 @@
}
if (element.equals("keyboard-layout")) {
TypedArray a = resources.obtainAttributes(
- parser, com.android.internal.R.styleable.KeyboardLayout);
+ parser, R.styleable.KeyboardLayout);
try {
String name = a.getString(
- com.android.internal.R.styleable.KeyboardLayout_name);
+ R.styleable.KeyboardLayout_name);
String label = a.getString(
- com.android.internal.R.styleable.KeyboardLayout_label);
+ R.styleable.KeyboardLayout_label);
int keyboardLayoutResId = a.getResourceId(
- com.android.internal.R.styleable.KeyboardLayout_keyboardLayout,
+ R.styleable.KeyboardLayout_keyboardLayout,
0);
String languageTags = a.getString(
- com.android.internal.R.styleable.KeyboardLayout_locale);
+ R.styleable.KeyboardLayout_locale);
LocaleList locales = getLocalesFromLanguageTags(languageTags);
int vid = a.getInt(
- com.android.internal.R.styleable.KeyboardLayout_vendorId, -1);
+ R.styleable.KeyboardLayout_vendorId, -1);
int pid = a.getInt(
- com.android.internal.R.styleable.KeyboardLayout_productId, -1);
+ R.styleable.KeyboardLayout_productId, -1);
if (name == null || label == null || keyboardLayoutResId == 0) {
Slog.w(TAG, "Missing required 'name', 'label' or 'keyboardLayout' "
@@ -1504,8 +1532,6 @@
+ receiver.packageName + "/" + receiver.name);
}
}
- } finally {
- parser.close();
}
} catch (Exception ex) {
Slog.w(TAG, "Could not parse keyboard layout resource from receiver "
@@ -1532,10 +1558,7 @@
if (identifier.getVendorId() == 0 && identifier.getProductId() == 0) {
return identifier.getDescriptor();
}
- StringBuilder bob = new StringBuilder();
- bob.append("vendor:").append(identifier.getVendorId());
- bob.append(",product:").append(identifier.getProductId());
- return bob.toString();
+ return "vendor:" + identifier.getVendorId() + ",product:" + identifier.getProductId();
}
@Override // Binder call
@@ -1543,7 +1566,7 @@
String key = getLayoutDescriptor(identifier);
synchronized (mDataStore) {
- String layout = null;
+ String layout;
// try loading it using the layout descriptor if we have it
layout = mDataStore.getCurrentKeyboardLayout(key);
if (layout == null && !key.equals(identifier.getDescriptor())) {
@@ -1798,10 +1821,60 @@
nativeSetPointerSpeed(mPtr, speed);
}
- private void setPointerAcceleration(float acceleration) {
+ private void setPointerAcceleration(float acceleration, int displayId) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(displayId);
+ if (properties == null) {
+ properties = new AdditionalDisplayInputProperties();
+ mAdditionalDisplayInputProperties.put(displayId, properties);
+ }
+ properties.pointerAcceleration = acceleration;
+ if (properties.allDefaults()) {
+ mAdditionalDisplayInputProperties.remove(displayId);
+ }
+ if (mOverriddenPointerDisplayId == displayId) {
+ updatePointerAccelerationLocked(acceleration);
+ }
+ }
+ }
+
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private void updatePointerAccelerationLocked(float acceleration) {
nativeSetPointerAcceleration(mPtr, acceleration);
}
+ private void setPointerIconVisible(boolean visible, int displayId) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(displayId);
+ if (properties == null) {
+ properties = new AdditionalDisplayInputProperties();
+ mAdditionalDisplayInputProperties.put(displayId, properties);
+ }
+ properties.pointerIconVisible = visible;
+ if (properties.allDefaults()) {
+ mAdditionalDisplayInputProperties.remove(displayId);
+ }
+ if (mOverriddenPointerDisplayId == displayId) {
+ updatePointerIconVisibleLocked(visible);
+ }
+ }
+ }
+
+ @GuardedBy("mAdditionalDisplayInputPropertiesLock")
+ private void updatePointerIconVisibleLocked(boolean visible) {
+ if (visible) {
+ if (mIconType == PointerIcon.TYPE_CUSTOM) {
+ nativeSetCustomPointerIcon(mPtr, mIcon);
+ } else {
+ nativeSetPointerIconType(mPtr, mIconType);
+ }
+ } else {
+ nativeSetPointerIconType(mPtr, PointerIcon.TYPE_NULL);
+ }
+ }
+
private void registerPointerSpeedSettingObserver() {
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.POINTER_SPEED), true,
@@ -1818,7 +1891,7 @@
try {
speed = Settings.System.getIntForUser(mContext.getContentResolver(),
Settings.System.POINTER_SPEED, UserHandle.USER_CURRENT);
- } catch (SettingNotFoundException snfe) {
+ } catch (SettingNotFoundException ignored) {
}
return speed;
}
@@ -1936,13 +2009,27 @@
}
private void setVirtualMousePointerDisplayId(int displayId) {
- synchronized (mPointerDisplayIdLock) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
mOverriddenPointerDisplayId = displayId;
+ if (displayId != Display.INVALID_DISPLAY) {
+ final AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(displayId);
+ if (properties != null) {
+ updatePointerAccelerationLocked(properties.pointerAcceleration);
+ updatePointerIconVisibleLocked(properties.pointerIconVisible);
+ }
+ }
}
// TODO(b/215597605): trigger MousePositionTracker update
nativeNotifyPointerDisplayIdChanged(mPtr);
}
+ private int getVirtualMousePointerDisplayId() {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ return mOverriddenPointerDisplayId;
+ }
+ }
+
private void setDisplayEligibilityForPointerCapture(int displayId, boolean isEligible) {
nativeSetDisplayEligibilityForPointerCapture(mPtr, displayId, isEligible);
}
@@ -2082,7 +2169,7 @@
SparseArray<VibrationEffect> effects = stereo.getEffects();
long[] pattern = new long[0];
int repeat = Integer.MIN_VALUE;
- SparseArray<int[]> amplitudes = new SparseArray<int[]>(effects.size());
+ SparseArray<int[]> amplitudes = new SparseArray<>(effects.size());
for (int i = 0; i < effects.size(); i++) {
VibrationInfo info = new VibrationInfo(effects.valueAt(i));
// Pattern of all effects should be same
@@ -2132,6 +2219,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifyVibratorState(int deviceId, boolean isOn) {
if (DEBUG) {
Slog.d(TAG, "notifyVibratorState: deviceId=" + deviceId + " isOn=" + isOn);
@@ -2173,7 +2261,7 @@
@Override // Binder call
public boolean registerVibratorStateListener(int deviceId, IVibratorStateListener listener) {
- Preconditions.checkNotNull(listener, "listener must not be null");
+ Objects.requireNonNull(listener, "listener must not be null");
RemoteCallbackList<IVibratorStateListener> listeners;
synchronized (mVibratorLock) {
@@ -2231,15 +2319,44 @@
// Binder call
@Override
- public void setPointerIconType(int iconId) {
- nativeSetPointerIconType(mPtr, iconId);
+ public void setPointerIconType(int iconType) {
+ if (iconType == PointerIcon.TYPE_CUSTOM) {
+ throw new IllegalArgumentException("Use setCustomPointerIcon to set custom pointers");
+ }
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ mIcon = null;
+ mIconType = iconType;
+ if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+ final AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
+ if (properties == null || properties.pointerIconVisible) {
+ nativeSetPointerIconType(mPtr, mIconType);
+ }
+ } else {
+ nativeSetPointerIconType(mPtr, mIconType);
+ }
+ }
}
// Binder call
@Override
public void setCustomPointerIcon(PointerIcon icon) {
Objects.requireNonNull(icon);
- nativeSetCustomPointerIcon(mPtr, icon);
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ mIconType = PointerIcon.TYPE_CUSTOM;
+ mIcon = icon;
+ if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+ final AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.get(mOverriddenPointerDisplayId);
+ if (properties == null || properties.pointerIconVisible) {
+ // Only set the icon if it is not currently hidden; otherwise, it will be set
+ // once it's no longer hidden.
+ nativeSetCustomPointerIcon(mPtr, mIcon);
+ }
+ } else {
+ nativeSetCustomPointerIcon(mPtr, mIcon);
+ }
+ }
}
/**
@@ -2331,7 +2448,7 @@
}
Objects.requireNonNull(listener, "listener must not be null");
- synchronized (mInputDevicesLock) {
+ synchronized (mSensorEventLock) {
int callingPid = Binder.getCallingPid();
if (mSensorEventListeners.get(callingPid) != null) {
Slog.e(TAG, "The calling process " + callingPid + " has already "
@@ -2363,7 +2480,7 @@
Objects.requireNonNull(listener, "listener must not be null");
- synchronized (mInputDevicesLock) {
+ synchronized (mSensorEventLock) {
int callingPid = Binder.getCallingPid();
if (mSensorEventListeners.get(callingPid) != null) {
SensorEventListenerRecord record = mSensorEventListeners.get(callingPid);
@@ -2377,7 +2494,7 @@
@Override // Binder call
public boolean flushSensor(int deviceId, int sensorType) {
- synchronized (mInputDevicesLock) {
+ synchronized (mSensorEventLock) {
int callingPid = Binder.getCallingPid();
SensorEventListenerRecord listener = mSensorEventListeners.get(callingPid);
if (listener != null) {
@@ -2445,7 +2562,7 @@
* Set specified light state with for a specific input device.
*/
private void setLightStateInternal(int deviceId, Light light, LightState lightState) {
- Preconditions.checkNotNull(light, "light does not exist");
+ Objects.requireNonNull(light, "light does not exist");
if (DEBUG) {
Slog.d(TAG, "setLightStateInternal device " + deviceId + " light " + light
+ "lightState " + lightState);
@@ -2508,7 +2625,7 @@
@Override
public void openLightSession(int deviceId, String opPkg, IBinder token) {
- Preconditions.checkNotNull(token);
+ Objects.requireNonNull(token);
synchronized (mLightLock) {
Preconditions.checkState(mLightSessions.get(token) == null, "already registered");
LightSession lightSession = new LightSession(deviceId, opPkg, token);
@@ -2527,7 +2644,7 @@
@Override
public void closeLightSession(int deviceId, IBinder token) {
- Preconditions.checkNotNull(token);
+ Objects.requireNonNull(token);
synchronized (mLightLock) {
LightSession lightSession = mLightSessions.get(token);
Preconditions.checkState(lightSession != null, "not registered");
@@ -2563,37 +2680,71 @@
String dumpStr = nativeDump(mPtr);
if (dumpStr != null) {
pw.println(dumpStr);
- dumpAssociations(pw);
}
+
+ pw.println("Input Manager Service (Java) State:");
+ dumpAssociations(pw, " " /*prefix*/);
+ dumpSpyWindowGestureMonitors(pw, " " /*prefix*/);
+ dumpDisplayInputPropertiesValues(pw, " " /* prefix */);
}
- private void dumpAssociations(PrintWriter pw) {
+ private void dumpAssociations(PrintWriter pw, String prefix) {
if (!mStaticAssociations.isEmpty()) {
- pw.println("Static Associations:");
+ pw.println(prefix + "Static Associations:");
mStaticAssociations.forEach((k, v) -> {
- pw.print(" port: " + k);
+ pw.print(prefix + " port: " + k);
pw.println(" display: " + v);
});
}
synchronized (mAssociationsLock) {
if (!mRuntimeAssociations.isEmpty()) {
- pw.println("Runtime Associations:");
+ pw.println(prefix + "Runtime Associations:");
mRuntimeAssociations.forEach((k, v) -> {
- pw.print(" port: " + k);
+ pw.print(prefix + " port: " + k);
pw.println(" display: " + v);
});
}
if (!mUniqueIdAssociations.isEmpty()) {
- pw.println("Unique Id Associations:");
+ pw.println(prefix + "Unique Id Associations:");
mUniqueIdAssociations.forEach((k, v) -> {
- pw.print(" port: " + k);
+ pw.print(prefix + " port: " + k);
pw.println(" uniqueId: " + v);
});
}
}
}
+ private void dumpSpyWindowGestureMonitors(PrintWriter pw, String prefix) {
+ synchronized (mInputMonitors) {
+ if (mInputMonitors.isEmpty()) return;
+ pw.println(prefix + "Gesture Monitors (implemented as spy windows):");
+ int i = 0;
+ for (final GestureMonitorSpyWindow monitor : mInputMonitors.values()) {
+ pw.append(prefix + " " + i++ + ": ").println(monitor.dump());
+ }
+ }
+ }
+
+ private void dumpDisplayInputPropertiesValues(PrintWriter pw, String prefix) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
+ if (mAdditionalDisplayInputProperties.size() != 0) {
+ pw.println(prefix + "mAdditionalDisplayInputProperties:");
+ for (int i = 0; i < mAdditionalDisplayInputProperties.size(); i++) {
+ pw.println(prefix + " displayId: "
+ + mAdditionalDisplayInputProperties.keyAt(i));
+ final AdditionalDisplayInputProperties properties =
+ mAdditionalDisplayInputProperties.valueAt(i);
+ pw.println(prefix + " pointerAcceleration: " + properties.pointerAcceleration);
+ pw.println(prefix + " pointerIconVisible: " + properties.pointerIconVisible);
+ }
+ }
+ if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
+ pw.println(prefix + "mOverriddenPointerDisplayId: " + mOverriddenPointerDisplayId);
+ }
+ }
+ }
+
private boolean checkCallingPermission(String permission, String func) {
// Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == Process.myPid()) {
@@ -2617,16 +2768,19 @@
synchronized (mInputFilterLock) { }
synchronized (mAssociationsLock) { /* Test if blocked by associations lock. */}
synchronized (mLidSwitchLock) { /* Test if blocked by lid switch lock. */ }
- synchronized (mPointerDisplayIdLock) { /* Test if blocked by pointer display id lock */ }
+ synchronized (mInputMonitors) { /* Test if blocked by input monitor lock. */ }
+ synchronized (mAdditionalDisplayInputPropertiesLock) { /* Test if blocked by props lock */ }
nativeMonitor(mPtr);
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifyConfigurationChanged(long whenNanos) {
mWindowManagerCallbacks.notifyConfigurationChanged();
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifyInputDevicesChanged(InputDevice[] inputDevices) {
synchronized (mInputDevicesLock) {
if (!mInputDevicesChangedPending) {
@@ -2640,6 +2794,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifySwitch(long whenNanos, int switchValues, int switchMask) {
if (DEBUG) {
Slog.d(TAG, "notifySwitch: values=" + Integer.toHexString(switchValues)
@@ -2672,7 +2827,7 @@
SomeArgs args = SomeArgs.obtain();
args.argi1 = (int) (whenNanos & 0xFFFFFFFF);
args.argi2 = (int) (whenNanos >> 32);
- args.arg1 = Boolean.valueOf((switchValues & SW_TABLET_MODE_BIT) != 0);
+ args.arg1 = (switchValues & SW_TABLET_MODE_BIT) != 0;
mHandler.obtainMessage(MSG_DELIVER_TABLET_MODE_CHANGED,
args).sendToTarget();
}
@@ -2685,21 +2840,30 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifyInputChannelBroken(IBinder token) {
+ synchronized (mInputMonitors) {
+ if (mInputMonitors.containsKey(token)) {
+ removeSpyWindowGestureMonitor(token);
+ }
+ }
mWindowManagerCallbacks.notifyInputChannelBroken(token);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyDropWindow(IBinder token, float x, float y) {
mWindowManagerCallbacks.notifyDropWindow(token, x, y);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyUntrustedTouch(String packageName) {
// TODO(b/169067926): Remove toast after gathering feedback on dogfood.
if (!UNTRUSTED_TOUCHES_TOAST || ArrayUtils.contains(
@@ -2715,31 +2879,59 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifyNoFocusedWindowAnr(InputApplicationHandle inputApplicationHandle) {
mWindowManagerCallbacks.notifyNoFocusedWindowAnr(inputApplicationHandle);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyWindowUnresponsive(IBinder token, String reason) {
+ int gestureMonitorPid = -1;
+ synchronized (mInputMonitors) {
+ final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
+ if (gestureMonitor != null) {
+ gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
+ }
+ }
+ if (gestureMonitorPid != -1) {
+ mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(gestureMonitorPid, reason);
+ return;
+ }
mWindowManagerCallbacks.notifyWindowUnresponsive(token, reason);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyMonitorUnresponsive(int pid, String reason) {
mWindowManagerCallbacks.notifyGestureMonitorUnresponsive(pid, reason);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyWindowResponsive(IBinder token) {
+ int gestureMonitorPid = -1;
+ synchronized (mInputMonitors) {
+ final GestureMonitorSpyWindow gestureMonitor = mInputMonitors.get(token);
+ if (gestureMonitor != null) {
+ gestureMonitorPid = gestureMonitor.mWindowHandle.ownerPid;
+ }
+ }
+ if (gestureMonitorPid != -1) {
+ mWindowManagerCallbacks.notifyGestureMonitorResponsive(gestureMonitorPid);
+ return;
+ }
mWindowManagerCallbacks.notifyWindowResponsive(token);
}
// Native callback
+ @SuppressWarnings("unused")
private void notifyMonitorResponsive(int pid) {
mWindowManagerCallbacks.notifyGestureMonitorResponsive(pid);
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifySensorEvent(int deviceId, int sensorType, int accuracy, long timestamp,
float[] values) {
if (DEBUG) {
@@ -2763,6 +2955,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private void notifySensorAccuracy(int deviceId, int sensorType, int accuracy) {
mSensorAccuracyListenersToNotify.clear();
final int numListeners;
@@ -2780,6 +2973,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
final boolean filterInputEvent(InputEvent event, int policyFlags) {
synchronized (mInputFilterLock) {
if (mInputFilter != null) {
@@ -2796,11 +2990,13 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeQueueing(event, policyFlags);
}
// Native callback.
+ @SuppressWarnings("unused")
private int interceptMotionBeforeQueueingNonInteractive(int displayId,
long whenNanos, int policyFlags) {
return mWindowManagerCallbacks.interceptMotionBeforeQueueingNonInteractive(
@@ -2808,33 +3004,39 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private long interceptKeyBeforeDispatching(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.interceptKeyBeforeDispatching(focus, event, policyFlags);
}
// Native callback.
+ @SuppressWarnings("unused")
private KeyEvent dispatchUnhandledKey(IBinder focus, KeyEvent event, int policyFlags) {
return mWindowManagerCallbacks.dispatchUnhandledKey(focus, event, policyFlags);
}
// Native callback.
+ @SuppressWarnings("unused")
private boolean checkInjectEventsPermission(int injectorPid, int injectorUid) {
return mContext.checkPermission(android.Manifest.permission.INJECT_EVENTS,
injectorPid, injectorUid) == PackageManager.PERMISSION_GRANTED;
}
// Native callback.
+ @SuppressWarnings("unused")
private void onPointerDownOutsideFocus(IBinder touchedToken) {
mWindowManagerCallbacks.onPointerDownOutsideFocus(touchedToken);
}
// Native callback.
+ @SuppressWarnings("unused")
private int getVirtualKeyQuietTimeMillis() {
return mContext.getResources().getInteger(
com.android.internal.R.integer.config_virtualKeyQuietTimeMillis);
}
// Native callback.
+ @SuppressWarnings("unused")
private static String[] getExcludedDeviceNames() {
List<String> names = new ArrayList<>();
// Read partner-provided list of excluded input devices
@@ -2890,6 +3092,7 @@
}
// Native callback
+ @SuppressWarnings("unused")
private String[] getInputPortAssociations() {
final Map<String, Integer> associations = new HashMap<>(mStaticAssociations);
@@ -2902,6 +3105,7 @@
}
// Native callback
+ @SuppressWarnings("unused")
private String[] getInputUniqueIdAssociations() {
final Map<String, String> associations;
synchronized (mAssociationsLock) {
@@ -2922,46 +3126,55 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private int getKeyRepeatTimeout() {
return ViewConfiguration.getKeyRepeatTimeout();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getKeyRepeatDelay() {
return ViewConfiguration.getKeyRepeatDelay();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getHoverTapTimeout() {
return ViewConfiguration.getHoverTapTimeout();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getHoverTapSlop() {
return ViewConfiguration.getHoverTapSlop();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getDoubleTapTimeout() {
return ViewConfiguration.getDoubleTapTimeout();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getLongPressTimeout() {
return ViewConfiguration.getLongPressTimeout();
}
// Native callback.
+ @SuppressWarnings("unused")
private int getPointerLayer() {
return mWindowManagerCallbacks.getPointerLayer();
}
// Native callback.
+ @SuppressWarnings("unused")
private PointerIcon getPointerIcon(int displayId) {
return PointerIcon.getDefaultIcon(getContextForPointerIcon(displayId));
}
// Native callback.
+ @SuppressWarnings("unused")
private long getParentSurfaceForPointers(int displayId) {
final SurfaceControl sc = mWindowManagerCallbacks.getParentSurfaceForPointers(displayId);
if (sc == null) {
@@ -3007,8 +3220,9 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private int getPointerDisplayId() {
- synchronized (mPointerDisplayIdLock) {
+ synchronized (mAdditionalDisplayInputPropertiesLock) {
// Prefer the override to all other displays.
if (mOverriddenPointerDisplayId != Display.INVALID_DISPLAY) {
return mOverriddenPointerDisplayId;
@@ -3018,6 +3232,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private String[] getKeyboardLayoutOverlay(InputDeviceIdentifier identifier) {
if (!mSystemReady) {
return null;
@@ -3029,19 +3244,15 @@
}
final String[] result = new String[2];
- visitKeyboardLayout(keyboardLayoutDescriptor, new KeyboardLayoutVisitor() {
- @Override
- public void visitKeyboardLayout(Resources resources,
- int keyboardLayoutResId, KeyboardLayout layout) {
- try (final InputStreamReader stream = new InputStreamReader(
- resources.openRawResource(keyboardLayoutResId))) {
- result[0] = layout.getDescriptor();
- result[1] = Streams.readFully(stream);
- } catch (IOException ex) {
- } catch (NotFoundException ex) {
- }
- }
- });
+ visitKeyboardLayout(keyboardLayoutDescriptor,
+ (resources, keyboardLayoutResId, layout) -> {
+ try (InputStreamReader stream = new InputStreamReader(
+ resources.openRawResource(keyboardLayoutResId))) {
+ result[0] = layout.getDescriptor();
+ result[1] = Streams.readFully(stream);
+ } catch (IOException | NotFoundException ignored) {
+ }
+ });
if (result[0] == null) {
Slog.w(TAG, "Could not get keyboard layout with descriptor '"
+ keyboardLayoutDescriptor + "'.");
@@ -3051,6 +3262,7 @@
}
// Native callback.
+ @SuppressWarnings("unused")
private String getDeviceAlias(String uniqueId) {
if (BluetoothAdapter.checkBluetoothAddress(uniqueId)) {
// TODO(BT) mBluetoothService.getRemoteAlias(uniqueId)
@@ -3184,14 +3396,34 @@
* pointers such as the mouse cursor and touch spots for the given display.
*/
SurfaceControl getParentSurfaceForPointers(int displayId);
+
+ /**
+ * Create a {@link SurfaceControl} that can be configured to receive input over the entire
+ * display to implement a gesture monitor. The surface will not have a graphical buffer.
+ * @param name the name of the gesture monitor
+ * @param displayId the display to create the window in
+ * @return the SurfaceControl of the new layer container surface
+ */
+ @Nullable
+ SurfaceControl createSurfaceForGestureMonitor(String name, int displayId);
}
/**
* Callback interface implemented by WiredAccessoryObserver.
*/
public interface WiredAccessoryCallbacks {
- public void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
- public void systemReady();
+ /**
+ * Notifies WiredAccessoryObserver that input state for wired accessories has changed
+ * @param whenNanos When the wired accessories changed
+ * @param switchValues The state of the switches
+ * @param switchMask The mask of switches that changed
+ */
+ void notifyWiredAccessoryChanged(long whenNanos, int switchValues, int switchMask);
+
+ /**
+ * Notifies WiredAccessoryObserver that the system is now ready.
+ */
+ void systemReady();
}
/**
@@ -3222,7 +3454,7 @@
break;
case MSG_DELIVER_TABLET_MODE_CHANGED:
SomeArgs args = (SomeArgs) msg.obj;
- long whenNanos = (args.argi1 & 0xFFFFFFFFl) | ((long) args.argi2 << 32);
+ long whenNanos = (args.argi1 & 0xFFFFFFFFL) | ((long) args.argi2 << 32);
boolean inTabletMode = (boolean) args.arg1;
deliverTabletModeChanged(whenNanos, inTabletMode);
break;
@@ -3234,8 +3466,10 @@
* Hosting interface for input filters to call back into the input manager.
*/
private final class InputFilterHost extends IInputFilterHost.Stub {
+ @GuardedBy("mInputFilterLock")
private boolean mDisconnected;
+ @GuardedBy("mInputFilterLock")
public void disconnectLocked() {
mDisconnected = true;
}
@@ -3258,20 +3492,20 @@
* Interface for the system to handle request from InputMonitors.
*/
private final class InputMonitorHost extends IInputMonitorHost.Stub {
- private final IBinder mToken;
+ private final IBinder mInputChannelToken;
- InputMonitorHost(IBinder token) {
- mToken = token;
+ InputMonitorHost(IBinder inputChannelToken) {
+ mInputChannelToken = inputChannelToken;
}
@Override
public void pilferPointers() {
- nativePilferPointers(mPtr, mToken);
+ nativePilferPointers(mPtr, mInputChannelToken);
}
@Override
public void dispose() {
- nativeRemoveInputChannel(mPtr, mToken);
+ removeSpyWindowGestureMonitor(mInputChannelToken);
}
}
@@ -3484,13 +3718,18 @@
}
@Override
+ public int getVirtualMousePointerDisplayId() {
+ return InputManagerService.this.getVirtualMousePointerDisplayId();
+ }
+
+ @Override
public PointF getCursorPosition() {
return mWindowManagerCallbacks.getCursorPosition();
}
@Override
- public void setPointerAcceleration(float acceleration) {
- InputManagerService.this.setPointerAcceleration(acceleration);
+ public void setPointerAcceleration(float acceleration, int displayId) {
+ InputManagerService.this.setPointerAcceleration(acceleration, displayId);
}
@Override
@@ -3499,6 +3738,11 @@
}
@Override
+ public void setPointerIconVisible(boolean visible, int displayId) {
+ InputManagerService.this.setPointerIconVisible(visible, displayId);
+ }
+
+ @Override
public void registerLidSwitchCallback(LidSwitchCallback callbacks) {
registerLidSwitchCallbackInternal(callbacks);
}
@@ -3525,4 +3769,21 @@
new InputShellCommand().exec(this, in, out, err, args, callback, resultReceiver);
}
+ private static class AdditionalDisplayInputProperties {
+
+ static final boolean DEFAULT_POINTER_ICON_VISIBLE = true;
+ static final float DEFAULT_POINTER_ACCELERATION =
+ (float) IInputConstants.DEFAULT_POINTER_ACCELERATION;
+
+ // The pointer acceleration for this display.
+ public float pointerAcceleration = DEFAULT_POINTER_ACCELERATION;
+
+ // Whether the pointer icon should be visible or hidden on this display.
+ public boolean pointerIconVisible = DEFAULT_POINTER_ICON_VISIBLE;
+
+ public boolean allDefaults() {
+ return Float.compare(pointerAcceleration, DEFAULT_POINTER_ACCELERATION) == 0
+ && pointerIconVisible == DEFAULT_POINTER_ICON_VISIBLE;
+ }
+ }
}
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/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index c207738a..ba15a04 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1495,8 +1495,7 @@
@Override
public void onStart() {
- LocalServices.addService(InputMethodManagerInternal.class,
- new LocalServiceImpl(mService));
+ mService.publishLocalService();
publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
}
@@ -4866,26 +4865,6 @@
return mCurrentSubtype;
}
- private List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (ImfLock.class) {
- return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
- }
- }
-
- private List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
- synchronized (ImfLock.class) {
- return getEnabledInputMethodListLocked(userId);
- }
- }
-
- private void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
- InlineSuggestionsRequestInfo requestInfo,
- IInlineSuggestionsRequestCallback callback) {
- synchronized (ImfLock.class) {
- onCreateInlineSuggestionsRequestLocked(userId, requestInfo, callback);
- }
- }
-
private ArrayMap<String, InputMethodInfo> queryMethodMapForUser(@UserIdInt int userId) {
final ArrayMap<String, InputMethodInfo> methodMap = new ArrayMap<>();
final ArrayList<InputMethodInfo> methodList = new ArrayList<>();
@@ -4897,161 +4876,153 @@
return methodMap;
}
- private boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- synchronized (ImfLock.class) {
- if (userId == mSettings.getCurrentUserId()) {
- if (!mMethodMap.containsKey(imeId)
- || !mSettings.getEnabledInputMethodListLocked()
- .contains(mMethodMap.get(imeId))) {
- return false; // IME is not found or not enabled.
- }
- setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
- return true;
- }
- final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(
- mContext.getResources(), mContext.getContentResolver(), methodMap,
- userId, false);
- if (!methodMap.containsKey(imeId)
- || !settings.getEnabledInputMethodListLocked()
- .contains(methodMap.get(imeId))) {
+ @GuardedBy("ImfLock.class")
+ private boolean switchToInputMethodLocked(String imeId, @UserIdInt int userId) {
+ if (userId == mSettings.getCurrentUserId()) {
+ if (!mMethodMap.containsKey(imeId)
+ || !mSettings.getEnabledInputMethodListLocked()
+ .contains(mMethodMap.get(imeId))) {
return false; // IME is not found or not enabled.
}
- settings.putSelectedInputMethod(imeId);
- settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ setInputMethodLocked(imeId, NOT_A_SUBTYPE_ID);
return true;
}
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = new InputMethodSettings(
+ mContext.getResources(), mContext.getContentResolver(), methodMap,
+ userId, false);
+ if (!methodMap.containsKey(imeId)
+ || !settings.getEnabledInputMethodListLocked()
+ .contains(methodMap.get(imeId))) {
+ return false; // IME is not found or not enabled.
+ }
+ settings.putSelectedInputMethod(imeId);
+ settings.putSelectedSubtype(NOT_A_SUBTYPE_ID);
+ return true;
}
- private boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
- synchronized (ImfLock.class) {
- if (userId == mSettings.getCurrentUserId()) {
- if (!mMethodMap.containsKey(imeId)) {
- return false; // IME is not found.
- }
- setInputMethodEnabledLocked(imeId, enabled);
- return true;
- }
- final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
- final InputMethodSettings settings = new InputMethodSettings(
- mContext.getResources(), mContext.getContentResolver(), methodMap,
- userId, false);
- if (!methodMap.containsKey(imeId)) {
- return false; // IME is not found.
- }
- if (enabled) {
- if (!settings.getEnabledInputMethodListLocked().contains(methodMap.get(imeId))) {
- settings.appendAndPutEnabledInputMethodLocked(imeId, false);
- }
- } else {
- settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
- new StringBuilder(),
- settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
- }
- return true;
- }
+ private void publishLocalService() {
+ LocalServices.addService(InputMethodManagerInternal.class, new LocalServiceImpl());
}
- private boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
- int displayId) {
- //TODO(b/150843766): Check if Input Token is valid.
- final IBinder curHostInputToken;
- synchronized (ImfLock.class) {
- if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
- return false;
- }
- curHostInputToken = mCurHostInputToken;
- }
- return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
- }
-
- private void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
- synchronized (ImfLock.class) {
- if (mCurFocusedWindow != windowToken) {
- // mCurPerceptible was set by the focused window, but it is no longer in control,
- // so we reset mCurPerceptible.
- mCurPerceptible = true;
- }
- if (imeParentChanged) {
- // Hide the IME method menu earlier when the IME surface parent will change in
- // case seeing the dialog dismiss flickering during the next focused window
- // starting the input connection.
- mMenuController.hideInputMethodMenu();
- }
- }
- }
-
- private static final class LocalServiceImpl extends InputMethodManagerInternal {
- @NonNull
- private final InputMethodManagerService mService;
-
- LocalServiceImpl(@NonNull InputMethodManagerService service) {
- mService = service;
- }
+ private final class LocalServiceImpl extends InputMethodManagerInternal {
@Override
public void setInteractive(boolean interactive) {
// Do everything in handler so as not to block the caller.
- mService.mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0)
- .sendToTarget();
+ mHandler.obtainMessage(MSG_SET_INTERACTIVE, interactive ? 1 : 0, 0).sendToTarget();
}
@Override
public void hideCurrentInputMethod(@SoftInputShowHideReason int reason) {
- mService.mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
- mService.mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
+ mHandler.removeMessages(MSG_HIDE_CURRENT_INPUT_METHOD);
+ mHandler.obtainMessage(MSG_HIDE_CURRENT_INPUT_METHOD, reason).sendToTarget();
}
@Override
public List<InputMethodInfo> getInputMethodListAsUser(@UserIdInt int userId) {
- return mService.getInputMethodListAsUser(userId);
+ synchronized (ImfLock.class) {
+ return getInputMethodListLocked(userId, DirectBootAwareness.AUTO);
+ }
}
@Override
public List<InputMethodInfo> getEnabledInputMethodListAsUser(@UserIdInt int userId) {
- return mService.getEnabledInputMethodListAsUser(userId);
+ synchronized (ImfLock.class) {
+ return getEnabledInputMethodListLocked(userId);
+ }
}
@Override
public void onCreateInlineSuggestionsRequest(@UserIdInt int userId,
InlineSuggestionsRequestInfo requestInfo, IInlineSuggestionsRequestCallback cb) {
- mService.onCreateInlineSuggestionsRequest(userId, requestInfo, cb);
+ synchronized (ImfLock.class) {
+ onCreateInlineSuggestionsRequestLocked(userId, requestInfo, cb);
+ }
}
@Override
public boolean switchToInputMethod(String imeId, @UserIdInt int userId) {
- return mService.switchToInputMethod(imeId, userId);
+ synchronized (ImfLock.class) {
+ return switchToInputMethodLocked(imeId, userId);
+ }
}
@Override
public boolean setInputMethodEnabled(String imeId, boolean enabled, @UserIdInt int userId) {
- return mService.setInputMethodEnabled(imeId, enabled, userId);
+ synchronized (ImfLock.class) {
+ if (userId == mSettings.getCurrentUserId()) {
+ if (!mMethodMap.containsKey(imeId)) {
+ return false; // IME is not found.
+ }
+ setInputMethodEnabledLocked(imeId, enabled);
+ return true;
+ }
+ final ArrayMap<String, InputMethodInfo> methodMap = queryMethodMapForUser(userId);
+ final InputMethodSettings settings = new InputMethodSettings(
+ mContext.getResources(), mContext.getContentResolver(), methodMap,
+ userId, false);
+ if (!methodMap.containsKey(imeId)) {
+ return false; // IME is not found.
+ }
+ if (enabled) {
+ if (!settings.getEnabledInputMethodListLocked().contains(
+ methodMap.get(imeId))) {
+ settings.appendAndPutEnabledInputMethodLocked(imeId, false);
+ }
+ } else {
+ settings.buildAndPutEnabledInputMethodsStrRemovingIdLocked(
+ new StringBuilder(),
+ settings.getEnabledInputMethodsAndSubtypeListLocked(), imeId);
+ }
+ return true;
+ }
}
@Override
public void registerInputMethodListListener(InputMethodListListener listener) {
- mService.mInputMethodListListeners.addIfAbsent(listener);
+ mInputMethodListListeners.addIfAbsent(listener);
}
@Override
public boolean transferTouchFocusToImeWindow(@NonNull IBinder sourceInputToken,
int displayId) {
- return mService.transferTouchFocusToImeWindow(sourceInputToken, displayId);
+ //TODO(b/150843766): Check if Input Token is valid.
+ final IBinder curHostInputToken;
+ synchronized (ImfLock.class) {
+ if (displayId != mCurTokenDisplayId || mCurHostInputToken == null) {
+ return false;
+ }
+ curHostInputToken = mCurHostInputToken;
+ }
+ return mInputManagerInternal.transferTouchFocus(sourceInputToken, curHostInputToken);
}
@Override
public void reportImeControl(@Nullable IBinder windowToken, boolean imeParentChanged) {
- mService.reportImeControl(windowToken, imeParentChanged);
+ synchronized (ImfLock.class) {
+ if (mCurFocusedWindow != windowToken) {
+ // mCurPerceptible was set by the focused window, but it is no longer in
+ // control, so we reset mCurPerceptible.
+ mCurPerceptible = true;
+ }
+ if (imeParentChanged) {
+ // Hide the IME method menu earlier when the IME surface parent will change in
+ // case seeing the dialog dismiss flickering during the next focused window
+ // starting the input connection.
+ mMenuController.hideInputMethodMenu();
+ }
+ }
}
@Override
public void removeImeSurface() {
- mService.mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
+ mHandler.obtainMessage(MSG_REMOVE_IME_SURFACE).sendToTarget();
}
@Override
public void updateImeWindowStatus(boolean disableImeIcon) {
- mService.mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
+ mHandler.obtainMessage(MSG_UPDATE_IME_WINDOW_STATUS, disableImeIcon ? 1 : 0, 0)
.sendToTarget();
}
}
@@ -5689,7 +5660,7 @@
if (!userHasDebugPriv(userId, shellCommand)) {
continue;
}
- boolean failedToSelectUnknownIme = !switchToInputMethod(imeId, userId);
+ boolean failedToSelectUnknownIme = !switchToInputMethodLocked(imeId, userId);
if (failedToSelectUnknownIme) {
error.print("Unknown input method ");
error.print(imeId);
diff --git a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
index 134fb96..01aee7b 100644
--- a/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
+++ b/services/core/java/com/android/server/locales/LocaleManagerBackupHelper.java
@@ -31,13 +31,11 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
-import android.os.BestClock;
import android.os.Binder;
import android.os.HandlerThread;
import android.os.LocaleList;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Slog;
@@ -61,7 +59,6 @@
import java.nio.charset.StandardCharsets;
import java.time.Clock;
import java.time.Duration;
-import java.time.ZoneOffset;
import java.util.HashMap;
/**
@@ -97,15 +94,10 @@
LocaleManagerBackupHelper(LocaleManagerService localeManagerService,
PackageManagerInternal pmInternal) {
- this(localeManagerService.mContext, localeManagerService, pmInternal, getDefaultClock(),
+ this(localeManagerService.mContext, localeManagerService, pmInternal, Clock.systemUTC(),
new SparseArray<>());
}
- private static @NonNull Clock getDefaultClock() {
- return new BestClock(ZoneOffset.UTC, SystemClock.currentNetworkTimeClock(),
- Clock.systemUTC());
- }
-
@VisibleForTesting LocaleManagerBackupHelper(Context context,
LocaleManagerService localeManagerService,
PackageManagerInternal pmInternal, Clock clock, SparseArray<StagedData> stagedData) {
diff --git a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
index 5036a6e..bfef978 100644
--- a/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssNmeaProvider.java
@@ -63,16 +63,25 @@
@Override
protected boolean registerWithService(Void ignored,
Collection<GnssListenerRegistration> registrations) {
- if (D) {
- Log.d(TAG, "starting gnss nmea messages");
+ if (mGnssNative.startNmeaMessageCollection()) {
+ if (D) {
+ Log.d(TAG, "starting gnss nmea messages collection");
+ }
+ return true;
+ } else {
+ Log.e(TAG, "error starting gnss nmea messages collection");
+ return false;
}
- return true;
}
@Override
protected void unregisterWithService() {
- if (D) {
- Log.d(TAG, "stopping gnss nmea messages");
+ if (mGnssNative.stopNmeaMessageCollection()) {
+ if (D) {
+ Log.d(TAG, "stopping gnss nmea messages collection");
+ }
+ } else {
+ Log.e(TAG, "error stopping gnss nmea messages collection");
}
}
diff --git a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
index 936283d..0ce36d6 100644
--- a/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
+++ b/services/core/java/com/android/server/location/gnss/GnssStatusProvider.java
@@ -43,6 +43,7 @@
private final AppOpsHelper mAppOpsHelper;
private final LocationUsageLogger mLogger;
+ private final GnssNative mGnssNative;
private boolean mIsNavigating = false;
@@ -50,6 +51,7 @@
super(injector);
mAppOpsHelper = injector.getAppOpsHelper();
mLogger = injector.getLocationUsageLogger();
+ mGnssNative = gnssNative;
gnssNative.addBaseCallbacks(this);
gnssNative.addStatusCallbacks(this);
@@ -64,16 +66,25 @@
@Override
protected boolean registerWithService(Void ignored,
Collection<GnssListenerRegistration> registrations) {
- if (D) {
- Log.d(TAG, "starting gnss status");
+ if (mGnssNative.startSvStatusCollection()) {
+ if (D) {
+ Log.d(TAG, "starting gnss sv status");
+ }
+ return true;
+ } else {
+ Log.e(TAG, "error starting gnss sv status");
+ return false;
}
- return true;
}
@Override
protected void unregisterWithService() {
- if (D) {
- Log.d(TAG, "stopping gnss status");
+ if (mGnssNative.stopSvStatusCollection()) {
+ if (D) {
+ Log.d(TAG, "stopping gnss sv status");
+ }
+ } else {
+ Log.e(TAG, "error stopping gnss sv status");
}
}
diff --git a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
index e072bf7..af87677 100644
--- a/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
+++ b/services/core/java/com/android/server/location/gnss/hal/GnssNative.java
@@ -783,6 +783,38 @@
}
/**
+ * Starts sv status collection.
+ */
+ public boolean startSvStatusCollection() {
+ Preconditions.checkState(mRegistered);
+ return mGnssHal.startSvStatusCollection();
+ }
+
+ /**
+ * Stops sv status collection.
+ */
+ public boolean stopSvStatusCollection() {
+ Preconditions.checkState(mRegistered);
+ return mGnssHal.stopSvStatusCollection();
+ }
+
+ /**
+ * Starts NMEA message collection.
+ */
+ public boolean startNmeaMessageCollection() {
+ Preconditions.checkState(mRegistered);
+ return mGnssHal.startNmeaMessageCollection();
+ }
+
+ /**
+ * Stops NMEA message collection.
+ */
+ public boolean stopNmeaMessageCollection() {
+ Preconditions.checkState(mRegistered);
+ return mGnssHal.stopNmeaMessageCollection();
+ }
+
+ /**
* Returns true if measurement corrections are supported.
*/
public boolean isMeasurementCorrectionsSupported() {
@@ -1369,6 +1401,22 @@
return native_inject_measurement_corrections(corrections);
}
+ protected boolean startSvStatusCollection() {
+ return native_start_sv_status_collection();
+ }
+
+ protected boolean stopSvStatusCollection() {
+ return native_stop_sv_status_collection();
+ }
+
+ protected boolean startNmeaMessageCollection() {
+ return native_start_nmea_message_collection();
+ }
+
+ protected boolean stopNmeaMessageCollection() {
+ return native_stop_nmea_message_collection();
+ }
+
protected int getBatchSize() {
return native_get_batch_size();
}
@@ -1478,6 +1526,10 @@
private static native int native_read_nmea(byte[] buffer, int bufferSize);
+ private static native boolean native_start_nmea_message_collection();
+
+ private static native boolean native_stop_nmea_message_collection();
+
// location injection APIs
private static native void native_inject_location(
@@ -1501,6 +1553,11 @@
private static native void native_inject_time(long time, long timeReference, int uncertainty);
+ // sv status APIs
+ private static native boolean native_start_sv_status_collection();
+
+ private static native boolean native_stop_sv_status_collection();
+
// navigation message APIs
private static native boolean native_is_navigation_message_supported();
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/net/NetworkPolicyManagerService.java b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
index 8ce67a6..76d06c8 100644
--- a/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
+++ b/services/core/java/com/android/server/net/NetworkPolicyManagerService.java
@@ -119,9 +119,7 @@
import static com.android.internal.util.XmlUtils.readIntAttribute;
import static com.android.internal.util.XmlUtils.readLongAttribute;
import static com.android.internal.util.XmlUtils.readStringAttribute;
-import static com.android.internal.util.XmlUtils.readThisIntArrayXml;
import static com.android.internal.util.XmlUtils.writeBooleanAttribute;
-import static com.android.internal.util.XmlUtils.writeIntArrayXml;
import static com.android.internal.util.XmlUtils.writeIntAttribute;
import static com.android.internal.util.XmlUtils.writeLongAttribute;
import static com.android.internal.util.XmlUtils.writeStringAttribute;
@@ -246,7 +244,6 @@
import com.android.internal.util.DumpUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.internal.util.StatLogger;
-import com.android.internal.util.XmlUtils;
import com.android.net.module.util.NetworkIdentityUtils;
import com.android.net.module.util.NetworkStatsUtils;
import com.android.net.module.util.PermissionUtils;
@@ -260,8 +257,6 @@
import libcore.io.IoUtils;
-import org.xmlpull.v1.XmlPullParserException;
-
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
@@ -336,7 +331,8 @@
private static final int VERSION_ADDED_CYCLE = 11;
private static final int VERSION_ADDED_NETWORK_TYPES = 12;
private static final int VERSION_SUPPORTED_CARRIER_USAGE = 13;
- private static final int VERSION_LATEST = VERSION_SUPPORTED_CARRIER_USAGE;
+ private static final int VERSION_REMOVED_SUBSCRIPTION_PLANS = 14;
+ private static final int VERSION_LATEST = VERSION_REMOVED_SUBSCRIPTION_PLANS;
@VisibleForTesting
public static final int TYPE_WARNING = SystemMessage.NOTE_NET_WARNING;
@@ -349,7 +345,6 @@
private static final String TAG_POLICY_LIST = "policy-list";
private static final String TAG_NETWORK_POLICY = "network-policy";
- private static final String TAG_SUBSCRIPTION_PLAN = "subscription-plan";
private static final String TAG_UID_POLICY = "uid-policy";
private static final String TAG_APP_POLICY = "app-policy";
private static final String TAG_WHITELIST = "whitelist";
@@ -426,6 +421,13 @@
* obj = oldBlockedReasons
*/
private static final int MSG_BLOCKED_REASON_CHANGED = 21;
+ /**
+ * Message to indicate that subscription plans expired and should be cleared.
+ * arg1 = subId
+ * arg2 = setSubscriptionPlans call ID
+ * obj = callingPackage
+ */
+ private static final int MSG_CLEAR_SUBSCRIPTION_PLANS = 22;
private static final int UID_MSG_STATE_CHANGED = 100;
private static final int UID_MSG_GONE = 101;
@@ -492,6 +494,12 @@
/** Map from subId to package name that owns subscription plans. */
@GuardedBy("mNetworkPoliciesSecondLock")
final SparseArray<String> mSubscriptionPlansOwner = new SparseArray<>();
+ /** Map from subId to the ID of the clear plans request. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ final SparseIntArray mSetSubscriptionPlansIds = new SparseIntArray();
+ /** Atomic integer to generate a new ID for each clear plans request. */
+ @GuardedBy("mNetworkPoliciesSecondLock")
+ int mSetSubscriptionPlansIdCounter = 0;
/** Map from subId to daily opportunistic quota. */
@GuardedBy("mNetworkPoliciesSecondLock")
@@ -2523,56 +2531,6 @@
warningBytes, limitBytes, lastWarningSnooze,
lastLimitSnooze, metered, inferred));
}
-
- } else if (TAG_SUBSCRIPTION_PLAN.equals(tag)) {
- final String start = readStringAttribute(in, ATTR_CYCLE_START);
- final String end = readStringAttribute(in, ATTR_CYCLE_END);
- final String period = readStringAttribute(in, ATTR_CYCLE_PERIOD);
- final SubscriptionPlan.Builder builder = new SubscriptionPlan.Builder(
- RecurrenceRule.convertZonedDateTime(start),
- RecurrenceRule.convertZonedDateTime(end),
- RecurrenceRule.convertPeriod(period));
- builder.setTitle(readStringAttribute(in, ATTR_TITLE));
- builder.setSummary(readStringAttribute(in, ATTR_SUMMARY));
-
- final long limitBytes = readLongAttribute(in, ATTR_LIMIT_BYTES,
- SubscriptionPlan.BYTES_UNKNOWN);
- final int limitBehavior = readIntAttribute(in, ATTR_LIMIT_BEHAVIOR,
- SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN);
- if (limitBytes != SubscriptionPlan.BYTES_UNKNOWN
- && limitBehavior != SubscriptionPlan.LIMIT_BEHAVIOR_UNKNOWN) {
- builder.setDataLimit(limitBytes, limitBehavior);
- }
-
- final long usageBytes = readLongAttribute(in, ATTR_USAGE_BYTES,
- SubscriptionPlan.BYTES_UNKNOWN);
- final long usageTime = readLongAttribute(in, ATTR_USAGE_TIME,
- SubscriptionPlan.TIME_UNKNOWN);
- if (usageBytes != SubscriptionPlan.BYTES_UNKNOWN
- && usageTime != SubscriptionPlan.TIME_UNKNOWN) {
- builder.setDataUsage(usageBytes, usageTime);
- }
-
- final int subId = readIntAttribute(in, ATTR_SUB_ID);
- final String ownerPackage = readStringAttribute(in, ATTR_OWNER_PACKAGE);
-
- if (version >= VERSION_ADDED_NETWORK_TYPES) {
- final int depth = in.getDepth();
- while (XmlUtils.nextElementWithin(in, depth)) {
- if (TAG_XML_UTILS_INT_ARRAY.equals(in.getName())
- && ATTR_NETWORK_TYPES.equals(
- readStringAttribute(in, ATTR_XML_UTILS_NAME))) {
- final int[] networkTypes =
- readThisIntArrayXml(in, TAG_XML_UTILS_INT_ARRAY, null);
- builder.setNetworkTypes(networkTypes);
- }
- }
- }
-
- final SubscriptionPlan plan = builder.build();
- mSubscriptionPlans.put(subId, ArrayUtils.appendElement(
- SubscriptionPlan.class, mSubscriptionPlans.get(subId), plan));
- mSubscriptionPlansOwner.put(subId, ownerPackage);
} else if (TAG_UID_POLICY.equals(tag)) {
final int uid = readIntAttribute(in, ATTR_UID);
final int policy = readIntAttribute(in, ATTR_POLICY);
@@ -2763,38 +2721,6 @@
out.endTag(null, TAG_NETWORK_POLICY);
}
- // write all known subscription plans
- for (int i = 0; i < mSubscriptionPlans.size(); i++) {
- final int subId = mSubscriptionPlans.keyAt(i);
- if (subId == INVALID_SUBSCRIPTION_ID) continue;
- final String ownerPackage = mSubscriptionPlansOwner.get(subId);
- final SubscriptionPlan[] plans = mSubscriptionPlans.valueAt(i);
- if (ArrayUtils.isEmpty(plans)) continue;
-
- for (SubscriptionPlan plan : plans) {
- out.startTag(null, TAG_SUBSCRIPTION_PLAN);
- writeIntAttribute(out, ATTR_SUB_ID, subId);
- writeStringAttribute(out, ATTR_OWNER_PACKAGE, ownerPackage);
- final RecurrenceRule cycleRule = plan.getCycleRule();
- writeStringAttribute(out, ATTR_CYCLE_START,
- RecurrenceRule.convertZonedDateTime(cycleRule.start));
- writeStringAttribute(out, ATTR_CYCLE_END,
- RecurrenceRule.convertZonedDateTime(cycleRule.end));
- writeStringAttribute(out, ATTR_CYCLE_PERIOD,
- RecurrenceRule.convertPeriod(cycleRule.period));
- writeStringAttribute(out, ATTR_TITLE, plan.getTitle());
- writeStringAttribute(out, ATTR_SUMMARY, plan.getSummary());
- writeLongAttribute(out, ATTR_LIMIT_BYTES, plan.getDataLimitBytes());
- writeIntAttribute(out, ATTR_LIMIT_BEHAVIOR, plan.getDataLimitBehavior());
- writeLongAttribute(out, ATTR_USAGE_BYTES, plan.getDataUsageBytes());
- writeLongAttribute(out, ATTR_USAGE_TIME, plan.getDataUsageTime());
- try {
- writeIntArrayXml(plan.getNetworkTypes(), ATTR_NETWORK_TYPES, out);
- } catch (XmlPullParserException ignored) { }
- out.endTag(null, TAG_SUBSCRIPTION_PLAN);
- }
- }
-
// write all known uid policies
for (int i = 0; i < mUidPolicy.size(); i++) {
final int uid = mUidPolicy.keyAt(i);
@@ -3668,7 +3594,8 @@
}
@Override
- public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage) {
+ public void setSubscriptionPlans(int subId, SubscriptionPlan[] plans,
+ long expirationDurationMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
enforceSubscriptionPlanValidity(plans);
@@ -3678,34 +3605,47 @@
final long token = Binder.clearCallingIdentity();
try {
- synchronized (mUidRulesFirstLock) {
- synchronized (mNetworkPoliciesSecondLock) {
- mSubscriptionPlans.put(subId, plans);
- mSubscriptionPlansOwner.put(subId, callingPackage);
-
- final String subscriberId = mSubIdToSubscriberId.get(subId, null);
- if (subscriberId != null) {
- ensureActiveCarrierPolicyAL(subId, subscriberId);
- maybeUpdateCarrierPolicyCycleAL(subId, subscriberId);
- } else {
- Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
- }
-
- handleNetworkPoliciesUpdateAL(true);
- }
- }
-
- final Intent intent = new Intent(SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
- mContext.sendBroadcast(intent, android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
- mHandler.sendMessage(
- mHandler.obtainMessage(MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
+ setSubscriptionPlansInternal(subId, plans, expirationDurationMillis, callingPackage);
} finally {
Binder.restoreCallingIdentity(token);
}
}
+ private void setSubscriptionPlansInternal(int subId, SubscriptionPlan[] plans,
+ long expirationDurationMillis, String callingPackage) {
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ mSubscriptionPlans.put(subId, plans);
+ mSubscriptionPlansOwner.put(subId, callingPackage);
+
+ final String subscriberId = mSubIdToSubscriberId.get(subId, null);
+ if (subscriberId != null) {
+ ensureActiveCarrierPolicyAL(subId, subscriberId);
+ maybeUpdateCarrierPolicyCycleAL(subId, subscriberId);
+ } else {
+ Slog.wtf(TAG, "Missing subscriberId for subId " + subId);
+ }
+
+ handleNetworkPoliciesUpdateAL(true);
+
+ final Intent intent = new Intent(
+ SubscriptionManager.ACTION_SUBSCRIPTION_PLANS_CHANGED);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra(SubscriptionManager.EXTRA_SUBSCRIPTION_INDEX, subId);
+ mContext.sendBroadcast(intent,
+ android.Manifest.permission.MANAGE_SUBSCRIPTION_PLANS);
+ mHandler.sendMessage(mHandler.obtainMessage(
+ MSG_SUBSCRIPTION_PLANS_CHANGED, subId, 0, plans));
+ final int setPlansId = mSetSubscriptionPlansIdCounter++;
+ mSetSubscriptionPlansIds.put(subId, setPlansId);
+ if (expirationDurationMillis > 0) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_CLEAR_SUBSCRIPTION_PLANS,
+ subId, setPlansId, callingPackage), expirationDurationMillis);
+ }
+ }
+ }
+ }
+
/**
* Only visible for testing purposes. This doesn't give any access to
* existing plans; it simply lets the debug package define new plans.
@@ -3728,7 +3668,7 @@
@Override
public void setSubscriptionOverride(int subId, int overrideMask, int overrideValue,
- int[] networkTypes, long timeoutMillis, String callingPackage) {
+ int[] networkTypes, long expirationDurationMillis, String callingPackage) {
enforceSubscriptionPlanAccess(subId, Binder.getCallingUid(), callingPackage);
final ArraySet<Integer> allNetworksSet = new ArraySet<>();
@@ -3766,10 +3706,10 @@
args.arg3 = overrideValue;
args.arg4 = applicableNetworks.toArray();
mHandler.sendMessage(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args));
- if (timeoutMillis > 0) {
+ if (expirationDurationMillis > 0) {
args.arg3 = 0;
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SUBSCRIPTION_OVERRIDE, args),
- timeoutMillis);
+ expirationDurationMillis);
}
}
}
@@ -5184,6 +5124,22 @@
mListeners.finishBroadcast();
return true;
}
+ case MSG_CLEAR_SUBSCRIPTION_PLANS: {
+ synchronized (mUidRulesFirstLock) {
+ synchronized (mNetworkPoliciesSecondLock) {
+ int subId = msg.arg1;
+ if (msg.arg2 == mSetSubscriptionPlansIds.get(subId)) {
+ if (LOGD) Slog.d(TAG, "Clearing expired subscription plans.");
+ setSubscriptionPlansInternal(subId, new SubscriptionPlan[]{},
+ 0 /* expirationDurationMillis */,
+ (String) msg.obj /* callingPackage */);
+ } else {
+ if (LOGD) Slog.d(TAG, "Ignoring stale CLEAR_SUBSCRIPTION_PLANS.");
+ }
+ }
+ }
+ return true;
+ }
case MSG_BLOCKED_REASON_CHANGED: {
final int uid = msg.arg1;
final int newBlockedReasons = msg.arg2;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index 6b27321f..bc38087 100755
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -2463,7 +2463,7 @@
};
mAllowFgsDismissal = DeviceConfig.getBoolean(
DeviceConfig.NAMESPACE_SYSTEMUI,
- SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, false);
+ SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, true);
DeviceConfig.addOnPropertiesChangedListener(
DeviceConfig.NAMESPACE_SYSTEMUI,
new HandlerExecutor(mHandler),
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/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java
index 9f086e6..d745a23 100644
--- a/services/core/java/com/android/server/pm/AppDataHelper.java
+++ b/services/core/java/com/android/server/pm/AppDataHelper.java
@@ -48,8 +48,8 @@
import com.android.server.pm.dex.ArtManagerService;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.parsing.pkg.AndroidPackageUtils;
-import com.android.server.pm.pkg.SELinuxUtil;
import com.android.server.pm.pkg.PackageStateInternal;
+import com.android.server.pm.pkg.SELinuxUtil;
import dalvik.system.VMRuntime;
@@ -549,6 +549,10 @@
}
public void migrateKeyStoreData(int previousAppId, int appId) {
+ // If previous UID is system UID, declaring inheritKeyStoreKeys is not supported.
+ // Silently ignore the request to migrate keys.
+ if (previousAppId == Process.SYSTEM_UID) return;
+
for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) {
int srcUid = UserHandle.getUid(userId, previousAppId);
int destUid = UserHandle.getUid(userId, appId);
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/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3549c9e..ee5c638 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -247,6 +247,7 @@
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxy;
import com.android.server.pm.verify.domain.proxy.DomainVerificationProxyV1;
import com.android.server.storage.DeviceStorageMonitorInternal;
+import com.android.server.supplementalprocess.SupplementalProcessManagerLocal;
import com.android.server.utils.SnapshotCache;
import com.android.server.utils.TimingsTraceAndSlog;
import com.android.server.utils.Watchable;
@@ -934,6 +935,7 @@
final @Nullable String mOverlayConfigSignaturePackage;
final @Nullable String mRecentsPackage;
final @Nullable String mAmbientContextDetectionPackage;
+ private final @NonNull String mRequiredSupplementalProcessPackage;
@GuardedBy("mLock")
private final PackageUsage mPackageUsage = new PackageUsage();
@@ -1671,6 +1673,7 @@
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
+ mRequiredSupplementalProcessPackage = testParams.requiredSupplementalProcessPackage;
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
@@ -1695,6 +1698,7 @@
mResolveIntentHelper = testParams.resolveIntentHelper;
mDexOptHelper = testParams.dexOptHelper;
mSuspendPackageHelper = testParams.suspendPackageHelper;
+
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
registerObservers(false);
@@ -2138,6 +2142,9 @@
getPackageInfo(mRequiredPermissionControllerPackage, 0,
UserHandle.USER_SYSTEM).getLongVersionCode());
+ // Resolve the supplemental process
+ mRequiredSupplementalProcessPackage = getRequiredSupplementalProcessPackageName();
+
// Initialize InstantAppRegistry's Instant App list for all users.
for (AndroidPackage pkg : mPackages.values()) {
if (pkg.isSystem()) {
@@ -3153,6 +3160,11 @@
throw new IllegalStateException("PermissionController is not found");
}
+ @Override
+ public String getSupplementalProcessPackageName() {
+ return mRequiredSupplementalProcessPackage;
+ }
+
String getPackageInstallerPackageName() {
return mRequiredInstallerPackage;
}
@@ -5518,6 +5530,24 @@
}
}
+ private @NonNull String getRequiredSupplementalProcessPackageName() {
+ final Intent intent = new Intent(SupplementalProcessManagerLocal.SERVICE_INTERFACE);
+
+ final List<ResolveInfo> matches = queryIntentServicesInternal(
+ intent,
+ /* resolvedType= */ null,
+ MATCH_SYSTEM_ONLY | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE,
+ UserHandle.USER_SYSTEM,
+ /* callingUid= */ Process.myUid(),
+ /* includeInstantApps= */ false);
+ if (matches.size() == 1) {
+ return matches.get(0).getComponentInfo().packageName;
+ } else {
+ throw new RuntimeException("There should exactly one supplemental process; found "
+ + matches.size() + ": matches=" + matches);
+ }
+ }
+
@Override
public String getDefaultTextClassifierPackageName() {
return ensureSystemPackageName(
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 1caa76d..d12c826 100644
--- a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
+++ b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
@@ -89,6 +89,7 @@
public @Nullable String defaultTextClassifierPackage;
public @Nullable String systemTextClassifierPackage;
public @Nullable String overlayConfigSignaturePackage;
+ public @NonNull String requiredSupplementalProcessPackage;
public ViewCompiler viewCompiler;
public @Nullable String retailDemoPackage;
public @Nullable String recentsPackage;
diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java
index d2abc69..bd32d03 100644
--- a/services/core/java/com/android/server/pm/PackageSetting.java
+++ b/services/core/java/com/android/server/pm/PackageSetting.java
@@ -29,10 +29,8 @@
import android.content.pm.SharedLibraryInfo;
import android.content.pm.SigningDetails;
import android.content.pm.SigningInfo;
-import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
import android.content.pm.overlay.OverlayPaths;
-import android.os.PersistableBundle;
import android.os.UserHandle;
import android.service.pm.PackageProto;
import android.util.ArrayMap;
@@ -50,7 +48,6 @@
import com.android.server.pm.pkg.PackageState;
import com.android.server.pm.pkg.PackageStateInternal;
import com.android.server.pm.pkg.PackageStateUnserialized;
-import com.android.server.pm.pkg.PackageUserState;
import com.android.server.pm.pkg.PackageUserStateImpl;
import com.android.server.pm.pkg.PackageUserStateInternal;
import com.android.server.pm.pkg.SuspendParams;
@@ -662,10 +659,10 @@
usesStaticLibrariesVersions = other.usesStaticLibrariesVersions != null
? Arrays.copyOf(other.usesStaticLibrariesVersions,
other.usesStaticLibrariesVersions.length) : null;
-
mUserStates.clear();
for (int i = 0; i < other.mUserStates.size(); i++) {
- mUserStates.put(other.mUserStates.keyAt(i), other.mUserStates.valueAt(i));
+ mUserStates.put(other.mUserStates.keyAt(i),
+ new PackageUserStateImpl(this, other.mUserStates.valueAt(i)));
}
if (mOldCodePaths != null) {
@@ -686,7 +683,7 @@
PackageUserStateImpl modifyUserState(int userId) {
PackageUserStateImpl state = mUserStates.get(userId);
if (state == null) {
- state = new PackageUserStateImpl();
+ state = new PackageUserStateImpl(this);
mUserStates.put(userId, state);
onChanged();
}
@@ -696,7 +693,7 @@
public PackageUserStateImpl getOrCreateUserState(@UserIdInt int userId) {
PackageUserStateImpl state = mUserStates.get(userId);
if (state == null) {
- state = new PackageUserStateImpl();
+ state = new PackageUserStateImpl(this);
mUserStates.put(userId, state);
}
return state;
@@ -1491,10 +1488,10 @@
}
@DataClass.Generated(
- time = 1640923794772L,
+ time = 1643648635766L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/PackageSetting.java",
- inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n java.lang.String getLastDisabledAppCaller(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n @android.annotation.NonNull java.util.Map<java.lang.String,android.content.pm.overlay.OverlayPaths> getOverlayPathsForLibrary(int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\n boolean getSuspended(int)\n boolean addOrUpdateSuspension(java.lang.String,android.content.pm.SuspendDialogInfo,android.os.PersistableBundle,android.os.PersistableBundle,int)\n boolean removeSuspension(java.lang.String,int)\n void removeSuspension(java.util.function.Predicate<java.lang.String>,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n void setHarmfulAppWarning(int,java.lang.String)\n java.lang.String getHarmfulAppWarning(int)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic void setSplashScreenTheme(int,java.lang.String)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override java.lang.Integer getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
+ inputSignatures = "private int sharedUserId\nprivate @android.annotation.Nullable java.util.Map<java.lang.String,java.util.Set<java.lang.String>> mimeGroups\nprivate @java.lang.Deprecated @android.annotation.Nullable java.util.Set<java.lang.String> mOldCodePaths\nprivate @android.annotation.Nullable java.lang.String[] usesSdkLibraries\nprivate @android.annotation.Nullable long[] usesSdkLibrariesVersionsMajor\nprivate @android.annotation.Nullable java.lang.String[] usesStaticLibraries\nprivate @android.annotation.Nullable long[] usesStaticLibrariesVersions\nprivate @android.annotation.Nullable @java.lang.Deprecated java.lang.String legacyNativeLibraryPath\nprivate @android.annotation.NonNull java.lang.String mName\nprivate @android.annotation.Nullable java.lang.String mRealName\nprivate int mAppId\nprivate @android.annotation.Nullable com.android.server.pm.parsing.pkg.AndroidPackage pkg\nprivate @android.annotation.Nullable com.android.server.pm.SharedUserSetting sharedUser\nprivate @android.annotation.NonNull java.io.File mPath\nprivate @android.annotation.NonNull java.lang.String mPathString\nprivate float mLoadingProgress\nprivate @android.annotation.Nullable java.lang.String mPrimaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mSecondaryCpuAbi\nprivate @android.annotation.Nullable java.lang.String mCpuAbiOverride\nprivate long mLastModifiedTime\nprivate long lastUpdateTime\nprivate long versionCode\nprivate @android.annotation.NonNull com.android.server.pm.PackageSignatures signatures\nprivate boolean installPermissionsFixed\nprivate @android.annotation.NonNull com.android.server.pm.PackageKeySetData keySetData\nprivate final @android.annotation.NonNull android.util.SparseArray<com.android.server.pm.pkg.PackageUserStateImpl> mUserStates\nprivate @android.annotation.NonNull com.android.server.pm.InstallSource installSource\nprivate @android.annotation.Nullable java.lang.String volumeUuid\nprivate int categoryOverride\nprivate boolean updateAvailable\nprivate boolean forceQueryableOverride\nprivate final @android.annotation.NonNull com.android.server.pm.pkg.PackageStateUnserialized pkgState\nprivate @android.annotation.NonNull java.util.UUID mDomainSetId\nprivate final @android.annotation.NonNull com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> mSnapshot\nprivate com.android.server.utils.SnapshotCache<com.android.server.pm.PackageSetting> makeCache()\npublic com.android.server.pm.PackageSetting snapshot()\npublic void dumpDebug(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\npublic boolean isSharedUser()\npublic com.android.server.pm.PackageSetting setAppId(int)\npublic com.android.server.pm.PackageSetting setCpuAbiOverride(java.lang.String)\npublic com.android.server.pm.PackageSetting setFirstInstallTimeFromReplaced(com.android.server.pm.pkg.PackageStateInternal,int[])\npublic com.android.server.pm.PackageSetting setFirstInstallTime(long,int)\npublic com.android.server.pm.PackageSetting setForceQueryableOverride(boolean)\npublic com.android.server.pm.PackageSetting setInstallerPackageName(java.lang.String)\npublic com.android.server.pm.PackageSetting setInstallSource(com.android.server.pm.InstallSource)\n com.android.server.pm.PackageSetting removeInstallerPackage(java.lang.String)\npublic com.android.server.pm.PackageSetting setIsOrphaned(boolean)\npublic com.android.server.pm.PackageSetting setKeySetData(com.android.server.pm.PackageKeySetData)\npublic com.android.server.pm.PackageSetting setLastModifiedTime(long)\npublic com.android.server.pm.PackageSetting setLastUpdateTime(long)\npublic com.android.server.pm.PackageSetting setLongVersionCode(long)\npublic boolean setMimeGroup(java.lang.String,android.util.ArraySet<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPkg(com.android.server.pm.parsing.pkg.AndroidPackage)\npublic com.android.server.pm.PackageSetting setPkgStateLibraryFiles(java.util.Collection<java.lang.String>)\npublic com.android.server.pm.PackageSetting setPrimaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSecondaryCpuAbi(java.lang.String)\npublic com.android.server.pm.PackageSetting setSignatures(com.android.server.pm.PackageSignatures)\npublic com.android.server.pm.PackageSetting setVolumeUuid(java.lang.String)\npublic @java.lang.Override boolean isExternalStorage()\npublic com.android.server.pm.PackageSetting setUpdateAvailable(boolean)\npublic int getSharedUserIdInt()\npublic @java.lang.Override java.lang.String toString()\nprotected void copyMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic void updateFrom(com.android.server.pm.PackageSetting)\n com.android.server.pm.PackageSetting updateMimeGroups(java.util.Set<java.lang.String>)\npublic @java.lang.Deprecated @java.lang.Override com.android.server.pm.permission.LegacyPermissionState getLegacyPermissionState()\npublic com.android.server.pm.PackageSetting setInstallPermissionsFixed(boolean)\npublic boolean isPrivileged()\npublic boolean isOem()\npublic boolean isVendor()\npublic boolean isProduct()\npublic @java.lang.Override boolean isRequiredForSystemUser()\npublic boolean isSystemExt()\npublic boolean isOdm()\npublic boolean isSystem()\npublic android.content.pm.SigningDetails getSigningDetails()\npublic com.android.server.pm.PackageSetting setSigningDetails(android.content.pm.SigningDetails)\npublic void copyPackageSetting(com.android.server.pm.PackageSetting)\n @com.android.internal.annotations.VisibleForTesting com.android.server.pm.pkg.PackageUserStateImpl modifyUserState(int)\npublic com.android.server.pm.pkg.PackageUserStateImpl getOrCreateUserState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateInternal readUserState(int)\n void setEnabled(int,int,java.lang.String)\n int getEnabled(int)\n void setInstalled(boolean,int)\n boolean getInstalled(int)\n int getInstallReason(int)\n void setInstallReason(int,int)\n int getUninstallReason(int)\n void setUninstallReason(int,int)\n @android.annotation.NonNull android.content.pm.overlay.OverlayPaths getOverlayPaths(int)\n boolean setOverlayPathsForLibrary(java.lang.String,android.content.pm.overlay.OverlayPaths,int)\n boolean isAnyInstalled(int[])\n int[] queryInstalledUsers(int[],boolean)\n long getCeDataInode(int)\n void setCeDataInode(long,int)\n boolean getStopped(int)\n void setStopped(boolean,int)\n boolean getNotLaunched(int)\n void setNotLaunched(boolean,int)\n boolean getHidden(int)\n void setHidden(boolean,int)\n int getDistractionFlags(int)\n void setDistractionFlags(int,int)\npublic boolean getInstantApp(int)\n void setInstantApp(boolean,int)\n boolean getVirtualPreload(int)\n void setVirtualPreload(boolean,int)\n void setUserState(int,long,int,boolean,boolean,boolean,boolean,int,android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>,boolean,boolean,java.lang.String,android.util.ArraySet<java.lang.String>,android.util.ArraySet<java.lang.String>,int,int,java.lang.String,java.lang.String,long)\n void setUserState(int,com.android.server.pm.pkg.PackageUserStateInternal)\n android.util.ArraySet<java.lang.String> getEnabledComponents(int)\n android.util.ArraySet<java.lang.String> getDisabledComponents(int)\n void setEnabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponents(android.util.ArraySet<java.lang.String>,int)\n void setEnabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n void setDisabledComponentsCopy(android.util.ArraySet<java.lang.String>,int)\n com.android.server.pm.pkg.PackageUserStateImpl modifyUserStateComponents(int,boolean,boolean)\n void addDisabledComponent(java.lang.String,int)\n void addEnabledComponent(java.lang.String,int)\n boolean enableComponentLPw(java.lang.String,int)\n boolean disableComponentLPw(java.lang.String,int)\n boolean restoreComponentLPw(java.lang.String,int)\n int getCurrentEnabledStateLPr(java.lang.String,int)\n void removeUser(int)\npublic int[] getNotInstalledUserIds()\n void writePackageUserPermissionsProto(android.util.proto.ProtoOutputStream,long,java.util.List<android.content.pm.UserInfo>,com.android.server.pm.permission.LegacyPermissionDataProvider)\nprotected void writeUsersInfoToProto(android.util.proto.ProtoOutputStream,long)\n com.android.server.pm.PackageSetting setPath(java.io.File)\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideNonLocalizedLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer,int)\npublic void resetOverrideComponentLabelIcon(int)\npublic @android.annotation.Nullable java.lang.String getSplashScreenTheme(int)\npublic boolean isLoading()\npublic com.android.server.pm.PackageSetting setLoadingProgress(float)\npublic @android.annotation.NonNull @java.lang.Override long getVersionCode()\npublic @android.annotation.Nullable @java.lang.Override java.util.Map<java.lang.String,java.util.Set<java.lang.String>> getMimeGroups()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String getPackageName()\npublic @android.annotation.Nullable @java.lang.Override com.android.server.pm.pkg.AndroidPackageApi getAndroidPackage()\npublic @android.annotation.Nullable @java.lang.Override int getSharedUserId()\npublic @android.annotation.NonNull android.content.pm.SigningInfo getSigningInfo()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesSdkLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesSdkLibrariesVersionsMajor()\npublic @android.annotation.NonNull @java.lang.Override java.lang.String[] getUsesStaticLibraries()\npublic @android.annotation.NonNull @java.lang.Override long[] getUsesStaticLibrariesVersions()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<android.content.pm.SharedLibraryInfo> getUsesLibraryInfos()\npublic @android.annotation.NonNull @java.lang.Override java.util.List<java.lang.String> getUsesLibraryFiles()\npublic @java.lang.Override boolean isHiddenUntilInstalled()\npublic @android.annotation.NonNull @java.lang.Override long[] getLastPackageUsageTime()\npublic @java.lang.Override boolean isUpdatedSystemApp()\npublic com.android.server.pm.PackageSetting setDomainSetId(java.util.UUID)\npublic com.android.server.pm.PackageSetting setSharedUser(com.android.server.pm.SharedUserSetting)\npublic com.android.server.pm.PackageSetting setCategoryOverride(int)\npublic com.android.server.pm.PackageSetting setLegacyNativeLibraryPath(java.lang.String)\npublic com.android.server.pm.PackageSetting setMimeGroups(java.util.Map<java.lang.String,java.util.Set<java.lang.String>>)\npublic com.android.server.pm.PackageSetting setOldCodePaths(java.util.Set<java.lang.String>)\npublic com.android.server.pm.PackageSetting setUsesSdkLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesSdkLibrariesVersionsMajor(long[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibraries(java.lang.String[])\npublic com.android.server.pm.PackageSetting setUsesStaticLibrariesVersions(long[])\npublic @android.annotation.NonNull @java.lang.Override com.android.server.pm.pkg.PackageStateUnserialized getTransientState()\npublic @android.annotation.NonNull android.util.SparseArray<? extends PackageUserStateInternal> getUserStates()\npublic com.android.server.pm.PackageSetting addMimeTypes(java.lang.String,java.util.Set<java.lang.String>)\nclass PackageSetting extends com.android.server.pm.SettingBase implements [com.android.server.pm.pkg.PackageStateInternal]\n@com.android.internal.util.DataClass(genGetters=true, genConstructor=false, genSetters=false, genBuilder=false)")
@Deprecated
private void __metadata() {}
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index b07cd106..652080a 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -2515,13 +2515,17 @@
return 0 < getRemainingCreatableProfileCount(userType, userId, allowedToRemoveOne);
}
+ @Override
+ public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId) {
+ return getRemainingCreatableProfileCount(userType, userId, false);
+ }
+
/**
* Returns the remaining number of profiles of the given type that can be added to the given
* user. (taking into account the total number of users on the device as well as how many
* profiles exist of that type both in general and for the given user)
*/
- @Override
- public int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId,
+ private int getRemainingCreatableProfileCount(@NonNull String userType, @UserIdInt int userId,
boolean allowedToRemoveOne) {
checkQueryOrCreateUsersPermission(
"get the remaining number of profiles that can be added to the given user.");
diff --git a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
index d455be7..46fde4b 100644
--- a/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
+++ b/services/core/java/com/android/server/pm/permission/OneTimePermissionUserManager.java
@@ -108,14 +108,18 @@
* </p>
* @param packageName The package to start a one-time permission session for
* @param timeoutMillis Number of milliseconds for an app to be in an inactive state
+ * @param revokeAfterKilledDelayMillis Number of milliseconds to wait after the process dies
+ * before ending the session. Set to -1 to use default value
+ * for the device.
* @param importanceToResetTimer The least important level to uid must be to reset the timer
* @param importanceToKeepSessionAlive The least important level the uid must be to keep the
- * session alive
+ * session alive
*
* @hide
*/
void startPackageOneTimeSession(@NonNull String packageName, long timeoutMillis,
- int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ long revokeAfterKilledDelayMillis, int importanceToResetTimer,
+ int importanceToKeepSessionAlive) {
int uid;
try {
uid = mContext.getPackageManager().getPackageUid(packageName, 0);
@@ -126,11 +130,15 @@
synchronized (mLock) {
PackageInactivityListener listener = mListeners.get(uid);
- if (listener == null) {
- listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+ if (listener != null) {
+ listener.updateSessionParameters(timeoutMillis, revokeAfterKilledDelayMillis,
importanceToResetTimer, importanceToKeepSessionAlive);
- mListeners.put(uid, listener);
+ return;
}
+ listener = new PackageInactivityListener(uid, packageName, timeoutMillis,
+ revokeAfterKilledDelayMillis, importanceToResetTimer,
+ importanceToKeepSessionAlive);
+ mListeners.put(uid, listener);
}
}
@@ -159,18 +167,6 @@
}
/**
- * The delay to wait before revoking on the event an app is terminated. Recommended to be long
- * enough so that apps don't lose permission on an immediate restart
- */
- private long getKilledDelayMillis(boolean isSelfRevokedPermissionSession) {
- if (isSelfRevokedPermissionSession) {
- return 0;
- }
- return DeviceConfig.getLong(DeviceConfig.NAMESPACE_PERMISSIONS,
- PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS);
- }
-
- /**
* Register to listen for Uids being uninstalled. This must be done outside of the
* PermissionManagerService lock.
*/
@@ -178,18 +174,6 @@
mContext.registerReceiver(mUninstallListener, new IntentFilter(Intent.ACTION_UID_REMOVED));
}
- void setSelfRevokedPermissionSession(int uid) {
- synchronized (mLock) {
- PackageInactivityListener listener = mListeners.get(uid);
- if (listener == null) {
- Log.e(LOG_TAG, "Could not set session for uid " + uid
- + " as self-revoke session: session not found");
- return;
- }
- listener.setSelfRevokedPermissionSession();
- }
- }
-
/**
* A class which watches a package for inactivity and notifies the permission controller when
* the package becomes inactive
@@ -200,11 +184,11 @@
private final int mUid;
private final @NonNull String mPackageName;
- private final long mTimeout;
- private final int mImportanceToResetTimer;
- private final int mImportanceToKeepSessionAlive;
+ private long mTimeout;
+ private long mRevokeAfterKilledDelay;
+ private int mImportanceToResetTimer;
+ private int mImportanceToKeepSessionAlive;
- private boolean mIsSelfRevokedPermissionSession;
private boolean mIsAlarmSet;
private boolean mIsFinished;
@@ -218,16 +202,23 @@
private final Object mToken = new Object();
private PackageInactivityListener(int uid, @NonNull String packageName, long timeout,
- int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ long revokeAfterkilledDelay, int importanceToResetTimer,
+ int importanceToKeepSessionAlive) {
Log.i(LOG_TAG,
"Start tracking " + packageName + ". uid=" + uid + " timeout=" + timeout
+ + " killedDelay=" + revokeAfterkilledDelay
+ " importanceToResetTimer=" + importanceToResetTimer
+ " importanceToKeepSessionAlive=" + importanceToKeepSessionAlive);
mUid = uid;
mPackageName = packageName;
mTimeout = timeout;
+ mRevokeAfterKilledDelay = revokeAfterkilledDelay == -1
+ ? DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS, PROPERTY_KILLED_DELAY_CONFIG_KEY,
+ DEFAULT_KILLED_DELAY_MILLIS)
+ : revokeAfterkilledDelay;
mImportanceToResetTimer = importanceToResetTimer;
mImportanceToKeepSessionAlive = importanceToKeepSessionAlive;
@@ -247,6 +238,28 @@
onImportanceChanged(mUid, mActivityManager.getPackageImportance(packageName));
}
+ public void updateSessionParameters(long timeoutMillis, long revokeAfterKilledDelayMillis,
+ int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ synchronized (mInnerLock) {
+ mTimeout = Math.min(mTimeout, timeoutMillis);
+ mRevokeAfterKilledDelay = Math.min(mRevokeAfterKilledDelay,
+ revokeAfterKilledDelayMillis == -1
+ ? DeviceConfig.getLong(
+ DeviceConfig.NAMESPACE_PERMISSIONS,
+ PROPERTY_KILLED_DELAY_CONFIG_KEY, DEFAULT_KILLED_DELAY_MILLIS)
+ : revokeAfterKilledDelayMillis);
+ mImportanceToResetTimer = Math.min(importanceToResetTimer, mImportanceToResetTimer);
+ mImportanceToKeepSessionAlive = Math.min(importanceToKeepSessionAlive,
+ mImportanceToKeepSessionAlive);
+ Log.v(LOG_TAG,
+ "Updated params for " + mPackageName + ". timeout=" + mTimeout
+ + " killedDelay=" + mRevokeAfterKilledDelay
+ + " importanceToResetTimer=" + mImportanceToResetTimer
+ + " importanceToKeepSessionAlive=" + mImportanceToKeepSessionAlive);
+ onImportanceChanged(mUid, mActivityManager.getPackageImportance(mPackageName));
+ }
+ }
+
private void onImportanceChanged(int uid, int importance) {
if (uid != mUid) {
return;
@@ -271,7 +284,7 @@
}
onImportanceChanged(mUid, imp);
}
- }, mToken, getKilledDelayMillis(mIsSelfRevokedPermissionSession));
+ }, mToken, mRevokeAfterKilledDelay);
return;
}
if (importance > mImportanceToResetTimer) {
@@ -307,14 +320,6 @@
}
/**
- * Marks the session as a self-revoke session, which does not delay the revocation when
- * the app is restarting.
- */
- public void setSelfRevokedPermissionSession() {
- mIsSelfRevokedPermissionSession = true;
- }
-
- /**
* Set the alarm which will callback when the package is inactive
*/
@GuardedBy("mInnerLock")
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..698068d 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;
@@ -67,7 +68,6 @@
import android.util.SparseArray;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.util.Preconditions;
import com.android.internal.util.function.TriFunction;
import com.android.server.LocalServices;
@@ -387,7 +387,8 @@
@Override
public void startOneTimePermissionSession(String packageName, @UserIdInt int userId,
- long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) {
+ long timeoutMillis, long revokeAfterKilledDelayMillis, int importanceToResetTimer,
+ int importanceToKeepSessionAlive) {
mContext.enforceCallingOrSelfPermission(
Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS,
"Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS
@@ -397,7 +398,8 @@
final long token = Binder.clearCallingIdentity();
try {
getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName,
- timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive);
+ timeoutMillis, revokeAfterKilledDelayMillis, importanceToResetTimer,
+ importanceToKeepSessionAlive);
} finally {
Binder.restoreCallingIdentity(token);
}
@@ -562,16 +564,7 @@
@Override
public void revokeOwnPermissionsOnKill(@NonNull String packageName,
@NonNull List<String> permissions) {
- final int callingUid = Binder.getCallingUid();
- final int callingUserId = UserHandle.getUserId(callingUid);
- AndroidFuture<Void> future = new AndroidFuture<>();
- future.whenComplete((result, err) -> {
- if (err == null) {
- getOneTimePermissionUserManager(callingUserId)
- .setSelfRevokedPermissionSession(callingUid);
- }
- });
- mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions, future);
+ mPermissionManagerServiceImpl.revokeOwnPermissionsOnKill(packageName, permissions);
}
@Override
@@ -613,11 +606,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;
@@ -1316,7 +1310,7 @@
if (op < 0) {
// Bg location is one-off runtime modifier permission and has no app op
- if (sPlatformPermissions.contains(permission)
+ if (sPlatformPermissions.containsKey(permission)
&& !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)
&& !Manifest.permission.BODY_SENSORS_BACKGROUND.equals(permission)) {
Slog.wtf(LOG_TAG, "Platform runtime permission " + permission
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
index db9c1b5..ed351fd 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java
@@ -108,7 +108,6 @@
import com.android.internal.annotations.GuardedBy;
import com.android.internal.compat.IPlatformCompat;
-import com.android.internal.infra.AndroidFuture;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto;
import com.android.internal.os.RoSystemProperties;
@@ -1592,8 +1591,7 @@
}
@Override
- public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
- AndroidFuture<Void> callback) {
+ public void revokeOwnPermissionsOnKill(String packageName, List<String> permissions) {
final int callingUid = Binder.getCallingUid();
int callingUserId = UserHandle.getUserId(callingUid);
int targetPackageUid = mPackageManagerInt.getPackageUid(packageName, 0, callingUserId);
@@ -1608,8 +1606,7 @@
+ permName + " because it does not hold that permission");
}
}
- mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions,
- callback);
+ mPermissionControllerManager.revokeOwnPermissionsOnKill(packageName, permissions);
}
private boolean mayManageRolePermission(int uid) {
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
index 91c558b..3e28320 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java
@@ -27,7 +27,6 @@
import android.permission.IOnPermissionsChangeListener;
import android.permission.PermissionManagerInternal;
-import com.android.internal.infra.AndroidFuture;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import java.io.FileDescriptor;
@@ -338,16 +337,16 @@
* <li>Each permission in {@code permissions} must be a runtime permission.
* </ul>
* <p>
- * For every permission in {@code permissions}, the entire permission group it belongs to will
- * be revoked. This revocation happens asynchronously and kills all processes running in the
- * same UID as {@code packageName}. It will be triggered once it is safe to do so.
+ * Background permissions which have no corresponding foreground permission still granted once
+ * the revocation is effective will also be revoked.
+ * <p>
+ * This revocation happens asynchronously and kills all processes running in the same UID as
+ * {@code packageName}. It will be triggered once it is safe to do so.
*
* @param packageName The name of the package for which the permissions will be revoked.
* @param permissions List of permissions to be revoked.
- * @param callback Callback called when the revocation request has been completed.
*/
- void revokeOwnPermissionsOnKill(String packageName, List<String> permissions,
- AndroidFuture<Void> callback);
+ void revokeOwnPermissionsOnKill(String packageName, List<String> permissions);
/**
* Get whether you should show UI with rationale for requesting a permission. You should do this
diff --git a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
index 25abcb3..efb6144 100644
--- a/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
+++ b/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java
@@ -30,13 +30,13 @@
import com.android.internal.util.ArrayUtils;
import com.android.internal.util.CollectionUtils;
import com.android.internal.util.DataClass;
+import com.android.server.utils.Watchable;
import java.util.Objects;
@DataClass(genConstructor = false, genBuilder = false, genEqualsHashCode = true)
@DataClass.Suppress({"mOverlayPathsLock", "mOverlayPaths", "mSharedLibraryOverlayPathsLock",
- "mSharedLibraryOverlayPaths", "setOverlayPaths", "mCachedOverlayPathsLock",
- "mCachedOverlayPaths", "setCachedOverlayPaths"})
+ "mSharedLibraryOverlayPaths", "setOverlayPaths", "setCachedOverlayPaths"})
public class PackageUserStateImpl implements PackageUserStateInternal {
@Nullable
@@ -72,31 +72,37 @@
@Nullable
private String mSplashScreenTheme;
- /** Suspending package to suspend params */
+ /**
+ * Suspending package to suspend params
+ */
@Nullable
private ArrayMap<String, SuspendParams> mSuspendParams;
@Nullable
- private OverlayPaths mCachedOverlayPaths;
-
- @Nullable
private ArrayMap<ComponentName, Pair<String, Integer>> mComponentLabelIconOverrideMap;
private long mFirstInstallTime;
+ @Nullable
+ private final Watchable mWatchable;
+
public PackageUserStateImpl() {
super();
+ mWatchable = null;
}
- public PackageUserStateImpl(PackageUserStateImpl other) {
+ public PackageUserStateImpl(@NonNull Watchable watchable) {
+ mWatchable = watchable;
+ }
+
+ public PackageUserStateImpl(@NonNull Watchable watchable, PackageUserStateImpl other) {
+ mWatchable = watchable;
mDisabledComponents = ArrayUtils.cloneOrNull(other.mDisabledComponents);
mEnabledComponents = ArrayUtils.cloneOrNull(other.mEnabledComponents);
mOverlayPaths = other.mOverlayPaths;
if (other.mSharedLibraryOverlayPaths != null) {
mSharedLibraryOverlayPaths = new ArrayMap<>(other.mSharedLibraryOverlayPaths);
}
- mDisabledComponents = other.mDisabledComponents;
- mEnabledComponents = other.mEnabledComponents;
mCeDataInode = other.mCeDataInode;
mInstalled = other.mInstalled;
mStopped = other.mStopped;
@@ -110,16 +116,22 @@
mUninstallReason = other.mUninstallReason;
mHarmfulAppWarning = other.mHarmfulAppWarning;
mLastDisableAppCaller = other.mLastDisableAppCaller;
- mOverlayPaths = other.mOverlayPaths;
- mSharedLibraryOverlayPaths = other.mSharedLibraryOverlayPaths;
mSplashScreenTheme = other.mSplashScreenTheme;
mSuspendParams = other.mSuspendParams == null ? null : new ArrayMap<>(other.mSuspendParams);
mComponentLabelIconOverrideMap = other.mComponentLabelIconOverrideMap == null ? null
: new ArrayMap<>(other.mComponentLabelIconOverrideMap);
+ mFirstInstallTime = other.mFirstInstallTime;
+ }
+
+ private void onChanged() {
+ if (mWatchable != null) {
+ mWatchable.dispatchChange(mWatchable);
+ }
}
/**
* Sets the path of overlays currently enabled for this package and user combination.
+ *
* @return true if the path contents differ than what they were previously
*/
@Nullable
@@ -132,7 +144,7 @@
return false;
}
mOverlayPaths = paths;
- mCachedOverlayPaths = null;
+ onChanged();
return true;
}
@@ -150,11 +162,13 @@
if (Objects.equals(paths, currentPaths)) {
return false;
}
- mCachedOverlayPaths = null;
if (paths == null || paths.isEmpty()) {
- return mSharedLibraryOverlayPaths.remove(library) != null;
+ boolean returnValue = mSharedLibraryOverlayPaths.remove(library) != null;
+ onChanged();
+ return returnValue;
} else {
mSharedLibraryOverlayPaths.put(library, paths);
+ onChanged();
return true;
}
}
@@ -233,16 +247,18 @@
mComponentLabelIconOverrideMap.put(component, Pair.create(nonLocalizedLabel, icon));
}
+ onChanged();
}
return changed;
}
/**
- * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName,
- * String, Integer)}.
- *
- * This is done when the package is updated as the components and resource IDs may have changed.
+ * Clears all values previously set by {@link #overrideLabelAndIcon(ComponentName, String,
+ * Integer)}.
+ * <p>
+ * This is done when the package is updated as the components and resource IDs may have
+ * changed.
*/
public void resetOverrideComponentLabelIcon() {
mComponentLabelIconOverrideMap = null;
@@ -263,21 +279,157 @@
}
public PackageUserStateImpl putSuspendParams(@NonNull String suspendingPackage,
- @NonNull SuspendParams suspendParams) {
+ @Nullable SuspendParams suspendParams) {
if (mSuspendParams == null) {
mSuspendParams = new ArrayMap<>();
}
- mSuspendParams.put(suspendingPackage, suspendParams);
+ if (!mSuspendParams.containsKey(suspendingPackage)
+ || !Objects.equals(mSuspendParams.get(suspendingPackage), suspendParams)) {
+ mSuspendParams.put(suspendingPackage, suspendParams);
+ onChanged();
+ }
+
return this;
}
public PackageUserStateImpl removeSuspension(@NonNull String suspendingPackage) {
if (mSuspendParams != null) {
mSuspendParams.remove(suspendingPackage);
+ onChanged();
}
return this;
}
+ public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) {
+ mDisabledComponents = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) {
+ mEnabledComponents = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setCeDataInode(long value) {
+ mCeDataInode = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setInstalled(boolean value) {
+ mInstalled = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setStopped(boolean value) {
+ mStopped = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setNotLaunched(boolean value) {
+ mNotLaunched = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setHidden(boolean value) {
+ mHidden = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setDistractionFlags(int value) {
+ mDistractionFlags = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setInstantApp(boolean value) {
+ mInstantApp = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setVirtualPreload(boolean value) {
+ mVirtualPreload = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setEnabledState(int value) {
+ mEnabledState = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) {
+ mInstallReason = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.InstallReason.class, null, mInstallReason);
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setUninstallReason(
+ @PackageManager.UninstallReason int value) {
+ mUninstallReason = value;
+ com.android.internal.util.AnnotationValidations.validate(
+ PackageManager.UninstallReason.class, null, mUninstallReason);
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) {
+ mHarmfulAppWarning = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) {
+ mLastDisableAppCaller = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths(
+ @NonNull ArrayMap<String, OverlayPaths> value) {
+ mSharedLibraryOverlayPaths = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) {
+ mSplashScreenTheme = value;
+ onChanged();
+ return this;
+ }
+
+ /**
+ * Suspending package to suspend params
+ */
+ public @NonNull PackageUserStateImpl setSuspendParams(
+ @NonNull ArrayMap<String, SuspendParams> value) {
+ mSuspendParams = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setComponentLabelIconOverrideMap(
+ @NonNull ArrayMap<ComponentName, Pair<String, Integer>> value) {
+ mComponentLabelIconOverrideMap = value;
+ onChanged();
+ return this;
+ }
+
+ public @NonNull PackageUserStateImpl setFirstInstallTime(long value) {
+ mFirstInstallTime = value;
+ onChanged();
+ return this;
+ }
@@ -393,11 +545,6 @@
}
@DataClass.Generated.Member
- public @Nullable OverlayPaths getCachedOverlayPaths() {
- return mCachedOverlayPaths;
- }
-
- @DataClass.Generated.Member
public @Nullable ArrayMap<ComponentName,Pair<String,Integer>> getComponentLabelIconOverrideMap() {
return mComponentLabelIconOverrideMap;
}
@@ -408,130 +555,8 @@
}
@DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setDisabledComponents(@NonNull ArraySet<String> value) {
- mDisabledComponents = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setEnabledComponents(@NonNull ArraySet<String> value) {
- mEnabledComponents = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setCeDataInode( long value) {
- mCeDataInode = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstalled( boolean value) {
- mInstalled = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setStopped( boolean value) {
- mStopped = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setNotLaunched( boolean value) {
- mNotLaunched = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setHidden( boolean value) {
- mHidden = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setDistractionFlags( int value) {
- mDistractionFlags = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstantApp( boolean value) {
- mInstantApp = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setVirtualPreload( boolean value) {
- mVirtualPreload = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setEnabledState( int value) {
- mEnabledState = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setInstallReason(@PackageManager.InstallReason int value) {
- mInstallReason = value;
- com.android.internal.util.AnnotationValidations.validate(
- PackageManager.InstallReason.class, null, mInstallReason);
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setUninstallReason(@PackageManager.UninstallReason int value) {
- mUninstallReason = value;
- com.android.internal.util.AnnotationValidations.validate(
- PackageManager.UninstallReason.class, null, mUninstallReason);
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setHarmfulAppWarning(@NonNull String value) {
- mHarmfulAppWarning = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setLastDisableAppCaller(@NonNull String value) {
- mLastDisableAppCaller = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSharedLibraryOverlayPaths(@NonNull ArrayMap<String,OverlayPaths> value) {
- mSharedLibraryOverlayPaths = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSplashScreenTheme(@NonNull String value) {
- mSplashScreenTheme = value;
- return this;
- }
-
- /**
- * Suspending package to suspend params
- */
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setSuspendParams(@NonNull ArrayMap<String,SuspendParams> value) {
- mSuspendParams = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setComponentLabelIconOverrideMap(@NonNull ArrayMap<ComponentName,Pair<String,Integer>> value) {
- mComponentLabelIconOverrideMap = value;
- return this;
- }
-
- @DataClass.Generated.Member
- public @NonNull PackageUserStateImpl setFirstInstallTime( long value) {
- mFirstInstallTime = value;
- return this;
+ public @Nullable Watchable getWatchable() {
+ return mWatchable;
}
@Override
@@ -566,9 +591,9 @@
&& Objects.equals(mSharedLibraryOverlayPaths, that.mSharedLibraryOverlayPaths)
&& Objects.equals(mSplashScreenTheme, that.mSplashScreenTheme)
&& Objects.equals(mSuspendParams, that.mSuspendParams)
- && Objects.equals(mCachedOverlayPaths, that.mCachedOverlayPaths)
&& Objects.equals(mComponentLabelIconOverrideMap, that.mComponentLabelIconOverrideMap)
- && mFirstInstallTime == that.mFirstInstallTime;
+ && mFirstInstallTime == that.mFirstInstallTime
+ && Objects.equals(mWatchable, that.mWatchable);
}
@Override
@@ -597,17 +622,17 @@
_hash = 31 * _hash + Objects.hashCode(mSharedLibraryOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mSplashScreenTheme);
_hash = 31 * _hash + Objects.hashCode(mSuspendParams);
- _hash = 31 * _hash + Objects.hashCode(mCachedOverlayPaths);
_hash = 31 * _hash + Objects.hashCode(mComponentLabelIconOverrideMap);
_hash = 31 * _hash + Long.hashCode(mFirstInstallTime);
+ _hash = 31 * _hash + Objects.hashCode(mWatchable);
return _hash;
}
@DataClass.Generated(
- time = 1640923839971L,
+ time = 1643854846064L,
codegenVersion = "1.0.23",
sourceFile = "frameworks/base/services/core/java/com/android/server/pm/pkg/PackageUserStateImpl.java",
- inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mCachedOverlayPaths\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
+ inputSignatures = "protected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mDisabledComponents\nprotected @android.annotation.Nullable android.util.ArraySet<java.lang.String> mEnabledComponents\nprivate long mCeDataInode\nprivate boolean mInstalled\nprivate boolean mStopped\nprivate boolean mNotLaunched\nprivate boolean mHidden\nprivate int mDistractionFlags\nprivate boolean mInstantApp\nprivate boolean mVirtualPreload\nprivate int mEnabledState\nprivate @android.content.pm.PackageManager.InstallReason int mInstallReason\nprivate @android.content.pm.PackageManager.UninstallReason int mUninstallReason\nprivate @android.annotation.Nullable java.lang.String mHarmfulAppWarning\nprivate @android.annotation.Nullable java.lang.String mLastDisableAppCaller\nprotected @android.annotation.Nullable android.content.pm.overlay.OverlayPaths mOverlayPaths\nprotected @android.annotation.Nullable android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths> mSharedLibraryOverlayPaths\nprivate @android.annotation.Nullable java.lang.String mSplashScreenTheme\nprivate @android.annotation.Nullable android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams> mSuspendParams\nprivate @android.annotation.Nullable android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>> mComponentLabelIconOverrideMap\nprivate long mFirstInstallTime\nprivate final @android.annotation.Nullable com.android.server.utils.Watchable mWatchable\nprivate void onChanged()\npublic @android.annotation.Nullable boolean setOverlayPaths(android.content.pm.overlay.OverlayPaths)\npublic boolean setSharedLibraryOverlayPaths(java.lang.String,android.content.pm.overlay.OverlayPaths)\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getDisabledComponentsNoCopy()\npublic @android.annotation.Nullable @java.lang.Override android.util.ArraySet<java.lang.String> getEnabledComponentsNoCopy()\npublic @java.lang.Override boolean isComponentEnabled(java.lang.String)\npublic @java.lang.Override boolean isComponentDisabled(java.lang.String)\npublic @java.lang.Override android.content.pm.overlay.OverlayPaths getAllOverlayPaths()\npublic @com.android.internal.annotations.VisibleForTesting boolean overrideLabelAndIcon(android.content.ComponentName,java.lang.String,java.lang.Integer)\npublic void resetOverrideComponentLabelIcon()\npublic @android.annotation.Nullable android.util.Pair<java.lang.String,java.lang.Integer> getOverrideLabelIconForComponent(android.content.ComponentName)\npublic @java.lang.Override boolean isSuspended()\npublic com.android.server.pm.pkg.PackageUserStateImpl putSuspendParams(java.lang.String,com.android.server.pm.pkg.SuspendParams)\npublic com.android.server.pm.pkg.PackageUserStateImpl removeSuspension(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDisabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledComponents(android.util.ArraySet<java.lang.String>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setCeDataInode(long)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstalled(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setStopped(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setNotLaunched(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHidden(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setDistractionFlags(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstantApp(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setVirtualPreload(boolean)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setEnabledState(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setInstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setUninstallReason(int)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setHarmfulAppWarning(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setLastDisableAppCaller(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSharedLibraryOverlayPaths(android.util.ArrayMap<java.lang.String,android.content.pm.overlay.OverlayPaths>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSplashScreenTheme(java.lang.String)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setSuspendParams(android.util.ArrayMap<java.lang.String,com.android.server.pm.pkg.SuspendParams>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setComponentLabelIconOverrideMap(android.util.ArrayMap<android.content.ComponentName,android.util.Pair<java.lang.String,java.lang.Integer>>)\npublic @android.annotation.NonNull com.android.server.pm.pkg.PackageUserStateImpl setFirstInstallTime(long)\nclass PackageUserStateImpl extends java.lang.Object implements [com.android.server.pm.pkg.PackageUserStateInternal]\n@com.android.internal.util.DataClass(genConstructor=false, genBuilder=false, genEqualsHashCode=true)")
@Deprecated
private void __metadata() {}
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/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 7dd9425..7e36290 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -27,6 +27,7 @@
import static android.content.pm.PackageManager.FEATURE_PICTURE_IN_PICTURE;
import static android.content.pm.PackageManager.FEATURE_WATCH;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+import static android.os.BatteryManager.BATTERY_PLUGGED_WIRELESS;
import static android.os.Build.VERSION_CODES.M;
import static android.os.Build.VERSION_CODES.O;
import static android.provider.Settings.Secure.VOLUME_HUSH_OFF;
@@ -129,6 +130,7 @@
import android.media.AudioSystem;
import android.media.IAudioService;
import android.media.session.MediaSessionLegacyHelper;
+import android.os.BatteryManagerInternal;
import android.os.Binder;
import android.os.Bundle;
import android.os.DeviceIdleManager;
@@ -392,11 +394,12 @@
PowerManagerInternal mPowerManagerInternal;
IStatusBarService mStatusBarService;
StatusBarManagerInternal mStatusBarManagerInternal;
+ BatteryManagerInternal mBatteryManagerInternal;
AudioManagerInternal mAudioManagerInternal;
DisplayManager mDisplayManager;
DisplayManagerInternal mDisplayManagerInternal;
boolean mPreloadedRecentApps;
- final Object mServiceAquireLock = new Object();
+ final Object mServiceAcquireLock = new Object();
Vibrator mVibrator; // Vibrator for giving feedback of orientation changes
SearchManager mSearchManager;
AccessibilityManager mAccessibilityManager;
@@ -782,7 +785,8 @@
@Override
public void onWakeUp() {
synchronized (mLock) {
- if (shouldEnableWakeGestureLp()) {
+ if (shouldEnableWakeGestureLp()
+ && mBatteryManagerInternal.getPlugType() != BATTERY_PLUGGED_WIRELESS) {
performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY, false,
"Wake Up");
wakeUp(SystemClock.uptimeMillis(), mAllowTheaterModeWakeFromWakeGesture,
@@ -811,7 +815,7 @@
}
IStatusBarService getStatusBarService() {
- synchronized (mServiceAquireLock) {
+ synchronized (mServiceAcquireLock) {
if (mStatusBarService == null) {
mStatusBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService("statusbar"));
@@ -821,7 +825,7 @@
}
StatusBarManagerInternal getStatusBarManagerInternal() {
- synchronized (mServiceAquireLock) {
+ synchronized (mServiceAcquireLock) {
if (mStatusBarManagerInternal == null) {
mStatusBarManagerInternal =
LocalServices.getService(StatusBarManagerInternal.class);
@@ -831,7 +835,7 @@
}
AudioManagerInternal getAudioManagerInternal() {
- synchronized (mServiceAquireLock) {
+ synchronized (mServiceAcquireLock) {
if (mAudioManagerInternal == null) {
mAudioManagerInternal = LocalServices.getService(AudioManagerInternal.class);
}
@@ -839,6 +843,15 @@
}
}
+ BatteryManagerInternal getBatteryManagerInternal() {
+ synchronized (mServiceAcquireLock) {
+ if (mBatteryManagerInternal == null) {
+ mBatteryManagerInternal =
+ LocalServices.getService(BatteryManagerInternal.class);
+ }
+ return mBatteryManagerInternal;
+ }
+ }
// returns true if the key was handled and should not be passed to the user
private boolean backKeyPress() {
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/sensorprivacy/CameraPrivacyLightController.java b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
new file mode 100644
index 0000000..750d400
--- /dev/null
+++ b/services/core/java/com/android/server/sensorprivacy/CameraPrivacyLightController.java
@@ -0,0 +1,125 @@
+/*
+ * 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.sensorprivacy;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightState;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.internal.R;
+import com.android.server.FgThread;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+class CameraPrivacyLightController implements AppOpsManager.OnOpActiveChangedListener {
+
+ private final Context mContext;
+ private final LightsManager mLightsManager;
+
+ private final Set<String> mActivePackages = new ArraySet<>();
+ private final Set<String> mActivePhonePackages = new ArraySet<>();
+
+ private final int mCameraPrivacyLightColor;
+
+ private final List<Light> mCameraLights = new ArrayList<>();
+ private final AppOpsManager mAppOpsManager;
+
+ private LightsManager.LightsSession mLightsSession = null;
+
+ CameraPrivacyLightController(Context context) {
+ mContext = context;
+
+ mAppOpsManager = mContext.getSystemService(AppOpsManager.class);
+ mLightsManager = mContext.getSystemService(LightsManager.class);
+
+ mCameraPrivacyLightColor = mContext.getColor(R.color.camera_privacy_light);
+
+ List<Light> lights = mLightsManager.getLights();
+ for (int i = 0; i < lights.size(); i++) {
+ Light light = lights.get(i);
+ if (light.getType() == Light.LIGHT_TYPE_CAMERA) {
+ mCameraLights.add(light);
+ }
+ }
+
+ if (mCameraLights.isEmpty()) {
+ return;
+ }
+
+ mAppOpsManager.startWatchingActive(
+ new String[] {AppOpsManager.OPSTR_CAMERA, AppOpsManager.OPSTR_PHONE_CALL_CAMERA},
+ FgThread.getExecutor(), this);
+ }
+
+ @Override
+ public void onOpActiveChanged(String op, int uid, String packageName, boolean active) {
+ final Set<String> activePackages;
+ if (AppOpsManager.OPSTR_CAMERA.equals(op)) {
+ activePackages = mActivePackages;
+ } else if (AppOpsManager.OPSTR_PHONE_CALL_CAMERA.equals(op)) {
+ activePackages = mActivePhonePackages;
+ } else {
+ return;
+ }
+
+ if (active) {
+ activePackages.add(packageName);
+ } else {
+ activePackages.remove(packageName);
+ }
+
+ updateLightSession();
+ }
+
+ private void updateLightSession() {
+ Set<String> exemptedPackages = PermissionManager.getIndicatorExemptedPackages(mContext);
+
+ boolean shouldSessionEnd = exemptedPackages.containsAll(mActivePackages)
+ && exemptedPackages.containsAll(mActivePhonePackages);
+
+ if (shouldSessionEnd) {
+ if (mLightsSession == null) {
+ return;
+ }
+
+ mLightsSession.close();
+ mLightsSession = null;
+ } else {
+ if (mLightsSession != null) {
+ return;
+ }
+
+ LightsRequest.Builder requestBuilder = new LightsRequest.Builder();
+ for (int i = 0; i < mCameraLights.size(); i++) {
+ requestBuilder.addLight(mCameraLights.get(i),
+ new LightState.Builder()
+ .setColor(mCameraPrivacyLightColor)
+ .build());
+ }
+
+ mLightsSession = mLightsManager.openSession(Integer.MAX_VALUE);
+ mLightsSession.requestLights(requestBuilder.build());
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
index e9b5f11..040fffa8 100644
--- a/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
+++ b/services/core/java/com/android/server/sensorprivacy/SensorPrivacyService.java
@@ -154,6 +154,8 @@
private final AppOpsManagerInternal mAppOpsManagerInternal;
private final TelephonyManager mTelephonyManager;
+ private CameraPrivacyLightController mCameraPrivacyLightController;
+
private final IBinder mAppOpsRestrictionToken = new Binder();
private SensorPrivacyManagerInternalImpl mSensorPrivacyManagerInternal;
@@ -190,6 +192,8 @@
if (phase == PHASE_SYSTEM_SERVICES_READY) {
mKeyguardManager = mContext.getSystemService(KeyguardManager.class);
mEmergencyCallHelper = new EmergencyCallHelper();
+ } else if (phase == PHASE_ACTIVITY_MANAGER_READY) {
+ mCameraPrivacyLightController = new CameraPrivacyLightController(mContext);
}
}
diff --git a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
index 21d4cbb..f4b335e 100644
--- a/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
+++ b/services/core/java/com/android/server/speech/RemoteSpeechRecognitionService.java
@@ -365,6 +365,20 @@
}
@Override
+ public void onSegmentResults(Bundle results) throws RemoteException {
+ mRemoteListener.onSegmentResults(results);
+ }
+
+ @Override
+ public void onEndOfSegmentedSession() throws RemoteException {
+ if (DEBUG) {
+ Slog.i(TAG, "#onEndOfSegmentedSession invoked for a recognition session");
+ }
+ mOnSessionComplete.run();
+ mRemoteListener.onEndOfSegmentedSession();
+ }
+
+ @Override
public void onEvent(int eventType, Bundle params) throws RemoteException {
mRemoteListener.onEvent(eventType, params);
}
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/trust/TrustAgentWrapper.java b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
index 06ce4a4..1dea3d7 100644
--- a/services/core/java/com/android/server/trust/TrustAgentWrapper.java
+++ b/services/core/java/com/android/server/trust/TrustAgentWrapper.java
@@ -71,6 +71,7 @@
private static final int MSG_ESCROW_TOKEN_STATE = 9;
private static final int MSG_UNLOCK_USER = 10;
private static final int MSG_SHOW_KEYGUARD_ERROR_MESSAGE = 11;
+ private static final int MSG_LOCK_USER = 12;
/**
* Time in uptime millis that we wait for the service connection, both when starting
@@ -100,6 +101,8 @@
// Trust state
private boolean mTrusted;
+ private boolean mWaitingForTrustableDowngrade = false;
+ private boolean mTrustable;
private CharSequence mMessage;
private boolean mDisplayTrustGrantedMessage;
private boolean mTrustDisabledByDpm;
@@ -108,6 +111,25 @@
private AlarmManager mAlarmManager;
private final Intent mAlarmIntent;
private PendingIntent mAlarmPendingIntent;
+ private final BroadcastReceiver mTrustableDowngradeReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!TrustManagerService.ENABLE_ACTIVE_UNLOCK_FLAG) {
+ return;
+ }
+ if (!mWaitingForTrustableDowngrade) {
+ return;
+ }
+ // are these the broadcasts we want to listen to
+ if (Intent.ACTION_SCREEN_OFF.equals(intent.getAction())
+ || Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {
+ mTrusted = false;
+ mTrustable = true;
+ mWaitingForTrustableDowngrade = false;
+ mTrustManagerService.updateTrust(mUserId, 0);
+ }
+ }
+ };
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
@@ -126,16 +148,21 @@
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_GRANT_TRUST:
- // TODO(b/213631675): Respect FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
if (!isConnected()) {
Log.w(TAG, "Agent is not connected, cannot grant trust: "
+ mName.flattenToShortString());
return;
}
mTrusted = true;
+ mTrustable = false;
mMessage = (CharSequence) msg.obj;
int flags = msg.arg1;
mDisplayTrustGrantedMessage = (flags & FLAG_GRANT_TRUST_DISPLAY_MESSAGE) != 0;
+ if ((flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0) {
+ mWaitingForTrustableDowngrade = true;
+ } else {
+ mWaitingForTrustableDowngrade = false;
+ }
long durationMs = msg.getData().getLong(DATA_DURATION);
if (durationMs > 0) {
final long duration;
@@ -270,6 +297,13 @@
mTrustManagerService.showKeyguardErrorMessage(message);
break;
}
+ case MSG_LOCK_USER: {
+ mTrusted = false;
+ mTrustable = false;
+ mTrustManagerService.updateTrust(mUserId, 0 /* flags */);
+ mTrustManagerService.lockUser(mUserId);
+ break;
+ }
}
}
};
@@ -295,6 +329,11 @@
}
@Override
+ public void lockUser() {
+ mHandler.sendEmptyMessage(MSG_LOCK_USER);
+ }
+
+ @Override
public void setManagingTrust(boolean managingTrust) {
if (DEBUG) Slog.d(TAG, "managingTrust()");
mHandler.obtainMessage(MSG_MANAGING_TRUST, managingTrust ? 1 : 0, 0).sendToTarget();
@@ -427,6 +466,9 @@
final String pathUri = mAlarmIntent.toUri(Intent.URI_INTENT_SCHEME);
alarmFilter.addDataPath(pathUri, PatternMatcher.PATTERN_LITERAL);
+ IntentFilter trustableFilter = new IntentFilter(Intent.ACTION_USER_PRESENT);
+ trustableFilter.addAction(Intent.ACTION_SCREEN_OFF);
+
// Schedules a restart for when connecting times out. If the connection succeeds,
// the restart is canceled in mCallback's onConnected.
scheduleRestart();
@@ -435,6 +477,7 @@
if (mBound) {
mContext.registerReceiver(mBroadcastReceiver, alarmFilter, PERMISSION, null,
Context.RECEIVER_EXPORTED);
+ mContext.registerReceiver(mTrustableDowngradeReceiver, trustableFilter);
} else {
Log.e(TAG, "Can't bind to TrustAgent " + mName.flattenToShortString());
}
@@ -591,6 +634,10 @@
return mTrusted && mManagingTrust && !mTrustDisabledByDpm;
}
+ public boolean isTrustable() {
+ return mTrustable && mManagingTrust && !mTrustDisabledByDpm;
+ }
+
public boolean isManagingTrust() {
return mManagingTrust && !mTrustDisabledByDpm;
}
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index 9bed24d..6aafd4a 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -54,6 +54,7 @@
import android.os.PersistableBundle;
import android.os.RemoteException;
import android.os.SystemClock;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -65,6 +66,7 @@
import android.util.AttributeSet;
import android.util.Log;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.Xml;
import android.view.IWindowManager;
@@ -145,6 +147,21 @@
@GuardedBy("mUserIsTrusted")
private final SparseBooleanArray mUserIsTrusted = new SparseBooleanArray();
+ //TODO(b/215724686): remove flag
+ public static final boolean ENABLE_ACTIVE_UNLOCK_FLAG = SystemProperties.getBoolean(
+ "fw.enable_active_unlock_flag", true);
+
+ private enum TrustState {
+ UNTRUSTED, // the phone is not unlocked by any trustagents
+ TRUSTABLE, // the phone is in a semi-locked state that can be unlocked if
+ // FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE is passed and a trustagent is trusted
+ TRUSTED // the phone is unlocked
+ };
+
+ @GuardedBy("mUserTrustState")
+ private final SparseArray<TrustManagerService.TrustState> mUserTrustState =
+ new SparseArray<>();
+
/**
* Stores the locked state for users on the device. There are three different type of users
* which are handled slightly differently:
@@ -228,7 +245,6 @@
}
// Extend unlock config and logic
-
private final class SettingsObserver extends ContentObserver {
private final Uri TRUST_AGENTS_EXTEND_UNLOCK =
Settings.Secure.getUriFor(Settings.Secure.TRUST_AGENTS_EXTEND_UNLOCK);
@@ -396,6 +412,14 @@
}
private void updateTrust(int userId, int flags, boolean isFromUnlock) {
+ if (ENABLE_ACTIVE_UNLOCK_FLAG) {
+ updateTrustWithRenewableUnlock(userId, flags, isFromUnlock);
+ } else {
+ updateTrustWithExtendUnlock(userId, flags, isFromUnlock);
+ }
+ }
+
+ private void updateTrustWithExtendUnlock(int userId, int flags, boolean isFromUnlock) {
boolean managed = aggregateIsTrustManaged(userId);
dispatchOnTrustManagedChanged(managed, userId);
if (mStrongAuthTracker.isTrustAllowedForUser(userId)
@@ -441,6 +465,65 @@
}
}
+ private void updateTrustWithRenewableUnlock(int userId, int flags, boolean isFromUnlock) {
+ boolean managed = aggregateIsTrustManaged(userId);
+ dispatchOnTrustManagedChanged(managed, userId);
+ if (mStrongAuthTracker.isTrustAllowedForUser(userId)
+ && isTrustUsuallyManagedInternal(userId) != managed) {
+ updateTrustUsuallyManaged(userId, managed);
+ }
+
+ boolean trustedByAtLeastOneAgent = aggregateIsTrusted(userId);
+ boolean trustableByAtLeastOneAgent = aggregateIsTrustable(userId);
+ boolean wasTrusted;
+ boolean wasTrustable;
+ TrustState pendingTrustState;
+
+ IWindowManager wm = WindowManagerGlobal.getWindowManagerService();
+ boolean alreadyUnlocked = false;
+ try {
+ alreadyUnlocked = !wm.isKeyguardLocked();
+ } catch (RemoteException e) {
+ }
+
+ synchronized (mUserTrustState) {
+ wasTrusted = (mUserTrustState.get(userId) == TrustState.TRUSTED);
+ wasTrustable = (mUserTrustState.get(userId) == TrustState.TRUSTABLE);
+ boolean renewingTrust = wasTrustable && (
+ (flags & TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE) != 0);
+ boolean canMoveToTrusted = alreadyUnlocked || isFromUnlock || renewingTrust;
+ boolean upgradingTrustForCurrentUser = (userId == mCurrentUser);
+
+ if (trustedByAtLeastOneAgent && wasTrusted) {
+ // no change
+ return;
+ } else if (trustedByAtLeastOneAgent && canMoveToTrusted
+ && upgradingTrustForCurrentUser) {
+ pendingTrustState = TrustState.TRUSTED;
+ } else if (trustableByAtLeastOneAgent && (wasTrusted || wasTrustable)
+ && upgradingTrustForCurrentUser) {
+ pendingTrustState = TrustState.TRUSTABLE;
+ } else {
+ pendingTrustState = TrustState.UNTRUSTED;
+ }
+
+ mUserTrustState.put(userId, pendingTrustState);
+ }
+ if (DEBUG) Slog.d(TAG, "pendingTrustState: " + pendingTrustState);
+
+ boolean isNowTrusted = pendingTrustState == TrustState.TRUSTED;
+ dispatchOnTrustChanged(isNowTrusted, userId, flags, getTrustGrantedMessages(userId));
+ if (isNowTrusted != wasTrusted) {
+ refreshDeviceLockedForUser(userId);
+ if (!isNowTrusted) {
+ maybeLockScreen(userId);
+ } else {
+ scheduleTrustTimeout(userId, false /* override */);
+ }
+ }
+ }
+
+
private void updateTrustUsuallyManaged(int userId, boolean managed) {
synchronized (mTrustUsuallyManagedForUser) {
mTrustUsuallyManagedForUser.put(userId, managed);
@@ -472,6 +555,20 @@
mLockPatternUtils.unlockUserWithToken(handle, token, userId);
}
+ /**
+ * Locks the phone and requires some auth (not trust) like a biometric or passcode before
+ * unlocking.
+ */
+ public void lockUser(int userId) {
+ mLockPatternUtils.requireStrongAuth(
+ StrongAuthTracker.SOME_AUTH_REQUIRED_AFTER_USER_REQUEST, userId);
+ try {
+ WindowManagerGlobal.getWindowManagerService().lockNow(null);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Error locking screen when called from trust agent");
+ }
+ }
+
void showKeyguardErrorMessage(CharSequence message) {
dispatchOnTrustError(message);
}
@@ -950,6 +1047,21 @@
return false;
}
+ private boolean aggregateIsTrustable(int userId) {
+ if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
+ return false;
+ }
+ for (int i = 0; i < mActiveAgents.size(); i++) {
+ AgentInfo info = mActiveAgents.valueAt(i);
+ if (info.userId == userId) {
+ if (info.agent.isTrustable()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private List<String> getTrustGrantedMessages(int userId) {
if (!mStrongAuthTracker.isTrustAllowedForUser(userId)) {
return new ArrayList<>();
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
index ee2cc7b..56985af 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerService.java
@@ -2591,8 +2591,20 @@
*/
@Override
public void setWallpaperDimAmount(float dimAmount) throws RemoteException {
+ setWallpaperDimAmountForUid(Binder.getCallingUid(), dimAmount);
+ }
+
+ /**
+ * Sets wallpaper dim amount for a given UID. This only applies to FLAG_SYSTEM wallpaper as the
+ * lock screen does not have a wallpaper component, so we use mWallpaperMap.
+ *
+ * @param uid Caller UID that wants to set the wallpaper dim amount
+ * @param dimAmount Dim amount where 0f reverts any dimming applied by the caller (fully bright)
+ * and 1f is fully black
+ * @throws RemoteException
+ */
+ public void setWallpaperDimAmountForUid(int uid, float dimAmount) {
checkPermission(android.Manifest.permission.SET_WALLPAPER_DIM_AMOUNT);
- int uid = Binder.getCallingUid();
final long ident = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
diff --git a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
index fc827b4..458bf20 100644
--- a/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
+++ b/services/core/java/com/android/server/wallpaper/WallpaperManagerShellCommand.java
@@ -43,6 +43,8 @@
switch(cmd) {
case "set-dim-amount":
return setWallpaperDimAmount();
+ case "dim-with-uid":
+ return setDimmingWithUid();
case "get-dim-amount":
return getWallpaperDimAmount();
case "-h":
@@ -64,6 +66,10 @@
pw.println(" set-dim-amount DIMMING");
pw.println(" Sets the current dimming value to DIMMING (a number between 0 and 1).");
pw.println();
+ pw.println(" dim-with-uid UID DIMMING");
+ pw.println(" Sets the wallpaper dim amount to DIMMING as if an app with uid, UID, "
+ + "called it.");
+ pw.println();
pw.println(" get-dim-amount");
pw.println(" Get the current wallpaper dim amount.");
}
@@ -92,4 +98,17 @@
getOutPrintWriter().println("The current wallpaper dim amount is: " + dimAmount);
return 0;
}
+
+ /**
+ * Sets the wallpaper dim amount for an arbitrary UID to simulate multiple applications setting
+ * a dim amount on the wallpaper.
+ */
+ private int setDimmingWithUid() {
+ int mockUid = Integer.parseInt(getNextArgRequired());
+ float mockDimAmount = Float.parseFloat(getNextArgRequired());
+ mService.setWallpaperDimAmountForUid(mockUid, mockDimAmount);
+ getOutPrintWriter().println("Dimming the wallpaper for UID: " + mockUid + " to: "
+ + mockDimAmount);
+ return 0;
+ }
}
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/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index 8d4ae90..ca4d717 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -24,6 +24,7 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
+import static android.Manifest.permission.MANAGE_GAME_ACTIVITY;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REMOVE_TASKS;
import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
@@ -1771,6 +1772,43 @@
}
@Override
+ public int startActivityFromGameSession(IApplicationThread caller, String callingPackage,
+ String callingFeatureId, int callingPid, int callingUid, Intent intent, int taskId,
+ int userId) {
+ if (checkCallingPermission(MANAGE_GAME_ACTIVITY) != PERMISSION_GRANTED) {
+ final String msg = "Permission Denial: startActivityFromGameSession() from pid="
+ + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid()
+ + " requires " + MANAGE_GAME_ACTIVITY;
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ assertPackageMatchesCallingUid(callingPackage);
+
+ final ActivityOptions activityOptions = ActivityOptions.makeBasic();
+ activityOptions.setLaunchTaskId(taskId);
+
+ userId = handleIncomingUser(callingPid, callingUid, userId, "startActivityFromGameSession");
+
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ return getActivityStartController()
+ .obtainStarter(intent, "startActivityFromGameSession")
+ .setCaller(caller)
+ .setCallingUid(callingUid)
+ .setCallingPid(callingPid)
+ .setCallingPackage(intent.getPackage())
+ .setCallingFeatureId(callingFeatureId)
+ .setUserId(userId)
+ .setActivityOptions(activityOptions.toBundle())
+ .setRealCallingUid(Binder.getCallingUid())
+ .execute();
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ @Override
public BackNavigationInfo startBackNavigation() {
mAmInternal.enforceCallingPermission(START_TASKS_FROM_RECENTS,
"startBackNavigation()");
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/InputManagerCallback.java b/services/core/java/com/android/server/wm/InputManagerCallback.java
index f91969b..1f0fdcf 100644
--- a/services/core/java/com/android/server/wm/InputManagerCallback.java
+++ b/services/core/java/com/android/server/wm/InputManagerCallback.java
@@ -24,6 +24,7 @@
import static com.android.server.wm.WindowManagerService.H.ON_POINTER_DOWN_OUTSIDE_FOCUS;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.graphics.PointF;
import android.os.Debug;
import android.os.IBinder;
@@ -254,6 +255,25 @@
}
}
+ @Override
+ @Nullable
+ public SurfaceControl createSurfaceForGestureMonitor(String name, int displayId) {
+ synchronized (mService.mGlobalLock) {
+ final DisplayContent dc = mService.mRoot.getDisplayContent(displayId);
+ if (dc == null) {
+ Slog.e(TAG, "Failed to create a gesture monitor on display: " + displayId
+ + " - DisplayContent not found.");
+ return null;
+ }
+ return mService.makeSurfaceBuilder(dc.getSession())
+ .setContainerLayer()
+ .setName(name)
+ .setCallsite("createSurfaceForGestureMonitor")
+ .setParent(dc.getSurfaceControl())
+ .build();
+ }
+ }
+
/** Waits until the built-in input devices have been configured. */
public boolean waitForInputDevicesReady(long timeoutMillis) {
synchronized (mInputDevicesReadyMonitor) {
diff --git a/services/core/java/com/android/server/wm/InsetsPolicy.java b/services/core/java/com/android/server/wm/InsetsPolicy.java
index 10776ab..1e12173 100644
--- a/services/core/java/com/android/server/wm/InsetsPolicy.java
+++ b/services/core/java/com/android/server/wm/InsetsPolicy.java
@@ -32,6 +32,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.app.ActivityTaskManager;
import android.app.StatusBarManager;
import android.graphics.Insets;
import android.graphics.Rect;
@@ -134,7 +135,7 @@
/** Updates the target which can control system bars. */
void updateBarControlTarget(@Nullable WindowState focusedWin) {
- if (mFocusedWin != focusedWin){
+ if (mFocusedWin != focusedWin) {
abortTransient();
}
mFocusedWin = focusedWin;
@@ -156,7 +157,7 @@
}
boolean isHidden(@InternalInsetsType int type) {
- final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
+ final InsetsSourceProvider provider = mStateController.peekSourceProvider(type);
return provider != null && provider.hasWindow() && !provider.getSource().isVisible();
}
@@ -181,6 +182,10 @@
mShowingTransientTypes.toArray(), isGestureOnSystemBar);
}
updateBarControlTarget(mFocusedWin);
+ dispatchTransientSystemBarsVisibilityChanged(
+ mFocusedWin,
+ isTransient(ITYPE_STATUS_BAR) || isTransient(ITYPE_NAVIGATION_BAR),
+ isGestureOnSystemBar);
// The leashes can be created while updating bar control target. The surface transaction
// of the new leashes might not be applied yet. The callback posted here ensures we can
@@ -198,6 +203,12 @@
if (mShowingTransientTypes.size() == 0) {
return;
}
+
+ dispatchTransientSystemBarsVisibilityChanged(
+ mFocusedWin,
+ /* areVisible= */ false,
+ /* wereRevealedFromSwipeOnSystemBar= */ false);
+
startAnimation(false /* show */, () -> {
synchronized (mDisplayContent.mWmService.mGlobalLock) {
for (int i = mShowingTransientTypes.size() - 1; i >= 0; i--) {
@@ -373,6 +384,11 @@
mDisplayContent.getDisplayId(), mShowingTransientTypes.toArray());
}
mShowingTransientTypes.clear();
+
+ dispatchTransientSystemBarsVisibilityChanged(
+ mFocusedWin,
+ /* areVisible= */ false,
+ /* wereRevealedFromSwipeOnSystemBar= */ false);
}
private @Nullable InsetsControlTarget getStatusControlTarget(@Nullable WindowState focusedWin,
@@ -521,6 +537,32 @@
listener.mControlCallbacks.controlAnimationUnchecked(typesReady, controls, show);
}
+ private void dispatchTransientSystemBarsVisibilityChanged(
+ @Nullable WindowState focusedWindow,
+ boolean areVisible,
+ boolean wereRevealedFromSwipeOnSystemBar) {
+ if (focusedWindow == null) {
+ return;
+ }
+
+ Task task = focusedWindow.getTask();
+ if (task == null) {
+ return;
+ }
+
+ int taskId = task.mTaskId;
+ boolean isValidTaskId = taskId != ActivityTaskManager.INVALID_TASK_ID;
+ if (!isValidTaskId) {
+ return;
+ }
+
+ mDisplayContent.mWmService.mTaskSystemBarsListenerController
+ .dispatchTransientSystemBarVisibilityChanged(
+ taskId,
+ areVisible,
+ wereRevealedFromSwipeOnSystemBar);
+ }
+
private class BarWindow {
private final int mId;
diff --git a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
index 0cd3680..f3be66c 100644
--- a/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
+++ b/services/core/java/com/android/server/wm/PackageConfigurationUpdaterImpl.java
@@ -106,14 +106,18 @@
private void updateConfig(int uid, String packageName) {
final ArraySet<WindowProcessController> processes = mAtm.mProcessMap.getProcesses(uid);
- if (processes == null) return;
+ if (processes == null || processes.isEmpty()) return;
+ LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
+ mLocales, mAtm.getGlobalConfiguration().getLocales());
for (int i = processes.size() - 1; i >= 0; i--) {
final WindowProcessController wpc = processes.valueAt(i);
- if (!wpc.mInfo.packageName.equals(packageName)) continue;
- LocaleList localesOverride = LocaleOverlayHelper.combineLocalesIfOverlayExists(
- mLocales, mAtm.getGlobalConfiguration().getLocales());
- wpc.applyAppSpecificConfig(mNightMode, localesOverride);
- wpc.updateAppSpecificSettingsForAllActivities(mNightMode, localesOverride);
+ if (wpc.mInfo.packageName.equals(packageName)) {
+ wpc.applyAppSpecificConfig(mNightMode, localesOverride);
+ }
+ // Always inform individual activities about the update, since activities from other
+ // packages may be sharing this process
+ wpc.updateAppSpecificSettingsForAllActivitiesInPackage(packageName, mNightMode,
+ localesOverride);
}
}
diff --git a/services/core/java/com/android/server/wm/RootWindowContainer.java b/services/core/java/com/android/server/wm/RootWindowContainer.java
index 76a7981..ee03d02 100644
--- a/services/core/java/com/android/server/wm/RootWindowContainer.java
+++ b/services/core/java/com/android/server/wm/RootWindowContainer.java
@@ -981,6 +981,29 @@
mWmService.checkDrawnWindowsLocked();
}
+ final int N = mWmService.mPendingRemove.size();
+ if (N > 0) {
+ if (mWmService.mPendingRemoveTmp.length < N) {
+ mWmService.mPendingRemoveTmp = new WindowState[N + 10];
+ }
+ mWmService.mPendingRemove.toArray(mWmService.mPendingRemoveTmp);
+ mWmService.mPendingRemove.clear();
+ ArrayList<DisplayContent> displayList = new ArrayList();
+ for (i = 0; i < N; i++) {
+ final WindowState w = mWmService.mPendingRemoveTmp[i];
+ w.removeImmediately();
+ final DisplayContent displayContent = w.getDisplayContent();
+ if (displayContent != null && !displayList.contains(displayContent)) {
+ displayList.add(displayContent);
+ }
+ }
+
+ for (int j = displayList.size() - 1; j >= 0; --j) {
+ final DisplayContent dc = displayList.get(j);
+ dc.assignWindowLayers(true /*setLayoutNeeded*/);
+ }
+ }
+
forAllDisplays(dc -> {
dc.getInputMonitor().updateInputWindowsLw(true /*force*/);
dc.updateSystemGestureExclusion();
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/TaskSystemBarsListenerController.java b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java
new file mode 100644
index 0000000..acb6061
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSystemBarsListenerController.java
@@ -0,0 +1,63 @@
+/*
+ * 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.wm;
+
+import com.android.internal.os.BackgroundThread;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
+
+import java.util.HashSet;
+import java.util.concurrent.Executor;
+
+/**
+ * Manages dispatch of task system bar changes to interested listeners. All invocations must be
+ * performed while the {@link WindowManagerService#getWindowManagerLock() Window Manager Lock} is
+ * held.
+ */
+final class TaskSystemBarsListenerController {
+
+ private final HashSet<TaskSystemBarsListener> mListeners = new HashSet<>();
+ private final Executor mBackgroundExecutor;
+
+ TaskSystemBarsListenerController() {
+ this.mBackgroundExecutor = BackgroundThread.getExecutor();
+ }
+
+ void registerListener(TaskSystemBarsListener listener) {
+ mListeners.add(listener);
+ }
+
+ void unregisterListener(TaskSystemBarsListener listener) {
+ mListeners.remove(listener);
+ }
+
+ void dispatchTransientSystemBarVisibilityChanged(
+ int taskId,
+ boolean visible,
+ boolean wereRevealedFromSwipeOnSystemBar) {
+ HashSet<TaskSystemBarsListener> localListeners;
+ localListeners = new HashSet<>(mListeners);
+
+ mBackgroundExecutor.execute(() -> {
+ for (TaskSystemBarsListener listener : localListeners) {
+ listener.onTransientSystemBarsVisibilityChanged(
+ taskId,
+ visible,
+ wereRevealedFromSwipeOnSystemBar);
+ }
+ });
+ }
+}
diff --git a/services/core/java/com/android/server/wm/Transition.java b/services/core/java/com/android/server/wm/Transition.java
index ad45948..155db2c 100644
--- a/services/core/java/com/android/server/wm/Transition.java
+++ b/services/core/java/com/android/server/wm/Transition.java
@@ -1082,6 +1082,10 @@
for (WindowContainer<?> p = wc.getParent(); p != null; p = p.getParent()) {
final ChangeInfo parentChange = changes.get(p);
if (parentChange == null || !parentChange.hasChanged(p)) break;
+ if (p.mRemoteToken == null) {
+ // Intermediate parents must be those that has window to be managed by Shell.
+ continue;
+ }
if (parentChange.mParent != null && !skipIntermediateReports) {
changes.get(wc).mParent = p;
// The chain above the parent was processed.
diff --git a/services/core/java/com/android/server/wm/OverlayHost.java b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
similarity index 89%
rename from services/core/java/com/android/server/wm/OverlayHost.java
rename to services/core/java/com/android/server/wm/TrustedOverlayHost.java
index 90f5b09..975b21c 100644
--- a/services/core/java/com/android/server/wm/OverlayHost.java
+++ b/services/core/java/com/android/server/wm/TrustedOverlayHost.java
@@ -32,14 +32,20 @@
*
* Also handles multiplexing of event dispatch and tracking of overlays
* to make things easier for WindowContainer.
+ *
+ * These overlays are to be used for various types of System UI and UI
+ * under the systems control. Provided SurfacePackages will be able
+ * to overlay application content, without engaging the usual cross process
+ * obscured touch filtering mechanisms. It's imperative that all UI provided
+ * be under complete control of the system.
*/
-class OverlayHost {
+class TrustedOverlayHost {
// Lazily initialized when required
SurfaceControl mSurfaceControl;
final ArrayList<SurfaceControlViewHost.SurfacePackage> mOverlays = new ArrayList<>();
final WindowManagerService mWmService;
- OverlayHost(WindowManagerService wms) {
+ TrustedOverlayHost(WindowManagerService wms) {
mWmService = wms;
}
@@ -51,6 +57,8 @@
.setName("Overlay Host Leash");
mSurfaceControl = b.build();
+ SurfaceControl.Transaction t = mWmService.mTransactionFactory.get();
+ t.setTrustedOverlay(mSurfaceControl, true).apply();
}
}
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 1bd153b..8a373bf 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -314,7 +314,7 @@
private final List<WindowContainerListener> mListeners = new ArrayList<>();
- protected OverlayHost mOverlayHost;
+ protected TrustedOverlayHost mOverlayHost;
WindowContainer(WindowManagerService wms) {
mWmService = wms;
@@ -3600,9 +3600,9 @@
@AnimationType int type, @Nullable AnimationAdapter snapshotAnim);
}
- void addOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ void addTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
if (mOverlayHost == null) {
- mOverlayHost = new OverlayHost(mWmService);
+ mOverlayHost = new TrustedOverlayHost(mWmService);
}
mOverlayHost.addOverlay(overlay, mSurfaceControl);
@@ -3613,11 +3613,11 @@
overlay.getRemoteInterface().onConfigurationChanged(getConfiguration());
} catch (Exception e) {
Slog.e(TAG, "Error sending initial configuration change to WindowContainer overlay");
- removeOverlay(overlay);
+ removeTrustedOverlay(overlay);
}
}
- void removeOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
+ void removeTrustedOverlay(SurfaceControlViewHost.SurfacePackage overlay) {
if (mOverlayHost != null && !mOverlayHost.removeOverlay(overlay)) {
mOverlayHost.release();
mOverlayHost = null;
diff --git a/services/core/java/com/android/server/wm/WindowManagerInternal.java b/services/core/java/com/android/server/wm/WindowManagerInternal.java
index 20fa7a9..9585a4b 100644
--- a/services/core/java/com/android/server/wm/WindowManagerInternal.java
+++ b/services/core/java/com/android/server/wm/WindowManagerInternal.java
@@ -256,6 +256,25 @@
}
/**
+ * An interface to be notified when the system bars for a task change.
+ */
+ public interface TaskSystemBarsListener {
+
+ /**
+ * Called when the visibility of the system bars of a task change.
+ *
+ * @param taskId the identifier of the task.
+ * @param visible if the transient system bars are visible.
+ * @param wereRevealedFromSwipeOnSystemBar if the transient bars were revealed due to a
+ * swipe gesture on a system bar.
+ */
+ void onTransientSystemBarsVisibilityChanged(
+ int taskId,
+ boolean visible,
+ boolean wereRevealedFromSwipeOnSystemBar);
+ }
+
+ /**
* An interface to be notified when keyguard exit animation should start.
*/
public interface KeyguardExitAnimationStartListener {
@@ -519,6 +538,20 @@
public abstract void registerAppTransitionListener(AppTransitionListener listener);
/**
+ * Registers a listener to be notified to when the system bars of a task changes.
+ *
+ * @param listener The listener to register.
+ */
+ public abstract void registerTaskSystemBarsListener(TaskSystemBarsListener listener);
+
+ /**
+ * Registers a listener to be notified to when the system bars of a task changes.
+ *
+ * @param listener The listener to unregister.
+ */
+ public abstract void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener);
+
+ /**
* Registers a listener to be notified to start the keyguard exit animation.
*
* @param listener The listener to register.
@@ -794,6 +827,11 @@
* Internal methods for other parts of SystemServer to manage
* SurfacePackage based overlays on tasks.
*
+ * Since these overlays will overlay application content, they exist
+ * in a container with setTrustedOverlay(true). This means its imperative
+ * that this overlay feature only be used with UI completely under the control
+ * of the system, without 3rd party content.
+ *
* Callers prepare a view hierarchy with SurfaceControlViewHost
* and send the package to WM here. The remote view hierarchy will receive
* configuration change, lifecycle events, etc, forwarded over the
@@ -804,8 +842,10 @@
* The embedded hierarchy exists in a coordinate space relative to the task
* bounds.
*/
- public abstract void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
- public abstract void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay);
+ public abstract void addTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay);
+ public abstract void removeTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay);
/**
* Get a SurfaceControl that is the container layer that should be used to receive input to
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b37cb4f..c9c3f1d 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -21,6 +21,7 @@
import static android.Manifest.permission.INTERNAL_SYSTEM_WINDOW;
import static android.Manifest.permission.MANAGE_ACTIVITY_TASKS;
import static android.Manifest.permission.MANAGE_APP_TOKENS;
+import static android.Manifest.permission.MODIFY_TOUCH_MODE_STATE;
import static android.Manifest.permission.READ_FRAME_BUFFER;
import static android.Manifest.permission.REGISTER_WINDOW_MANAGER_LISTENERS;
import static android.Manifest.permission.RESTRICTED_VR_ACCESS;
@@ -38,6 +39,7 @@
import static android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE;
import static android.os.Process.SYSTEM_UID;
import static android.os.Process.myPid;
+import static android.os.Process.myUid;
import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_NON_RESIZABLE_MULTI_WINDOW;
@@ -222,6 +224,7 @@
import android.util.EventLog;
import android.util.MergedConfiguration;
import android.util.Slog;
+import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.util.TimeUtils;
import android.util.TypedValue;
@@ -461,11 +464,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.
@@ -586,6 +584,20 @@
final ArrayList<WindowState> mResizingWindows = new ArrayList<>();
/**
+ * Windows whose animations have ended and now must be removed.
+ */
+ final ArrayList<WindowState> mPendingRemove = new ArrayList<>();
+
+ /**
+ * Used when processing mPendingRemove to avoid working on the original array.
+ */
+ WindowState[] mPendingRemoveTmp = new WindowState[20];
+
+ // TODO: use WindowProcessController once go/wm-unified is done.
+ /** Mapping of process pids to configurations */
+ final SparseArray<Configuration> mProcessConfigurations = new SparseArray<>();
+
+ /**
* Mapping of displayId to {@link DisplayImePolicy}.
* Note that this can be accessed without holding the lock.
*/
@@ -683,6 +695,7 @@
() -> mDisplayRotationController = null;
final DisplayWindowListenerController mDisplayNotificationController;
+ final TaskSystemBarsListenerController mTaskSystemBarsListenerController;
boolean mDisplayFrozen = false;
long mDisplayFreezeTime = 0;
@@ -695,6 +708,7 @@
final static int WINDOWS_FREEZING_SCREENS_TIMEOUT = 2;
int mWindowsFreezingScreen = WINDOWS_FREEZING_SCREENS_NONE;
+ /** Indicates that the system server is actively demanding the screen be frozen. */
boolean mClientFreezingScreen = false;
int mAppsFreezingScreen = 0;
@@ -795,8 +809,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);
}
@@ -837,11 +849,6 @@
return;
}
- if (mRenderShadowsInCompositorUri.equals(uri)) {
- setShadowRenderer();
- return;
- }
-
if (mDisplaySettingsPathUri.equals(uri)) {
updateDisplaySettingsLocation();
return;
@@ -956,11 +963,6 @@
}
}
- private void setShadowRenderer() {
- mRenderShadowsInCompositor = Settings.Global.getInt(mContext.getContentResolver(),
- DEVELOPMENT_RENDER_SHADOWS_IN_COMPOSITOR, 1) != 0;
- }
-
PowerManager mPowerManager;
PowerManagerInternal mPowerManagerInternal;
@@ -1186,7 +1188,8 @@
com.android.internal.R.bool.config_hasPermanentDpad);
mInTouchMode = context.getResources().getBoolean(
com.android.internal.R.bool.config_defaultInTouchMode);
- inputManager.setInTouchMode(mInTouchMode);
+ inputManager.setInTouchMode(
+ mInTouchMode, myPid(), myUid(), /* hasPermission = */ true);
mDrawLockTimeoutMillis = context.getResources().getInteger(
com.android.internal.R.integer.config_drawLockTimeoutMillis);
mAllowAnimationsInLowPowerMode = context.getResources().getBoolean(
@@ -1265,6 +1268,7 @@
mScreenFrozenLock.setReferenceCounted(false);
mDisplayNotificationController = new DisplayWindowListenerController(this);
+ mTaskSystemBarsListenerController = new TaskSystemBarsListenerController();
mActivityManager = ActivityManager.getService();
mActivityTaskManager = ActivityTaskManager.getService();
@@ -1378,7 +1382,6 @@
float[] spotColor = {0.f, 0.f, 0.f, spotShadowAlpha};
SurfaceControl.setGlobalShadowSettings(ambientColor, spotColor, lightY, lightZ,
lightRadius);
- setShadowRenderer();
}
/**
@@ -2036,6 +2039,7 @@
dc.mWinRemovedSinceNullFocus.add(win);
}
mEmbeddedWindowController.onWindowRemoved(win);
+ mPendingRemove.remove(win);
mResizingWindows.remove(win);
updateNonSystemOverlayWindowsVisibilityIfNeeded(win, false /* surfaceShown */);
mWindowsChanged = true;
@@ -2651,7 +2655,6 @@
}
boolean checkCallingPermission(String permission, String func, boolean printLog) {
- // Quick check: if the calling permission is me, it's all okay.
if (Binder.getCallingPid() == myPid()) {
return true;
}
@@ -3091,6 +3094,7 @@
// Misc IWindowSession methods
// -------------------------------------------------------------
+ /** Freeze the screen during a user-switch event. Called by UserController. */
@Override
public void startFreezingScreen(int exitAnim, int enterAnim) {
if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
@@ -3113,6 +3117,11 @@
}
}
+ /**
+ * No longer actively demand that the screen remain frozen.
+ * Called by UserController after a user-switch.
+ * This doesn't necessarily immediately unlock the screen; it just allows it if we're ready.
+ */
@Override
public void stopFreezingScreen() {
if (!checkCallingPermission(android.Manifest.permission.FREEZE_SCREEN,
@@ -3685,12 +3694,33 @@
}
}
- @Override
+ /**
+ * Sets the touch mode state.
+ *
+ * To be able to change touch mode state, the caller must either own the focused window, or must
+ * have the MODIFY_TOUCH_MODE_STATE permission.
+ *
+ * @param mode the touch mode to set
+ */
+ @Override // Binder call
public void setInTouchMode(boolean mode) {
synchronized (mGlobalLock) {
- mInTouchMode = mode;
+ if (mInTouchMode == mode) {
+ return;
+ }
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final boolean hasPermission = checkCallingPermission(MODIFY_TOUCH_MODE_STATE,
+ "setInTouchMode()");
+ final long token = Binder.clearCallingIdentity();
+ try {
+ if (mInputManager.setInTouchMode(mode, pid, uid, hasPermission)) {
+ mInTouchMode = mode;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(token);
+ }
}
- mInputManager.setInTouchMode(mode);
}
boolean getInTouchMode() {
@@ -3837,14 +3867,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;
}
@@ -3858,9 +3894,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;
@@ -5859,6 +5893,13 @@
return;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStartFreezingDisplay");
+ doStartFreezingDisplay(exitAnim, enterAnim, displayContent, overrideOriginalRotation);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ private void doStartFreezingDisplay(int exitAnim, int enterAnim, DisplayContent displayContent,
+ int overrideOriginalRotation) {
ProtoLog.d(WM_DEBUG_ORIENTATION,
"startFreezingDisplayLocked: exitAnim=%d enterAnim=%d called by %s",
exitAnim, enterAnim, Debug.getCallers(8));
@@ -5929,10 +5970,16 @@
return;
}
+ Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "WMS.doStopFreezingDisplayLocked-"
+ + mLastFinishedFreezeSource);
+ doStopFreezingDisplayLocked(displayContent);
+ Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+ }
+
+ private void doStopFreezingDisplayLocked(DisplayContent displayContent) {
ProtoLog.d(WM_DEBUG_ORIENTATION,
"stopFreezingDisplayLocked: Unfreezing now");
-
// We must make a local copy of the displayId as it can be potentially overwritten later on
// in this method. For example, {@link startFreezingDisplayLocked} may be called as a result
// of update rotation, but we reference the frozen display after that call in this method.
@@ -6364,6 +6411,23 @@
}
}
}
+ if (mPendingRemove.size() > 0) {
+ pw.println();
+ pw.println(" Remove pending for:");
+ for (int i=mPendingRemove.size()-1; i>=0; i--) {
+ WindowState w = mPendingRemove.get(i);
+ if (windows == null || windows.contains(w)) {
+ pw.print(" Remove #"); pw.print(i); pw.print(' ');
+ pw.print(w);
+ if (dumpAll) {
+ pw.println(":");
+ w.dump(pw, " ", true);
+ } else {
+ pw.println();
+ }
+ }
+ }
+ }
if (mForceRemoves != null && mForceRemoves.size() > 0) {
pw.println();
pw.println(" Windows force removing:");
@@ -7591,6 +7655,20 @@
}
@Override
+ public void registerTaskSystemBarsListener(TaskSystemBarsListener listener) {
+ synchronized (mGlobalLock) {
+ mTaskSystemBarsListenerController.registerListener(listener);
+ }
+ }
+
+ @Override
+ public void unregisterTaskSystemBarsListener(TaskSystemBarsListener listener) {
+ synchronized (mGlobalLock) {
+ mTaskSystemBarsListenerController.unregisterListener(listener);
+ }
+ }
+
+ @Override
public void registerKeyguardExitAnimationStartListener(
KeyguardExitAnimationStartListener listener) {
synchronized (mGlobalLock) {
@@ -7934,27 +8012,29 @@
@Override
public boolean shouldRestoreImeVisibility(IBinder imeTargetWindowToken) {
return WindowManagerService.this.shouldRestoreImeVisibility(imeTargetWindowToken);
- }
+ }
@Override
- public void addTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ public void addTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay) {
synchronized (mGlobalLock) {
final Task task = mRoot.getRootTask(taskId);
if (task == null) {
throw new IllegalArgumentException("no task with taskId" + taskId);
}
- task.addOverlay(overlay);
+ task.addTrustedOverlay(overlay);
}
}
@Override
- public void removeTaskOverlay(int taskId, SurfaceControlViewHost.SurfacePackage overlay) {
+ public void removeTrustedTaskOverlay(int taskId,
+ SurfaceControlViewHost.SurfacePackage overlay) {
synchronized (mGlobalLock) {
final Task task = mRoot.getRootTask(taskId);
if (task == null) {
throw new IllegalArgumentException("no task with taskId" + taskId);
}
- task.removeOverlay(overlay);
+ task.removeTrustedOverlay(overlay);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowProcessController.java b/services/core/java/com/android/server/wm/WindowProcessController.java
index 3ccb06c..ac9fbde 100644
--- a/services/core/java/com/android/server/wm/WindowProcessController.java
+++ b/services/core/java/com/android/server/wm/WindowProcessController.java
@@ -821,10 +821,15 @@
// TODO(b/199277065): Re-assess how app-specific locales are applied based on UXR
// TODO(b/199277729): Consider whether we need to add special casing for edge cases like
// activity-embeddings etc.
- void updateAppSpecificSettingsForAllActivities(Integer nightMode, LocaleList localesOverride) {
+ void updateAppSpecificSettingsForAllActivitiesInPackage(String packageName, Integer nightMode,
+ LocaleList localesOverride) {
for (int i = mActivities.size() - 1; i >= 0; --i) {
final ActivityRecord r = mActivities.get(i);
- if (r.applyAppSpecificConfig(nightMode, localesOverride) && r.mVisibleRequested) {
+ // Activities from other packages could be sharing this process. Only propagate updates
+ // to those activities that are part of the package whose app-specific settings changed
+ if (packageName.equals(r.packageName)
+ && r.applyAppSpecificConfig(nightMode, localesOverride)
+ && r.mVisibleRequested) {
r.ensureActivityConfiguration(0 /* globalChanges */, true /* preserveWindow */);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 0a02b44..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);
@@ -4887,20 +4910,15 @@
if (hasSurface) {
mWmService.mDestroySurface.add(this);
}
+ if (mRemoveOnExit) {
+ mWmService.mPendingRemove.add(this);
+ mRemoveOnExit = false;
+ }
}
mAnimatingExit = false;
getDisplayContent().mWallpaperController.hideWallpapers(this);
}
- @Override
- boolean handleCompleteDeferredRemoval() {
- if (mRemoveOnExit) {
- mRemoveOnExit = false;
- removeImmediately();
- }
- return super.handleCompleteDeferredRemoval();
- }
-
boolean clearAnimatingFlags() {
boolean didSomething = false;
// We don't want to clear it out for windows that get replaced, because the
@@ -5403,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/Android.bp b/services/core/jni/Android.bp
index 99abf44..f9de53c 100644
--- a/services/core/jni/Android.bp
+++ b/services/core/jni/Android.bp
@@ -44,7 +44,6 @@
"com_android_server_lights_LightsService.cpp",
"com_android_server_location_GnssLocationProvider.cpp",
"com_android_server_locksettings_SyntheticPasswordManager.cpp",
- "com_android_server_net_NetworkStatsService.cpp",
"com_android_server_power_PowerManagerService.cpp",
"com_android_server_powerstats_PowerStatsService.cpp",
"com_android_server_hint_HintManagerService.cpp",
@@ -71,7 +70,6 @@
"com_android_server_wm_TaskFpsCallbackController.cpp",
"onload.cpp",
":lib_cachedAppOptimizer_native",
- ":lib_networkStatsFactory_native",
":lib_gameManagerService_native",
],
@@ -86,7 +84,6 @@
header_libs: [
"bionic_libc_platform_headers",
- "bpf_connectivity_headers",
],
}
@@ -145,9 +142,6 @@
"libhidlbase",
"libutils",
"libhwui",
- "libbpf_android",
- "libnetdutils",
- "libnetworkstats",
"libpsi",
"libdataloader",
"libincfs",
@@ -214,13 +208,6 @@
}
filegroup {
- name: "lib_networkStatsFactory_native",
- srcs: [
- "com_android_server_net_NetworkStatsFactory.cpp",
- ],
-}
-
-filegroup {
name: "lib_cachedAppOptimizer_native",
srcs: [
"com_android_server_am_CachedAppOptimizer.cpp",
diff --git a/services/core/jni/com_android_server_input_InputManagerService.cpp b/services/core/jni/com_android_server_input_InputManagerService.cpp
index 8cb27e1..3c122b0 100644
--- a/services/core/jni/com_android_server_input_InputManagerService.cpp
+++ b/services/core/jni/com_android_server_input_InputManagerService.cpp
@@ -265,7 +265,6 @@
base::Result<std::unique_ptr<InputChannel>> createInputChannel(JNIEnv* env,
const std::string& name);
base::Result<std::unique_ptr<InputChannel>> createInputMonitor(JNIEnv* env, int32_t displayId,
- bool isGestureMonitor,
const std::string& name,
int32_t pid);
status_t removeInputChannel(JNIEnv* env, const sp<IBinder>& connectionToken);
@@ -522,11 +521,9 @@
}
base::Result<std::unique_ptr<InputChannel>> NativeInputManager::createInputMonitor(
- JNIEnv* /* env */, int32_t displayId, bool isGestureMonitor, const std::string& name,
- int32_t pid) {
+ JNIEnv* /* env */, int32_t displayId, const std::string& name, int32_t pid) {
ATRACE_CALL();
- return mInputManager->getDispatcher().createInputMonitor(displayId, isGestureMonitor, name,
- pid);
+ return mInputManager->getDispatcher().createInputMonitor(displayId, name, pid);
}
status_t NativeInputManager::removeInputChannel(JNIEnv* /* env */,
@@ -1659,7 +1656,7 @@
}
static jobject nativeCreateInputMonitor(JNIEnv* env, jclass /* clazz */, jlong ptr, jint displayId,
- jboolean isGestureMonitor, jstring nameObj, jint pid) {
+ jstring nameObj, jint pid) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
if (displayId == ADISPLAY_ID_NONE) {
@@ -1672,7 +1669,7 @@
std::string name = nameChars.c_str();
base::Result<std::unique_ptr<InputChannel>> inputChannel =
- im->createInputMonitor(env, displayId, isGestureMonitor, name, pid);
+ im->createInputMonitor(env, displayId, name, pid);
if (!inputChannel.ok()) {
std::string message = inputChannel.error().message();
@@ -1707,7 +1704,6 @@
im->pilferPointers(token);
}
-
static void nativeSetInputFilterEnabled(JNIEnv* /* env */, jclass /* clazz */,
jlong ptr, jboolean enabled) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
@@ -1715,11 +1711,13 @@
im->getInputManager()->getDispatcher().setInputFilterEnabled(enabled);
}
-static void nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */,
- jlong ptr, jboolean inTouchMode) {
+static jboolean nativeSetInTouchMode(JNIEnv* /* env */, jclass /* clazz */, jlong ptr,
+ jboolean inTouchMode, jint pid, jint uid,
+ jboolean hasPermission) {
NativeInputManager* im = reinterpret_cast<NativeInputManager*>(ptr);
- im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode);
+ return im->getInputManager()->getDispatcher().setInTouchMode(inTouchMode, pid, uid,
+ hasPermission);
}
static void nativeSetMaximumObscuringOpacityForTouch(JNIEnv* /* env */, jclass /* clazz */,
@@ -2381,12 +2379,12 @@
{"nativeGetKeyCodeForKeyLocation", "(JII)I", (void*)nativeGetKeyCodeForKeyLocation},
{"nativeCreateInputChannel", "(JLjava/lang/String;)Landroid/view/InputChannel;",
(void*)nativeCreateInputChannel},
- {"nativeCreateInputMonitor", "(JIZLjava/lang/String;I)Landroid/view/InputChannel;",
+ {"nativeCreateInputMonitor", "(JILjava/lang/String;I)Landroid/view/InputChannel;",
(void*)nativeCreateInputMonitor},
{"nativeRemoveInputChannel", "(JLandroid/os/IBinder;)V", (void*)nativeRemoveInputChannel},
{"nativePilferPointers", "(JLandroid/os/IBinder;)V", (void*)nativePilferPointers},
{"nativeSetInputFilterEnabled", "(JZ)V", (void*)nativeSetInputFilterEnabled},
- {"nativeSetInTouchMode", "(JZ)V", (void*)nativeSetInTouchMode},
+ {"nativeSetInTouchMode", "(JZIIZ)Z", (void*)nativeSetInTouchMode},
{"nativeSetMaximumObscuringOpacityForTouch", "(JF)V",
(void*)nativeSetMaximumObscuringOpacityForTouch},
{"nativeSetBlockUntrustedTouchesMode", "(JI)V", (void*)nativeSetBlockUntrustedTouchesMode},
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 161d7ce..0584604 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -244,6 +244,9 @@
return hasLatLong(location.v1_0);
}
+bool isSvStatusRegistered = false;
+bool isNmeaRegistered = false;
+
} // namespace
static inline jboolean boolToJbool(bool value) {
@@ -505,6 +508,13 @@
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 (!isSvStatusRegistered) {
+ return Void();
+ }
+ }
+
JNIEnv* env = getJniEnv();
uint32_t listSize = getGnssSvInfoListSize(svStatus);
@@ -566,8 +576,12 @@
return Void();
}
-Return<void> GnssCallback::gnssNmeaCb(
- int64_t timestamp, const ::android::hardware::hidl_string& nmea) {
+Return<void> GnssCallback::gnssNmeaCb(int64_t timestamp,
+ const ::android::hardware::hidl_string& nmea) {
+ // In HIDL, if no listener is registered, do not report nmea to the framework.
+ if (!isNmeaRegistered) {
+ return Void();
+ }
JNIEnv* env = getJniEnv();
/*
* The Java code will call back to read these values.
@@ -680,6 +694,12 @@
}
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 (!isNmeaRegistered) {
+ return Status::ok();
+ }
+ }
JNIEnv* env = getJniEnv();
/*
* The Java code will call back to read these values.
@@ -1562,6 +1582,58 @@
return checkHidlReturn(result, "IGnss stop() failed.");
}
+static jboolean android_location_gnss_hal_GnssNative_start_sv_status_collection(JNIEnv* /* env */,
+ jclass) {
+ isSvStatusRegistered = true;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->startSvStatus();
+ return checkAidlStatus(status, "IGnssAidl startSvStatus() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_stop_sv_status_collection(JNIEnv* /* env */,
+ jclass) {
+ isSvStatusRegistered = false;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stopSvStatus();
+ return checkAidlStatus(status, "IGnssAidl stopSvStatus() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_start_nmea_message_collection(
+ JNIEnv* /* env */, jclass) {
+ isNmeaRegistered = true;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->startNmea();
+ return checkAidlStatus(status, "IGnssAidl startNmea() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
+static jboolean android_location_gnss_hal_GnssNative_stop_nmea_message_collection(JNIEnv* /* env */,
+ jclass) {
+ isNmeaRegistered = false;
+ if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
+ auto status = gnssHalAidl->stopNmea();
+ return checkAidlStatus(status, "IGnssAidl stopNmea() failed.");
+ }
+ if (gnssHal == nullptr) {
+ return JNI_FALSE;
+ }
+ return JNI_TRUE;
+}
+
static void android_location_gnss_hal_GnssNative_delete_aiding_data(JNIEnv* /* env */, jclass,
jint flags) {
if (gnssHalAidl != nullptr && gnssHalAidl->getInterfaceVersion() >= 2) {
@@ -1989,7 +2061,7 @@
jfloat eplMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEpl);
jfloat eplUncMeters = env->CallFloatMethod(singleSatCorrectionObj, method_correctionSatEplUnc);
uint16_t corrFlags = static_cast<uint16_t>(correctionFlags);
- jobject reflectingPlaneObj;
+ jobject reflectingPlaneObj = nullptr;
bool has_ref_plane = (corrFlags & GnssSingleSatCorrectionFlags::HAS_REFLECTING_PLANE) != 0;
if (has_ref_plane) {
reflectingPlaneObj =
@@ -2013,6 +2085,7 @@
.azimuthDegrees = azimuthDegreeRefPlane,
};
}
+ env->DeleteLocalRef(reflectingPlaneObj);
SingleSatCorrection_V1_0 singleSatCorrection = {
.singleSatCorrectionFlags = corrFlags,
@@ -2044,6 +2117,7 @@
};
list[i] = singleSatCorrection_1_1;
+ env->DeleteLocalRef(singleSatCorrectionObj);
}
}
@@ -2061,6 +2135,7 @@
singleSatCorrection.constellation = static_cast<GnssConstellationType_V1_0>(constType),
list[i] = singleSatCorrection;
+ env->DeleteLocalRef(singleSatCorrectionObj);
}
}
@@ -2131,6 +2206,7 @@
hidl_vec<SingleSatCorrection_V1_0> list(len);
getSingleSatCorrectionList_1_0(env, singleSatCorrectionList, list);
+ env->DeleteLocalRef(singleSatCorrectionList);
measurementCorrections_1_0.satCorrections = list;
auto result = gnssCorrectionsIface_V1_0->setCorrections(measurementCorrections_1_0);
@@ -2365,6 +2441,16 @@
{"native_is_gnss_visibility_control_supported", "()Z",
reinterpret_cast<void*>(
android_location_gnss_hal_GnssNative_is_gnss_visibility_control_supported)},
+ {"native_start_sv_status_collection", "()Z",
+ reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_start_sv_status_collection)},
+ {"native_stop_sv_status_collection", "()Z",
+ reinterpret_cast<void*>(android_location_gnss_hal_GnssNative_stop_sv_status_collection)},
+ {"native_start_nmea_message_collection", "()Z",
+ reinterpret_cast<void*>(
+ android_location_gnss_hal_GnssNative_start_nmea_message_collection)},
+ {"native_stop_nmea_message_collection", "()Z",
+ reinterpret_cast<void*>(
+ android_location_gnss_hal_GnssNative_stop_nmea_message_collection)},
};
static const JNINativeMethod sBatchingMethods[] = {
diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp
index ba5b3f5..1c6a3b5 100644
--- a/services/core/jni/onload.cpp
+++ b/services/core/jni/onload.cpp
@@ -51,8 +51,6 @@
int register_android_server_HardwarePropertiesManagerService(JNIEnv* env);
int register_android_server_SyntheticPasswordManager(JNIEnv* env);
int register_android_hardware_display_DisplayViewport(JNIEnv* env);
-int register_android_server_net_NetworkStatsFactory(JNIEnv* env);
-int register_android_server_net_NetworkStatsService(JNIEnv* env);
int register_android_server_am_CachedAppOptimizer(JNIEnv* env);
int register_android_server_am_LowMemDetector(JNIEnv* env);
int register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(JNIEnv* env);
@@ -110,8 +108,6 @@
register_android_server_SyntheticPasswordManager(env);
register_android_graphics_GraphicsStatsService(env);
register_android_hardware_display_DisplayViewport(env);
- register_android_server_net_NetworkStatsFactory(env);
- register_android_server_net_NetworkStatsService(env);
register_android_server_am_CachedAppOptimizer(env);
register_android_server_am_LowMemDetector(env);
register_com_android_server_soundtrigger_middleware_AudioSessionProviderImpl(env);
diff --git a/services/core/lint-baseline.xml b/services/core/lint-baseline.xml
index c5b0549..69e13b3 100644
--- a/services/core/lint-baseline.xml
+++ b/services/core/lint-baseline.xml
@@ -1,158 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
-<issues format="6" by="lint 7.1.0-dev" type="baseline" client="" dependencies="true" name="" variant="all" version="7.1.0-dev">
+<issues format="5" by="lint 7.2.0-dev">
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java"
- line="1106"
- column="20"/>
+ line="1122"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" return Settings.Secure.getInt(context.getContentResolver(),"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java"
- line="1111"
- column="20"/>
+ line="1127"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
- errorLine1=" return Settings.Secure.getString(mContentResolver, mKey);"
- errorLine2=" ~~~~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
+ <location
+ file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java"
+ line="670"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
+ <location
+ file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java"
+ line="678"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
+ <location
+ file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java"
+ line="679"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
+ <location
+ file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java"
+ line="696"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
+ <location
+ file="packages/modules/Bluetooth/service/java/com/android/server/bluetooth/BluetoothManagerService.java"
+ line="705"/>
+ </issue>
+
+ <issue
+ id="NonUserGetterCalled"
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/CertBlacklister.java"
- line="73"
- column="36"/>
+ line="73"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" }"
- errorLine2=" ^">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/clipboard/ClipboardService.java"
- line="973"
- column="7"/>
+ line="1072"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" boolean accessibilityEnabled = Settings.Secure.getInt(cr,"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/DockObserver.java"
- line="176"
- column="60"/>
+ line="260"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
- errorLine1=" String mediaButtonReceiverInfo = Settings.Secure.getString(mContentResolver,"
- errorLine2=" ~~~~~~~~~">
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/media/MediaSessionService.java"
- line="928"
- column="46"/>
+ line="928"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" final boolean isSecureFrpEnabled ="
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java"
- line="1959"
- column="36"/>
+ line="1902"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" private int getUnknownSourcesSettings() {"
- errorLine2=" ~~~~~~">
- <location
- file="frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java"
- line="16741"
- column="39"/>
- </issue>
-
- <issue
- id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
- errorLine1=" String inputMethodComponent = Settings.Secure.getString(mContext.getContentResolver(),"
- errorLine2=" ~~~~~~~~~">
- <location
- file="frameworks/base/services/core/java/com/android/server/SensorPrivacyService.java"
- line="489"
- column="35"/>
- </issue>
-
- <issue
- id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. "
- errorLine1=" return Settings.Secure.getString(getContentResolverAsUser(userId), key);"
- errorLine2=" ~~~~~~~~~">
+ message="`android.provider.Settings.Secure#getString()` called from system process. Please call `android.provider.Settings.Secure#getStringForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/connectivity/Vpn.java"
- line="1994"
- column="12"/>
+ line="2069"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" return Settings.Secure.getInt(getContentResolverAsUser(userId), key, def);"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/connectivity/Vpn.java"
- line="2001"
- column="12"/>
+ line="2076"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" if (Settings.Secure.getInt(mContext.getContentResolver(),"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java"
- line="980"
- column="45"/>
+ line="984"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" && Settings.Secure.getInt(mContext.getContentResolver(),"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java"
- line="1442"
- column="36"/>
+ line="1446"/>
</issue>
<issue
id="NonUserGetterCalled"
- message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. "
- errorLine1=" && Settings.Secure.getInt(mContext.getContentResolver(),"
- errorLine2=" ~~~~~~">
+ message="`android.provider.Settings.Secure#getInt()` called from system process. Please call `android.provider.Settings.Secure#getIntForUser()` instead. ">
<location
file="frameworks/base/services/core/java/com/android/server/notification/ZenModeHelper.java"
- line="1444"
- column="36"/>
+ line="1448"/>
</issue>
</issues>
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/DevicePolicyData.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
index 26c442d..e18e002 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyData.java
@@ -23,6 +23,7 @@
import android.content.ComponentName;
import android.os.FileUtils;
import android.os.PersistableBundle;
+import android.os.UserHandle;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.DebugUtils;
@@ -83,7 +84,7 @@
private static final String ATTR_NEW_USER_DISCLAIMER = "new-user-disclaimer";
// Values of ATTR_NEW_USER_DISCLAIMER
- static final String NEW_USER_DISCLAIMER_SHOWN = "shown";
+ static final String NEW_USER_DISCLAIMER_ACKNOWLEDGED = "acked";
static final String NEW_USER_DISCLAIMER_NOT_NEEDED = "not_needed";
static final String NEW_USER_DISCLAIMER_NEEDED = "needed";
@@ -613,6 +614,28 @@
}
}
+ boolean isNewUserDisclaimerAcknowledged() {
+ if (mNewUserDisclaimer == null) {
+ if (mUserId == UserHandle.USER_SYSTEM) {
+ return true;
+ }
+ Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): mNewUserDisclaimer is null",
+ mUserId);
+ return false;
+ }
+ switch (mNewUserDisclaimer) {
+ case NEW_USER_DISCLAIMER_ACKNOWLEDGED:
+ case NEW_USER_DISCLAIMER_NOT_NEEDED:
+ return true;
+ case NEW_USER_DISCLAIMER_NEEDED:
+ return false;
+ default:
+ Slogf.w(TAG, "isNewUserDisclaimerAcknowledged(%d): invalid value %d", mUserId,
+ mNewUserDisclaimer);
+ return false;
+ }
+ }
+
void dump(IndentingPrintWriter pw) {
pw.println();
pw.println("Enabled Device Admins (User " + mUserId + ", provisioningState: "
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index e34178a..921e2e1 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -134,6 +134,7 @@
import static android.net.NetworkCapabilities.NET_ENTERPRISE_ID_1;
import static android.net.NetworkStack.PERMISSION_MAINLINE_NETWORK_STACK;
import static android.provider.Settings.Global.PRIVATE_DNS_SPECIFIER;
+import static android.provider.Settings.Secure.MANAGED_PROVISIONING_DPC_DOWNLOADED;
import static android.provider.Settings.Secure.USER_SETUP_COMPLETE;
import static android.provider.Telephony.Carriers.DPC_URI;
import static android.provider.Telephony.Carriers.ENFORCE_KEY;
@@ -214,6 +215,7 @@
import android.app.admin.UnsafeStateException;
import android.app.backup.IBackupManager;
import android.app.compat.CompatChanges;
+import android.app.role.RoleManager;
import android.app.trust.TrustManager;
import android.app.usage.UsageStatsManagerInternal;
import android.compat.annotation.ChangeId;
@@ -222,6 +224,7 @@
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.IIntentReceiver;
@@ -6847,7 +6850,7 @@
String.format(NOT_SYSTEM_CALLER_MSG, "call isAlwaysOnVpnLockdownEnabledForUser"));
synchronized (getLockObject()) {
ActiveAdmin admin = getDeviceOrProfileOwnerAdminLocked(userHandle);
- return admin != null ? admin.mAlwaysOnVpnLockdown : null;
+ return admin != null && admin.mAlwaysOnVpnLockdown;
}
}
@@ -10855,6 +10858,8 @@
final int userHandle = user.getIdentifier();
final long id = mInjector.binderClearCallingIdentity();
try {
+ maybeInstallDeviceManagerRoleHolderInUser(userHandle);
+
manageUserUnchecked(admin, profileOwner, userHandle, adminExtras,
/* showDisclaimer= */ true);
@@ -10951,7 +10956,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;
}
@@ -10978,10 +10983,11 @@
@Override
public void acknowledgeNewUserDisclaimer() {
CallerIdentity callerIdentity = getCallerIdentity();
- canManageUsers(callerIdentity);
+ Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
setShowNewUserDisclaimer(callerIdentity.getUserId(),
- DevicePolicyData.NEW_USER_DISCLAIMER_SHOWN);
+ DevicePolicyData.NEW_USER_DISCLAIMER_ACKNOWLEDGED);
}
private void setShowNewUserDisclaimer(@UserIdInt int userId, String value) {
@@ -11014,6 +11020,18 @@
}
@Override
+ public boolean isNewUserDisclaimerAcknowledged() {
+ CallerIdentity callerIdentity = getCallerIdentity();
+ Preconditions.checkCallAuthorization(canManageUsers(callerIdentity)
+ || hasCallingOrSelfPermission(permission.INTERACT_ACROSS_USERS));
+ int userId = callerIdentity.getUserId();
+ synchronized (getLockObject()) {
+ DevicePolicyData policyData = getUserData(userId);
+ return policyData.isNewUserDisclaimerAcknowledged();
+ }
+ }
+
+ @Override
public boolean removeUser(ComponentName who, UserHandle userHandle) {
Objects.requireNonNull(who, "ComponentName is null");
Objects.requireNonNull(userHandle, "UserHandle is null");
@@ -11070,6 +11088,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) {
@@ -11093,18 +11113,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);
}
@@ -11112,8 +11126,6 @@
@GuardedBy("getLockObject()")
private void setLogoutUserIdLocked(@UserIdInt int userId) {
- if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
-
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentForegroundUserId();
}
@@ -11196,8 +11208,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);
@@ -11210,11 +11221,18 @@
@Override
public int logoutUserInternal() {
CallerIdentity caller = getCallerIdentity();
- Preconditions.checkCallAuthorization(
- canManageUsers(caller) || hasCallingOrSelfPermission(permission.CREATE_USERS));
+ 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;
}
@@ -11600,6 +11618,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)
|| isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
@@ -13975,7 +13994,7 @@
synchronized (getLockObject()) {
if (isFinancedDeviceOwner(caller)) {
- enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission);
+ enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
long ident = mInjector.binderClearCallingIdentity();
try {
@@ -14036,14 +14055,14 @@
}
}
- private void enforceCanSetPermissionGrantOnFinancedDevice(
+ private void enforcePermissionGrantStateOnFinancedDevice(
String packageName, String permission) {
if (!Manifest.permission.READ_PHONE_STATE.equals(permission)) {
- throw new SecurityException("Cannot grant " + permission
- + " when managing a financed device");
+ throw new SecurityException(permission + " cannot be used when managing a financed"
+ + " device for permission grant state");
} else if (!mOwners.getDeviceOwnerPackageName().equals(packageName)) {
- throw new SecurityException("Cannot grant permission to a package that is not"
- + " the device owner");
+ throw new SecurityException("Device owner package is the only package that can be used"
+ + " for permission grant state when managing a financed device");
}
}
@@ -14052,10 +14071,14 @@
String packageName, String permission) throws RemoteException {
final CallerIdentity caller = getCallerIdentity(admin, callerPackage);
Preconditions.checkCallAuthorization(isSystemUid(caller) || (caller.hasAdminComponent()
- && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)))
+ && (isProfileOwner(caller) || isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)))
|| (caller.hasPackage() && isCallerDelegate(caller, DELEGATION_PERMISSION_GRANT)));
synchronized (getLockObject()) {
+ if (isFinancedDeviceOwner(caller)) {
+ enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
+ }
return mInjector.binderWithCleanCallingIdentity(() -> {
int granted;
if (getTargetSdk(caller.getPackageName(), caller.getUserId())
@@ -17676,6 +17699,9 @@
}
final long startTime = SystemClock.elapsedRealtime();
+
+ onCreateAndProvisionManagedProfileStarted(provisioningParams);
+
final Set<String> nonRequiredApps = provisioningParams.isLeaveAllSystemAppsEnabled()
? Collections.emptySet()
: mOverlayPackagesProvider.getNonRequiredApps(
@@ -17687,6 +17713,7 @@
Slogf.i(LOG_TAG, "Disallowed package [" + packageName + "]");
}
}
+
userInfo = mUserManager.createProfileForUserEvenWhenDisallowed(
provisioningParams.getProfileName(),
UserManager.USER_TYPE_PROFILE_MANAGED,
@@ -17705,7 +17732,7 @@
startTime,
callerPackage);
- onCreateAndProvisionManagedProfileStarted(provisioningParams);
+ maybeInstallDeviceManagerRoleHolderInUser(userInfo.id);
installExistingAdminPackage(userInfo.id, admin.getPackageName());
if (!enableAdminAndSetProfileOwner(
@@ -17773,6 +17800,43 @@
private void onCreateAndProvisionManagedProfileCompleted(
ManagedProfileProvisioningParams provisioningParams) {}
+ private void maybeInstallDeviceManagerRoleHolderInUser(int targetUserId) {
+ String deviceManagerRoleHolderPackageName = getDeviceManagerRoleHolderPackageName(mContext);
+ if (deviceManagerRoleHolderPackageName == null) {
+ Slogf.d(LOG_TAG, "No device manager role holder specified.");
+ return;
+ }
+ try {
+ if (mIPackageManager.isPackageAvailable(
+ deviceManagerRoleHolderPackageName, targetUserId)) {
+ Slogf.d(LOG_TAG, "The device manager role holder "
+ + deviceManagerRoleHolderPackageName + " is already installed in "
+ + "user " + targetUserId);
+ return;
+ }
+ Slogf.d(LOG_TAG, "Installing the device manager role holder "
+ + deviceManagerRoleHolderPackageName + " in user " + targetUserId);
+ mIPackageManager.installExistingPackageAsUser(
+ deviceManagerRoleHolderPackageName,
+ targetUserId,
+ PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS,
+ PackageManager.INSTALL_REASON_POLICY,
+ /* whiteListedPermissions= */ null);
+ } catch (RemoteException e) {
+ // Does not happen, same process
+ }
+ }
+
+ private String getDeviceManagerRoleHolderPackageName(Context context) {
+ RoleManager roleManager = context.getSystemService(RoleManager.class);
+ List<String> roleHolders =
+ roleManager.getRoleHolders(RoleManager.ROLE_DEVICE_MANAGER);
+ if (roleHolders.isEmpty()) {
+ return null;
+ }
+ return roleHolders.get(0);
+ }
+
private void resetInteractAcrossProfilesAppOps() {
mInjector.getCrossProfileApps().clearInteractAcrossProfilesAppOps();
pregrantDefaultInteractAcrossProfilesAppOps();
@@ -18656,4 +18720,26 @@
mContext.sendBroadcastAsUser(intent, user);
}
}
+
+ public boolean isDpcDownloaded() {
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ ContentResolver cr = mContext.getContentResolver();
+
+ return mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.getIntForUser(
+ cr, MANAGED_PROVISIONING_DPC_DOWNLOADED,
+ /* def= */ 0, /* userHandle= */ cr.getUserId())
+ == 1);
+ }
+
+ public void setDpcDownloaded(boolean downloaded) {
+ Preconditions.checkCallAuthorization(hasCallingOrSelfPermission(
+ android.Manifest.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS));
+
+ int setTo = downloaded ? 1 : 0;
+
+ mInjector.binderWithCleanCallingIdentity(() -> Settings.Secure.putInt(
+ mContext.getContentResolver(), MANAGED_PROVISIONING_DPC_DOWNLOADED, setTo));
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
index 6bc7ba6..f0ceb31 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/PolicyVersionUpgrader.java
@@ -184,7 +184,7 @@
Files.write(file.toPath(), versionBytes);
versionFile.commit();
} catch (IOException e) {
- Slog.e(LOG_TAG, String.format("Writing version %d failed: %s", version), e);
+ Slog.e(LOG_TAG, String.format("Writing version %d failed", version), e);
versionFile.rollback();
}
}
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index c9aeabd..a7539b5 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -54,7 +54,6 @@
import android.net.ConnectivityManager;
import android.net.ConnectivityModuleConnector;
import android.net.NetworkStackClient;
-import android.net.TrafficStats;
import android.os.BaseBundle;
import android.os.Binder;
import android.os.Build;
@@ -85,6 +84,7 @@
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.DisplayMetrics;
+import android.util.Dumpable;
import android.util.EventLog;
import android.util.IndentingPrintWriter;
import android.util.Pair;
@@ -143,7 +143,6 @@
import com.android.server.media.metrics.MediaMetricsManagerService;
import com.android.server.media.projection.MediaProjectionManagerService;
import com.android.server.net.NetworkPolicyManagerService;
-import com.android.server.net.NetworkStatsService;
import com.android.server.net.watchlist.NetworkWatchlistService;
import com.android.server.notification.NotificationManagerService;
import com.android.server.oemlock.OemLockService;
@@ -154,6 +153,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 +224,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;
@@ -366,6 +366,8 @@
"com.android.server.adb.AdbService$Lifecycle";
private static final String SPEECH_RECOGNITION_MANAGER_SERVICE_CLASS =
"com.android.server.speech.SpeechRecognitionManagerService";
+ private static final String WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS =
+ "com.android.server.wallpapereffectsgeneration.WallpaperEffectsGenerationManagerService";
private static final String APP_PREDICTION_MANAGER_SERVICE_CLASS =
"com.android.server.appprediction.AppPredictionManagerService";
private static final String CONTENT_SUGGESTIONS_SERVICE_CLASS =
@@ -394,6 +396,8 @@
"com.android.server.media.MediaResourceMonitorService";
private static final String CONNECTIVITY_SERVICE_INITIALIZER_CLASS =
"com.android.server.ConnectivityServiceInitializer";
+ private static final String NETWORK_STATS_SERVICE_INITIALIZER_CLASS =
+ "com.android.server.NetworkStatsServiceInitializer";
private static final String IP_CONNECTIVITY_METRICS_CLASS =
"com.android.server.connectivity.IpConnectivityMetrics";
private static final String MEDIA_COMMUNICATION_SERVICE_CLASS =
@@ -660,7 +664,12 @@
}
@Override
- public void dump(IndentingPrintWriter pw, String[] args) {
+ public String getDumpableName() {
+ return SystemServer.class.getSimpleName();
+ }
+
+ @Override
+ public void dump(PrintWriter pw, String[] args) {
pw.printf("Runtime restart: %b\n", mRuntimeRestart);
pw.printf("Start count: %d\n", mStartCount);
pw.print("Runtime start-up time: ");
@@ -1390,10 +1399,8 @@
DynamicSystemService dynamicSystem = null;
IStorageManager storageManager = null;
NetworkManagementService networkManagement = null;
- IpSecService ipSecService = null;
VpnManagerService vpnManager = null;
VcnManagementService vcnManagement = null;
- NetworkStatsService networkStats = null;
NetworkPolicyManagerService networkPolicy = null;
WindowManagerService wm = null;
SerialService serial = null;
@@ -1470,7 +1477,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();
@@ -1478,7 +1485,7 @@
t.traceBegin("StartTelephonyRegistry");
telephonyRegistry = new TelephonyRegistry(
- context, new TelephonyRegistry.ConfigurationProvider());
+ context, new TelephonyRegistry.ConfigurationProvider());
ServiceManager.addService("telephony.registry", telephonyRegistry);
t.traceEnd();
@@ -1503,6 +1510,10 @@
SQLiteCompatibilityWalFlags.reset();
t.traceEnd();
+ t.traceBegin("UpdateWatchdogTimeout");
+ Watchdog.getInstance().registerSettingsObserver(context);
+ t.traceEnd();
+
// Records errors and logs, for example wtf()
// Currently this service indirectly depends on SettingsProvider so do this after
// InstallSystemProviders.
@@ -1895,16 +1906,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();
@@ -1925,13 +1926,10 @@
t.traceEnd();
t.traceBegin("StartNetworkStatsService");
- try {
- networkStats = NetworkStatsService.create(context);
- ServiceManager.addService(Context.NETWORK_STATS_SERVICE, networkStats);
- TrafficStats.init(context);
- } catch (Throwable e) {
- reportWtf("starting NetworkStats Service", e);
- }
+ // This has to be called before NetworkPolicyManager because NetworkPolicyManager
+ // needs to take NetworkStatsService to initialize.
+ mSystemServiceManager.startServiceFromJar(NETWORK_STATS_SERVICE_INITIALIZER_CLASS,
+ CONNECTIVITY_SERVICE_APEX_PATH);
t.traceEnd();
t.traceBegin("StartNetworkPolicyManagerService");
@@ -2126,6 +2124,14 @@
Slog.i(TAG, "Wallpaper service disabled by config");
}
+ // WallpaperEffectsGeneration manager service
+ // TODO (b/135218095): Use deviceHasConfigString(context,
+ // R.string.config_defaultWallpaperEffectsGenerationService)
+ t.traceBegin("StartWallpaperEffectsGenerationService");
+ mSystemServiceManager.startService(
+ WALLPAPER_EFFECTS_GENERATION_MANAGER_SERVICE_CLASS);
+ t.traceEnd();
+
t.traceBegin("StartAudioService");
if (!isArc) {
mSystemServiceManager.startService(AudioService.Lifecycle.class);
@@ -2775,7 +2781,6 @@
// These are needed to propagate to the runnable below.
final NetworkManagementService networkManagementF = networkManagement;
- final NetworkStatsService networkStatsF = networkStats;
final NetworkPolicyManagerService networkPolicyF = networkPolicy;
final CountryDetectorService countryDetectorF = countryDetector;
final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater;
@@ -2783,7 +2788,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;
@@ -2875,24 +2879,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) {
- networkStatsF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making Network Stats Service ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeConnectivityServiceReady");
try {
if (connectivityF != null) {
@@ -3008,7 +2994,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);
}
@@ -3073,10 +3061,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);
@@ -3177,11 +3167,6 @@
}
private void startAmbientContextService(@NonNull TimingsTraceAndSlog t) {
- if (!AmbientContextManagerService.isDetectionServiceConfigured()) {
- Slog.d(TAG, "AmbientContextDetectionService is not configured on this device");
- return;
- }
-
t.traceBegin("StartAmbientContextService");
mSystemServiceManager.startService(AmbientContextManagerService.class);
t.traceEnd();
diff --git a/services/people/java/com/android/server/people/data/EventIndex.java b/services/people/java/com/android/server/people/data/EventIndex.java
index 6a13b0e..02afb8d 100644
--- a/services/people/java/com/android/server/people/data/EventIndex.java
+++ b/services/people/java/com/android/server/people/data/EventIndex.java
@@ -254,7 +254,7 @@
@Override
public int hashCode() {
- return Objects.hash(mLastUpdatedTime, mEventBitmaps);
+ return Objects.hash(mLastUpdatedTime, Arrays.hashCode(mEventBitmaps));
}
synchronized void writeToProto(@NonNull ProtoOutputStream protoOutputStream) {
diff --git a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
index 9e83f8e..ca9ff6f 100644
--- a/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
+++ b/services/robotests/backup/src/com/android/server/backup/keyvalue/KeyValueBackupTaskTest.java
@@ -2727,7 +2727,7 @@
// The second line will throw NPE because it will call lambda 1 with null, since argThat()
// returns null. So we guard against that by checking for null.
return packageInfo ->
- packageInfo != null && packageInfo.packageName.equals(packageInfo.packageName);
+ packageInfo != null && packageInfo.packageName.equals(packageData.packageName);
}
/** Matches {@link ApplicationInfo} whose package name is {@code packageData.packageName}. */
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/Android.bp b/services/tests/mockingservicestests/Android.bp
index 9e221be..75669d5 100644
--- a/services/tests/mockingservicestests/Android.bp
+++ b/services/tests/mockingservicestests/Android.bp
@@ -52,6 +52,7 @@
"service-blobstore",
"service-jobscheduler",
"service-permission.impl",
+ "service-supplementalprocess.impl",
"services.core",
"services.devicepolicy",
"services.net",
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml
new file mode 100644
index 0000000..eb15451
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_disabled.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<game-mode-config
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsPerformanceGameMode="false"
+ android:supportsBatteryGameMode="false"
+ android:allowGameAngleDriver="false"
+ android:allowGameDownscaling="false"
+ android:allowGameFpsOverride="false"
+/>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml
new file mode 100644
index 0000000..65b7467
--- /dev/null
+++ b/services/tests/mockingservicestests/res/xml/gama_manager_service_metadata_config_enabled.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<game-mode-config
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:supportsPerformanceGameMode="false"
+ android:supportsBatteryGameMode="false"
+ android:allowGameAngleDriver="true"
+ android:allowGameDownscaling="true"
+ android:allowGameFpsOverride="true"
+/>
\ No newline at end of file
diff --git a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
index fec9b12..e89c812 100644
--- a/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
+++ b/services/tests/mockingservicestests/src/android/service/games/GameSessionTest.java
@@ -359,6 +359,34 @@
LifecycleTrackingGameSession.LifecycleMethodCall.ON_DESTROY).inOrder();
}
+ @Test
+ public void dispatchTransientVisibilityChanged_valueUnchanged_doesNotInvokeCallback() {
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+
+ assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures).hasSize(0);
+ }
+
+ @Test
+ public void dispatchTransientVisibilityChanged_valueChanged_invokesCallback() {
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+
+ assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures)
+ .containsExactly(true).inOrder();
+ }
+
+ @Test
+ public void dispatchTransientVisibilityChanged_manyTimes_invokesCallbackWhenValueChanges() {
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(false);
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+ mGameSession.dispatchTransientSystemBarVisibilityFromRevealGestureChanged(true);
+
+ assertThat(mGameSession.mCapturedTransientSystemBarVisibilityFromRevealGestures)
+ .containsExactly(true, false, true).inOrder();
+ }
+
private static class LifecycleTrackingGameSession extends GameSession {
private enum LifecycleMethodCall {
ON_CREATE,
@@ -368,6 +396,8 @@
}
final List<LifecycleMethodCall> mLifecycleMethodCalls = new ArrayList<>();
+ final List<Boolean> mCapturedTransientSystemBarVisibilityFromRevealGestures =
+ new ArrayList<>();
@Override
public void onCreate() {
@@ -387,5 +417,11 @@
mLifecycleMethodCalls.add(LifecycleMethodCall.ON_GAME_TASK_UNFOCUSED);
}
}
+
+ @Override
+ public void onTransientSystemBarVisibilityFromRevealGestureChanged(
+ boolean visibleDueToGesture) {
+ mCapturedTransientSystemBarVisibilityFromRevealGestures.add(visibleDueToGesture);
+ }
}
}
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/GameManagerServiceTests.java b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
index 44b81d4..d2358a0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -26,6 +26,8 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -41,6 +43,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
import android.hardware.power.Mode;
import android.os.Bundle;
import android.os.PowerManagerInternal;
@@ -159,11 +164,17 @@
mPackageName = mMockContext.getPackageName();
final ApplicationInfo applicationInfo = new ApplicationInfo();
applicationInfo.category = ApplicationInfo.CATEGORY_GAME;
+ applicationInfo.packageName = mPackageName;
final PackageInfo pi = new PackageInfo();
pi.packageName = mPackageName;
pi.applicationInfo = applicationInfo;
final List<PackageInfo> packages = new ArrayList<>();
packages.add(pi);
+
+ final Resources resources =
+ InstrumentationRegistry.getInstrumentation().getContext().getResources();
+ when(mMockPackageManager.getResourcesForApplication(anyString()))
+ .thenReturn(resources);
when(mMockPackageManager.getInstalledPackagesAsUser(anyInt(), anyInt()))
.thenReturn(packages);
when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
@@ -322,6 +333,46 @@
.thenReturn(applicationInfo);
}
+ private void mockInterventionsEnabledFromXml() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ final int resId = 123;
+ metaDataBundle.putInt(
+ GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
+ applicationInfo.metaData = metaDataBundle;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
+ "res/xml/gama_manager_service_metadata_config_enabled.xml");
+ }
+
+ private void mockInterventionsDisabledFromXml() throws Exception {
+ final ApplicationInfo applicationInfo = mMockPackageManager.getApplicationInfoAsUser(
+ mPackageName, PackageManager.GET_META_DATA, USER_ID_1);
+ Bundle metaDataBundle = new Bundle();
+ final int resId = 123;
+ metaDataBundle.putInt(
+ GameManagerService.GamePackageConfiguration.METADATA_GAME_MODE_CONFIG, resId);
+ applicationInfo.metaData = metaDataBundle;
+ when(mMockPackageManager.getApplicationInfoAsUser(anyString(), anyInt(), anyInt()))
+ .thenReturn(applicationInfo);
+ seedGameManagerServiceMetaDataFromFile(mPackageName, resId,
+ "res/xml/gama_manager_service_metadata_config_disabled.xml");
+ }
+
+
+ private void seedGameManagerServiceMetaDataFromFile(String packageName, int resId,
+ String fileName)
+ throws Exception {
+ AssetManager assetManager =
+ InstrumentationRegistry.getInstrumentation().getContext().getAssets();
+ XmlResourceParser xmlResourceParser =
+ assetManager.openXmlResourceParser(fileName);
+ when(mMockPackageManager.getXml(eq(packageName), eq(resId), any()))
+ .thenReturn(xmlResourceParser);
+ }
+
/**
* By default game mode is not supported.
*/
@@ -511,8 +562,8 @@
gameManagerService.getConfig(mPackageName);
assertEquals(config.getGameModeConfiguration(gameMode).getUseAngle(), angleEnabled);
- // Validate GameManagerService.getAngleEnabled() returns the correct value.
- assertEquals(gameManagerService.getAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
+ // Validate GameManagerService.isAngleEnabled() returns the correct value.
+ assertEquals(gameManagerService.isAngleEnabled(mPackageName, USER_ID_1), angleEnabled);
}
private void checkFps(GameManagerService gameManagerService, int gameMode, int fps) {
@@ -523,7 +574,7 @@
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
- assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
+ assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
}
/**
@@ -905,6 +956,36 @@
}
@Test
+ public void testGameModeConfigAllowFpsTrue() throws Exception {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ mockInterventionsEnabledFromXml();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName);
+ assertEquals(90,
+ config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
+ assertEquals(30, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
+ }
+
+ @Test
+ public void testGameModeConfigAllowFpsFalse() throws Exception {
+ mockDeviceConfigAll();
+ mockModifyGameModeGranted();
+ mockInterventionsDisabledFromXml();
+ GameManagerService gameManagerService = new GameManagerService(mMockContext,
+ mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameManagerService.GamePackageConfiguration config =
+ gameManagerService.getConfig(mPackageName);
+ assertEquals(0,
+ config.getGameModeConfiguration(GameManager.GAME_MODE_PERFORMANCE).getFps());
+ assertEquals(0, config.getGameModeConfiguration(GameManager.GAME_MODE_BATTERY).getFps());
+ }
+
+ @Test
public void testInterventionFps() throws Exception {
mockDeviceConfigAll();
mockModifyGameModeGranted();
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 317a51b..ed232e5 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameServiceProviderInstanceImplTest.java
@@ -31,7 +31,6 @@
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import android.Manifest;
@@ -61,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;
@@ -72,6 +72,7 @@
import com.android.internal.util.FunctionalUtils.ThrowingConsumer;
import com.android.internal.util.Preconditions;
import com.android.server.wm.WindowManagerInternal;
+import com.android.server.wm.WindowManagerInternal.TaskSystemBarsListener;
import com.android.server.wm.WindowManagerService;
import org.junit.After;
@@ -112,6 +113,7 @@
private static final ComponentName GAME_B_MAIN_ACTIVITY =
new ComponentName(GAME_B_PACKAGE, "com.package.game.b.MainActivity");
+
private static final Bitmap TEST_BITMAP = Bitmap.createBitmap(32, 32, Bitmap.Config.ARGB_8888);
private MockitoSession mMockingSession;
@@ -131,6 +133,7 @@
private FakeGameSessionService mFakeGameSessionService;
private FakeServiceConnector<IGameSessionService> mFakeGameSessionServiceConnector;
private ArrayList<ITaskStackListener> mTaskStackListeners;
+ private ArrayList<TaskSystemBarsListener> mTaskSystemBarsListeners;
private ArrayList<RunningTaskInfo> mRunningTaskInfos;
@Mock
@@ -159,15 +162,25 @@
mTaskStackListeners.add(invocation.getArgument(0));
return null;
}).when(mMockActivityTaskManager).registerTaskStackListener(any());
+ doAnswer(invocation -> {
+ mTaskStackListeners.remove(invocation.getArgument(0));
+ return null;
+ }).when(mMockActivityTaskManager).unregisterTaskStackListener(any());
+
+ mTaskSystemBarsListeners = new ArrayList<>();
+ doAnswer(invocation -> {
+ mTaskSystemBarsListeners.add(invocation.getArgument(0));
+ return null;
+ }).when(mMockWindowManagerInternal).registerTaskSystemBarsListener(any());
+ doAnswer(invocation -> {
+ mTaskSystemBarsListeners.remove(invocation.getArgument(0));
+ return null;
+ }).when(mMockWindowManagerInternal).unregisterTaskSystemBarsListener(any());
mRunningTaskInfos = new ArrayList<>();
when(mMockActivityTaskManager.getTasks(anyInt(), anyBoolean(), anyBoolean())).thenReturn(
mRunningTaskInfos);
- doAnswer(invocation -> {
- mTaskStackListeners.remove(invocation.getArgument(0));
- return null;
- }).when(mMockActivityTaskManager).unregisterTaskStackListener(any());
mGameServiceProviderInstance = new GameServiceProviderInstanceImpl(
new UserHandle(USER_ID),
@@ -393,8 +406,61 @@
mFakeGameSessionService.removePendingFutureForTaskId(10)
.complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
- verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
- verifyNoMoreInteractions(mMockWindowManagerInternal);
+ verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ }
+
+ @Test
+ public void taskSystemBarsListenerChanged_noAssociatedGameSession_doesNothing() {
+ mGameServiceProviderInstance.start();
+
+ dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+ taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+ 10,
+ /* areVisible= */ false,
+ /* wereRevealedFromSwipeOnSystemBar= */ false);
+ });
+ }
+
+ @Test
+ public void systemBarsTransientShownDueToGesture_hasGameSession_propagatesToGameSession() {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+ taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+ 10,
+ /* areVisible= */ true,
+ /* wereRevealedFromSwipeOnSystemBar= */ true);
+ });
+
+ assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isTrue();
+ }
+
+ @Test
+ public void systemBarsTransientShownButNotGesture_hasGameSession_notPropagatedToGameSession() {
+ mGameServiceProviderInstance.start();
+ startTask(10, GAME_A_MAIN_ACTIVITY);
+ mFakeGameService.requestCreateGameSession(10);
+
+ FakeGameSession gameSession10 = new FakeGameSession();
+ SurfacePackage mockSurfacePackage10 = Mockito.mock(SurfacePackage.class);
+ mFakeGameSessionService.removePendingFutureForTaskId(10)
+ .complete(new CreateGameSessionResult(gameSession10, mockSurfacePackage10));
+
+ dispatchTaskSystemBarsEvent(taskSystemBarsListener -> {
+ taskSystemBarsListener.onTransientSystemBarsVisibilityChanged(
+ 10,
+ /* areVisible= */ true,
+ /* wereRevealedFromSwipeOnSystemBar= */ false);
+ });
+
+ assertThat(gameSession10.mAreTransientSystemBarsVisibleFromRevealGesture).isFalse();
}
@Test
@@ -490,9 +556,9 @@
stopTask(10);
- verify(mMockWindowManagerInternal).addTaskOverlay(eq(10), eq(mockSurfacePackage10));
- verify(mMockWindowManagerInternal).removeTaskOverlay(eq(10), eq(mockSurfacePackage10));
- verifyNoMoreInteractions(mMockWindowManagerInternal);
+ verify(mMockWindowManagerInternal).addTrustedTaskOverlay(eq(10), eq(mockSurfacePackage10));
+ verify(mMockWindowManagerInternal).removeTrustedTaskOverlay(eq(10),
+ eq(mockSurfacePackage10));
}
@Test
@@ -682,6 +748,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<>();
@@ -690,18 +761,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<>();
@@ -830,6 +911,13 @@
mMockContext.setPermission(permission, PackageManager.PERMISSION_DENIED);
}
+ private void dispatchTaskSystemBarsEvent(
+ ThrowingConsumer<TaskSystemBarsListener> taskSystemBarsListenerConsumer) {
+ for (TaskSystemBarsListener listener : mTaskSystemBarsListeners) {
+ taskSystemBarsListenerConsumer.accept(listener);
+ }
+ }
+
static final class FakeGameService extends IGameService.Stub {
private IGameServiceController mGameServiceController;
@@ -944,6 +1032,7 @@
private static class FakeGameSession extends IGameSession.Stub {
boolean mIsDestroyed = false;
boolean mIsFocused = false;
+ boolean mAreTransientSystemBarsVisibleFromRevealGesture = false;
@Override
public void onDestroyed() {
@@ -954,6 +1043,11 @@
public void onTaskFocusChanged(boolean focused) {
mIsFocused = focused;
}
+
+ @Override
+ public void onTransientSystemBarVisibilityFromRevealGestureChanged(boolean areVisible) {
+ mAreTransientSystemBarsVisibleFromRevealGesture = areVisible;
+ }
}
private final class MockContext extends ContextWrapper {
@@ -1005,5 +1099,4 @@
return mLastStartedIntent;
}
}
-
}
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/mockingservicestests/src/com/android/server/pm/MockSystem.kt b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
index 6ae0031..4ec1641 100644
--- a/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
+++ b/services/tests/mockingservicestests/src/com/android/server/pm/MockSystem.kt
@@ -69,6 +69,7 @@
import com.android.server.pm.pkg.parsing.ParsingPackage
import com.android.server.pm.pkg.parsing.ParsingPackageUtils
import com.android.server.pm.verify.domain.DomainVerificationManagerInternal
+import com.android.server.supplementalprocess.SupplementalProcessManagerLocal
import com.android.server.testutils.TestHandler
import com.android.server.testutils.mock
import com.android.server.testutils.nullable
@@ -335,6 +336,7 @@
stageServicesExtensionScan()
stageSystemSharedLibraryScan()
stagePermissionsControllerScan()
+ stageSupplementalProcessScan()
stageInstantAppResolverScan()
}
@@ -569,6 +571,22 @@
}
@Throws(Exception::class)
+ private fun stageSupplementalProcessScan() {
+ stageScanNewPackage("com.android.supplemental.process",
+ 1L, systemPartitions[0].privAppFolder,
+ withPackage = { pkg: PackageImpl ->
+ val applicationInfo: ApplicationInfo = createBasicApplicationInfo(pkg)
+ mockQueryServices(SupplementalProcessManagerLocal.SERVICE_INTERFACE,
+ createBasicServiceInfo(
+ pkg, applicationInfo, "SupplementalProcessService"))
+ pkg
+ },
+ withSetting = { setting: PackageSettingBuilder ->
+ setting.setPkgFlags(ApplicationInfo.FLAG_SYSTEM)
+ })
+ }
+
+ @Throws(Exception::class)
private fun stageSystemSharedLibraryScan() {
stageScanNewPackage("android.ext.shared",
1L, systemPartitions[0].appFolder,
diff --git a/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
new file mode 100644
index 0000000..fa3e05a
--- /dev/null
+++ b/services/tests/mockingservicestests/src/com/android/server/sensorprivacy/CameraPrivacyLightControllerTest.java
@@ -0,0 +1,248 @@
+/*
+ * 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.sensorprivacy;
+
+import static android.app.AppOpsManager.OPSTR_CAMERA;
+
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
+
+import static org.junit.Assert.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.hardware.lights.Light;
+import android.hardware.lights.LightsManager;
+import android.hardware.lights.LightsRequest;
+import android.permission.PermissionManager;
+import android.util.ArraySet;
+
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoSession;
+import org.mockito.quality.Strictness;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Random;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CameraPrivacyLightControllerTest {
+
+ private MockitoSession mMockitoSession;
+
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private LightsManager mLightsManager;
+
+ @Mock
+ private AppOpsManager mAppOpsManager;
+
+ @Mock
+ private LightsManager.LightsSession mLightsSession;
+
+ private ArgumentCaptor<AppOpsManager.OnOpActiveChangedListener> mAppOpsListenerCaptor =
+ ArgumentCaptor.forClass(AppOpsManager.OnOpActiveChangedListener.class);
+
+ private ArgumentCaptor<LightsRequest> mLightsRequestCaptor =
+ ArgumentCaptor.forClass(LightsRequest.class);
+
+ private Set<String> mExemptedPackages = new ArraySet<>();
+ private List<Light> mLights = new ArrayList<>();
+
+ private int mNextLightId = 1;
+
+ @Before
+ public void setUp() {
+ mMockitoSession = ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .strictness(Strictness.WARN)
+ .spyStatic(PermissionManager.class)
+ .startMocking();
+
+ doReturn(mLightsManager).when(mContext).getSystemService(LightsManager.class);
+ doReturn(mAppOpsManager).when(mContext).getSystemService(AppOpsManager.class);
+
+ doReturn(mLights).when(mLightsManager).getLights();
+ doReturn(mLightsSession).when(mLightsManager).openSession(anyInt());
+
+ doReturn(mExemptedPackages)
+ .when(() -> PermissionManager.getIndicatorExemptedPackages(any()));
+ }
+
+ @After
+ public void tearDown() {
+ mExemptedPackages.clear();
+ mLights.clear();
+
+ mMockitoSession.finishMocking();
+ }
+
+ @Test
+ public void testAppsOpsListenerNotRegisteredWithoutCameraLights() {
+ mLights.add(getNextLight(false));
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager, times(0)).startWatchingActive(any(), any(), any());
+ }
+
+ @Test
+ public void testAppsOpsListenerRegisteredWithCameraLight() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager, times(1)).startWatchingActive(any(), any(), any());
+ }
+
+ @Test
+ public void testAllCameraLightsAreRequestedOnOpActive() {
+ Random r = new Random(0);
+ for (int i = 0; i < 30; i++) {
+ mLights.add(getNextLight(r.nextBoolean()));
+ }
+
+ new CameraPrivacyLightController(mContext);
+
+ // Verify no session has been opened at this point.
+ verify(mLightsManager, times(0)).openSession(anyInt());
+
+ // Set camera op as active.
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+ mAppOpsListenerCaptor.getValue().onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+
+ // Verify session has been opened exactly once
+ verify(mLightsManager, times(1)).openSession(anyInt());
+
+ verify(mLightsSession).requestLights(mLightsRequestCaptor.capture());
+ assertEquals("requestLights() not invoked exactly once",
+ 1, mLightsRequestCaptor.getAllValues().size());
+
+ List<Integer> expectedCameraLightIds = mLights.stream()
+ .filter(l -> l.getType() == Light.LIGHT_TYPE_CAMERA)
+ .map(l -> l.getId())
+ .collect(Collectors.toList());
+ List<Integer> lightsRequestLightIds = mLightsRequestCaptor.getValue().getLights();
+
+ // We don't own lights framework, don't assume it will retain order
+ lightsRequestLightIds.sort(Integer::compare);
+ expectedCameraLightIds.sort(Integer::compare);
+
+ assertEquals(expectedCameraLightIds, lightsRequestLightIds);
+ }
+
+ @Test
+ public void testWillOnlyOpenOnceWhenTwoPackagesStartOp() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ verify(mLightsManager, times(1)).openSession(anyInt());
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10102, "pkg2", true);
+ verify(mLightsManager, times(1)).openSession(anyInt());
+ }
+
+ @Test
+ public void testWillCloseOnFinishOp() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+
+ verify(mLightsSession, times(0)).close();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", false);
+ verify(mLightsSession, times(1)).close();
+ }
+
+ @Test
+ public void testWillCloseOnFinishOpForAllPackages() {
+ mLights.add(getNextLight(true));
+
+ new CameraPrivacyLightController(mContext);
+
+ int numUids = 100;
+ List<Integer> uids = new ArrayList<>(numUids);
+ for (int i = 0; i < numUids; i++) {
+ uids.add(10001 + i);
+ }
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+
+ for (int i = 0; i < numUids; i++) {
+ listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), true);
+ }
+
+ // Change the order which their ops are finished
+ Collections.shuffle(uids, new Random(0));
+
+ for (int i = 0; i < numUids - 1; i++) {
+ listener.onOpActiveChanged(OPSTR_CAMERA, uids.get(i), "pkg" + (int) uids.get(i), false);
+ }
+
+ verify(mLightsSession, times(0)).close();
+ int lastUid = uids.get(numUids - 1);
+ listener.onOpActiveChanged(OPSTR_CAMERA, lastUid, "pkg" + lastUid, false);
+ verify(mLightsSession, times(1)).close();
+ }
+
+ @Test
+ public void testWontOpenForExemptedPackage() {
+ mLights.add(getNextLight(true));
+ mExemptedPackages.add("pkg1");
+
+ new CameraPrivacyLightController(mContext);
+
+ verify(mAppOpsManager).startWatchingActive(any(), any(), mAppOpsListenerCaptor.capture());
+
+ AppOpsManager.OnOpActiveChangedListener listener = mAppOpsListenerCaptor.getValue();
+ listener.onOpActiveChanged(OPSTR_CAMERA, 10101, "pkg1", true);
+ verify(mLightsManager, times(0)).openSession(anyInt());
+ }
+
+ private Light getNextLight(boolean cameraType) {
+ Light light = ExtendedMockito.mock(Light.class);
+ if (cameraType) {
+ doReturn(Light.LIGHT_TYPE_CAMERA).when(light).getType();
+ } else {
+ doReturn(Light.LIGHT_TYPE_MICROPHONE).when(light).getType();
+ }
+ doReturn(mNextLightId++).when(light).getId();
+ return light;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
new file mode 100644
index 0000000..0e84e04
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/BinaryTransparencyServiceTest.java
@@ -0,0 +1,112 @@
+/*
+ * 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;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.SystemProperties;
+
+import androidx.test.core.app.ApplicationProvider;
+import androidx.test.ext.junit.runners.AndroidJUnit4;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.FileDescriptor;
+import java.util.HashMap;
+import java.util.Map;
+
+@RunWith(AndroidJUnit4.class)
+public class BinaryTransparencyServiceTest {
+ private Context mContext;
+ private BinaryTransparencyService mBinaryTransparencyService;
+ private BinaryTransparencyService.BinaryTransparencyServiceImpl mTestInterface;
+
+ @Before
+ public void setUp() {
+ mContext = ApplicationProvider.getApplicationContext();
+ mBinaryTransparencyService = new BinaryTransparencyService(mContext);
+ mTestInterface = mBinaryTransparencyService.new BinaryTransparencyServiceImpl();
+ }
+
+ private void prepSignedInfo() {
+ // simulate what happens on boot completed phase
+ mBinaryTransparencyService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ }
+
+ private void prepApexInfo() throws RemoteException {
+ // simulates what happens to apex info after computations are done.
+ String[] args = {"get", "apex_info"};
+ mTestInterface.onShellCommand(FileDescriptor.in, FileDescriptor.out, FileDescriptor.err,
+ args, null, new ResultReceiver(null));
+ }
+
+ @Test
+ public void getSignedImageInfo_preInitialize_returnsUninitializedString() {
+ String result = mTestInterface.getSignedImageInfo();
+ Assert.assertNotNull("VBMeta digest value should not be null", result);
+ Assert.assertEquals(BinaryTransparencyService.VBMETA_DIGEST_UNINITIALIZED, result);
+ }
+
+ @Test
+ public void getSignedImageInfo_postInitialize_returnsNonErrorStrings() {
+ prepSignedInfo();
+ String result = mTestInterface.getSignedImageInfo();
+ Assert.assertNotNull("Initialized VBMeta digest string should not be null", result);
+ Assert.assertNotEquals("VBMeta digest value is uninitialized",
+ BinaryTransparencyService.VBMETA_DIGEST_UNINITIALIZED, result);
+ Assert.assertNotEquals("VBMeta value should not be unavailable",
+ BinaryTransparencyService.VBMETA_DIGEST_UNAVAILABLE, result);
+ }
+
+ @Test
+ public void getSignedImageInfo_postInitialize_returnsCorrectValue() {
+ prepSignedInfo();
+ String result = mTestInterface.getSignedImageInfo();
+ Assert.assertEquals(
+ SystemProperties.get(BinaryTransparencyService.SYSPROP_NAME_VBETA_DIGEST,
+ BinaryTransparencyService.VBMETA_DIGEST_UNAVAILABLE), result);
+ }
+
+ @Test
+ public void getApexInfo_postInitialize_returnsValidEntries() throws RemoteException {
+ prepApexInfo();
+ Map result = mTestInterface.getApexInfo();
+ Assert.assertNotNull("Apex info map should not be null", result);
+ Assert.assertFalse("Apex info map should not be empty", result.isEmpty());
+ }
+
+ @Test
+ public void getApexInfo_postInitialize_returnsActualApexs()
+ throws RemoteException, PackageManager.NameNotFoundException {
+ prepApexInfo();
+ Map result = mTestInterface.getApexInfo();
+
+ PackageManager pm = mContext.getPackageManager();
+ Assert.assertNotNull(pm);
+ HashMap<PackageInfo, String> castedResult = (HashMap<PackageInfo, String>) result;
+ for (PackageInfo packageInfo : castedResult.keySet()) {
+ Assert.assertTrue(packageInfo.packageName + "is not an APEX!", packageInfo.isApex);
+ }
+ }
+
+}
diff --git a/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java
index 96103e3..d95c9ac 100644
--- a/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/am/ActivityManagerUtilsTest.java
@@ -17,7 +17,7 @@
import static com.google.common.truth.Truth.assertThat;
-import static org.junit.Assert.fail;
+import static org.junit.Assert.assertTrue;
import androidx.test.filters.SmallTest;
@@ -89,18 +89,20 @@
}
@Test
- public void testSheckShouldSamplePackage() {
+ public void testCheckShouldSamplePackage() {
// Just make sure checkShouldSamplePackage is actually working...
+ assertFailure(() -> checkShouldSamplePackage(0.3f, 0.6f, false, true));
+ assertFailure(() -> checkShouldSamplePackage(0.6f, 0.3f, true, false));
+ }
+
+ private static void assertFailure(Runnable r) {
+ boolean failed = false;
try {
- checkShouldSamplePackage(0.3f, 0.6f, false, true);
- fail();
- } catch (AssertionError expected) {
+ r.run();
+ } catch (AssertionError e) {
+ failed = true;
}
- try {
- checkShouldSamplePackage(0.6f, 0.3f, true, false);
- fail();
- } catch (AssertionError expected) {
- }
+ assertTrue(failed);
}
private void checkShouldSamplePackage(float inputSampleRate, float expectedRate,
diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
index e1aa08d..5b3a128 100644
--- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java
@@ -29,6 +29,8 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.longThat;
import static org.mockito.Mockito.doAnswer;
@@ -38,6 +40,8 @@
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
+import android.app.usage.StorageStats;
+import android.app.usage.StorageStatsManager;
import android.app.usage.UsageEvents.Event;
import android.app.usage.UsageStatsManagerInternal;
import android.app.usage.UsageStatsManagerInternal.UsageEventListener;
@@ -48,6 +52,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
import android.content.pm.ParceledListSlice;
import android.content.pm.UserInfo;
@@ -68,10 +73,12 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.UUID;
import java.util.concurrent.Executor;
/**
@@ -104,6 +111,8 @@
@Mock
private UserManager mUserManager;
@Mock
+ private StorageStatsManager mStorageStatsManager;
+ @Mock
private HibernationStateDiskStore<UserLevelState> mUserLevelDiskStore;
@Mock
private UsageStatsManagerInternal mUsageStatsManagerInternal;
@@ -115,7 +124,7 @@
private ArgumentCaptor<UsageEventListener> mUsageEventListenerCaptor;
@Before
- public void setUp() throws RemoteException {
+ public void setUp() throws RemoteException, PackageManager.NameNotFoundException, IOException {
// Share class loader to allow access to package-private classes
System.setProperty("dexmaker.share_classloader", "true");
MockitoAnnotations.initMocks(this);
@@ -140,6 +149,11 @@
packages.add(makePackageInfo(PACKAGE_NAME_3));
doReturn(new ParceledListSlice<>(packages)).when(mIPackageManager).getInstalledPackages(
longThat(arg -> (arg & MATCH_ANY_USER) != 0), anyInt());
+ doReturn(mock(ApplicationInfo.class)).when(mIPackageManager).getApplicationInfo(
+ any(), anyLong(), anyInt());
+ StorageStats storageStats = new StorageStats();
+ doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
+ (UUID) any(), anyString(), any());
mAppHibernationService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
UserInfo userInfo = addUser(USER_ID_1);
@@ -235,6 +249,29 @@
}
@Test
+ public void testGetHibernatingPackagesForUser_doesNotReturnPackagesThatArentVisible()
+ throws RemoteException {
+ // GIVEN an unlocked user with all packages installed but only some are visible to the
+ // caller
+ UserInfo userInfo =
+ addUser(USER_ID_2, new String[]{PACKAGE_NAME_1, PACKAGE_NAME_2, PACKAGE_NAME_3});
+ doReturn(false).when(mPackageManagerInternal).canQueryPackage(anyInt(), eq(PACKAGE_NAME_2));
+ doReturn(true).when(mUserManager).isUserUnlockingOrUnlocked(USER_ID_2);
+ mAppHibernationService.onUserUnlocking(new SystemService.TargetUser(userInfo));
+
+ // WHEN packages are hibernated for the user
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_2, true);
+ mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_2, USER_ID_2, true);
+
+ // THEN the hibernating packages returned does not contain the package that was not visible
+ List<String> hibernatingPackages =
+ mAppHibernationService.getHibernatingPackagesForUser(USER_ID_2);
+ assertEquals(1, hibernatingPackages.size());
+ assertTrue(hibernatingPackages.contains(PACKAGE_NAME_1));
+ assertFalse(hibernatingPackages.contains(PACKAGE_NAME_2));
+ }
+
+ @Test
public void testUserLevelStatesInitializedFromDisk() throws RemoteException {
// GIVEN states stored on disk that match with package manager's force-stop states
List<UserLevelState> diskStates = new ArrayList<>();
@@ -381,18 +418,31 @@
}
@Test
- public void testGetHibernationStatsForUser_getsStatsForPackage() {
- // GIVEN a package is hibernating globally and for a user
+ public void testGetHibernationStatsForUser_getsStatsForPackage()
+ throws PackageManager.NameNotFoundException, IOException, RemoteException {
+ // GIVEN a package is hibernating globally and for a user with some storage saved
+ final long cacheSavings = 1000;
+ StorageStats storageStats = new StorageStats();
+ storageStats.cacheBytes = cacheSavings;
+ doReturn(storageStats).when(mStorageStatsManager).queryStatsForPackage(
+ (UUID) any(), eq(PACKAGE_NAME_1), any());
+ final long oatDeletionSavings = 2000;
+ doReturn(oatDeletionSavings).when(mPackageManagerInternal).deleteOatArtifactsOfPackage(
+ PACKAGE_NAME_1);
+
mAppHibernationService.setHibernatingGlobally(PACKAGE_NAME_1, true);
mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true);
// WHEN we ask for the hibernation stats for the package
- Map<String, HibernationStats> stats =
+ Map<String, HibernationStats> statsMap =
mAppHibernationService.getHibernationStatsForUser(
Set.of(PACKAGE_NAME_1), USER_ID_1);
- // THEN the stats exist for the package
- assertTrue(stats.containsKey(PACKAGE_NAME_1));
+ // THEN the stats exist for the package and add up to the OAT deletion and cache deletion
+ // savings
+ HibernationStats stats = statsMap.get(PACKAGE_NAME_1);
+ assertNotNull(stats);
+ assertEquals(cacheSavings + oatDeletionSavings, stats.getDiskBytesSaved());
}
@Test
@@ -519,6 +569,11 @@
}
@Override
+ public StorageStatsManager getStorageStatsManager() {
+ return mStorageStatsManager;
+ }
+
+ @Override
public UsageStatsManagerInternal getUsageStatsManagerInternal() {
return mUsageStatsManagerInternal;
}
diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
index 9ee1205..3890d4d 100644
--- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java
@@ -24,14 +24,20 @@
import static com.google.common.truth.Truth.assertThat;
+import static junit.framework.Assert.fail;
+
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
+import static org.mockito.Mockito.verifyZeroInteractions;
import android.attention.AttentionManagerInternal.AttentionCallbackInternal;
+import android.attention.AttentionManagerInternal.ProximityCallbackInternal;
import android.content.ComponentName;
import android.content.Context;
import android.os.IBinder;
@@ -42,6 +48,7 @@
import android.provider.DeviceConfig;
import android.service.attention.IAttentionCallback;
import android.service.attention.IAttentionService;
+import android.service.attention.IProximityCallback;
import androidx.test.filters.SmallTest;
@@ -49,6 +56,7 @@
import com.android.server.attention.AttentionManagerService.AttentionCheckCache;
import com.android.server.attention.AttentionManagerService.AttentionCheckCacheBuffer;
import com.android.server.attention.AttentionManagerService.AttentionHandler;
+import com.android.server.attention.AttentionManagerService.ProximityUpdate;
import org.junit.Before;
import org.junit.Test;
@@ -59,10 +67,13 @@
/**
* Tests for {@link com.android.server.attention.AttentionManagerService}
*/
+@SuppressWarnings("GuardedBy")
@SmallTest
public class AttentionManagerServiceTest {
+ private static final double PROXIMITY_SUCCESS_STATE = 1.0;
private AttentionManagerService mSpyAttentionManager;
private final int mTimeout = 1000;
+ private final Object mLock = new Object();
@Mock
private AttentionCallbackInternal mMockAttentionCallbackInternal;
@Mock
@@ -73,6 +84,8 @@
private IThermalService mMockIThermalService;
@Mock
Context mContext;
+ @Mock
+ private ProximityCallbackInternal mMockProximityCallbackInternal;
@Before
public void setUp() throws RemoteException {
@@ -84,7 +97,6 @@
doReturn(true).when(mMockIPowerManager).isInteractive();
mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null);
- Object mLock = new Object();
// setup a spy on attention manager
AttentionManagerService attentionManager = new AttentionManagerService(
mContext,
@@ -100,6 +112,119 @@
mSpyAttentionManager);
mSpyAttentionManager.mCurrentAttentionCheck = attentionCheck;
mSpyAttentionManager.mService = new MockIAttentionService();
+ doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
+ }
+
+ @Test
+ public void testRegisterProximityUpdates_returnFalseWhenServiceDisabled() {
+ mSpyAttentionManager.mIsServiceEnabled = false;
+
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isFalse();
+ }
+
+ @Test
+ public void testRegisterProximityUpdates_returnFalseWhenServiceUnavailable() {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(false).when(mSpyAttentionManager).isServiceAvailable();
+
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isFalse();
+ }
+
+ @Test
+ public void testRegisterProximityUpdates_returnFalseWhenPowerManagerNotInteract()
+ throws RemoteException {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(false).when(mMockIPowerManager).isInteractive();
+
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isFalse();
+ }
+
+ @Test
+ public void testRegisterProximityUpdates_callOnSuccess() throws RemoteException {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isTrue();
+ verify(mMockProximityCallbackInternal, times(1))
+ .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+ }
+
+ @Test
+ public void testRegisterProximityUpdates_callOnSuccessTwiceInARow() throws RemoteException {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isTrue();
+
+ ProximityUpdate prevProximityUpdate = mSpyAttentionManager.mCurrentProximityUpdate;
+ assertThat(mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal))
+ .isTrue();
+ assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isEqualTo(prevProximityUpdate);
+ verify(mMockProximityCallbackInternal, times(1))
+ .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+ }
+
+ @Test
+ public void testUnregisterProximityUpdates_noCrashWhenNoCallbackIsRegistered() {
+ mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+ verifyZeroInteractions(mMockProximityCallbackInternal);
+ }
+
+ @Test
+ public void testUnregisterProximityUpdates_noCrashWhenCallbackMismatched()
+ throws RemoteException {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+ mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+ verify(mMockProximityCallbackInternal, times(1))
+ .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+
+ ProximityCallbackInternal mismatchedCallback = new ProximityCallbackInternal() {
+ @Override
+ public void onProximityUpdate(double distance) {
+ fail("Callback shouldn't have responded.");
+ }
+ };
+ mSpyAttentionManager.onStopProximityUpdates(mismatchedCallback);
+
+ verifyNoMoreInteractions(mMockProximityCallbackInternal);
+ }
+
+ @Test
+ public void testUnregisterProximityUpdates_cancelRegistrationWhenMatched()
+ throws RemoteException {
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+ mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+ mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+
+ assertThat(mSpyAttentionManager.mCurrentProximityUpdate).isNull();
+ }
+
+ @Test
+ public void testUnregisterProximityUpdates_noCrashWhenTwiceInARow() throws RemoteException {
+ // Attention Service registers proximity updates.
+ mSpyAttentionManager.mIsServiceEnabled = true;
+ doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
+ doReturn(true).when(mMockIPowerManager).isInteractive();
+ mSpyAttentionManager.onStartProximityUpdates(mMockProximityCallbackInternal);
+ verify(mMockProximityCallbackInternal, times(1))
+ .onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+
+ // Attention Service unregisters the proximity update twice in a row.
+ mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+ mSpyAttentionManager.onStopProximityUpdates(mMockProximityCallbackInternal);
+ verifyNoMoreInteractions(mMockProximityCallbackInternal);
}
@Test
@@ -127,7 +252,6 @@
mSpyAttentionManager.mIsServiceEnabled = true;
doReturn(true).when(mSpyAttentionManager).isServiceAvailable();
doReturn(true).when(mMockIPowerManager).isInteractive();
- doNothing().when(mSpyAttentionManager).freeIfInactiveLocked();
mSpyAttentionManager.mCurrentAttentionCheck = null;
AttentionCallbackInternal callback = Mockito.mock(AttentionCallbackInternal.class);
@@ -213,6 +337,13 @@
public void cancelAttentionCheck(IAttentionCallback callback) {
}
+ public void onStartProximityUpdates(IProximityCallback callback) throws RemoteException {
+ callback.onProximityUpdate(PROXIMITY_SUCCESS_STATE);
+ }
+
+ public void onStopProximityUpdates() throws RemoteException {
+ }
+
public IBinder asBinder() {
return null;
}
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..dad9fe8 100644
--- a/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
+++ b/services/tests/servicestests/src/com/android/server/audio/AudioDeviceBrokerTest.java
@@ -28,9 +28,10 @@
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.content.Intent;
+import android.media.AudioDeviceAttributes;
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 +101,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)
@@ -186,8 +187,9 @@
doNothing().when(mSpySystemServer).broadcastStickyIntentToCurrentProfileGroup(
any(Intent.class));
- mSpyDevInventory.setWiredDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
- AudioService.CONNECTION_STATE_CONNECTED, address, name, caller);
+ mSpyDevInventory.setWiredDeviceConnectionState(new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_WIRED_HEADSET, address, name),
+ AudioService.CONNECTION_STATE_CONNECTED, caller);
Thread.sleep(MAX_MESSAGE_HANDLING_DELAY_MS);
// Verify that the sticky intent is broadcasted
@@ -208,13 +210,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 +224,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,
@@ -246,11 +248,11 @@
*/
private void checkSingleSystemConnection(BluetoothDevice btDevice) throws Exception {
final String expectedName = btDevice.getName() == null ? "" : btDevice.getName();
+ AudioDeviceAttributes expected = new AudioDeviceAttributes(
+ AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, btDevice.getAddress(), expectedName);
verify(mSpyAudioSystem, times(1)).setDeviceConnectionState(
- ArgumentMatchers.eq(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
+ ArgumentMatchers.argThat(x -> x.equalTypeAddress(expected)),
ArgumentMatchers.eq(AudioSystem.DEVICE_STATE_AVAILABLE),
- ArgumentMatchers.eq(btDevice.getAddress()),
- ArgumentMatchers.eq(expectedName),
anyInt() /*codec*/);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
index 8d706cb..1f355b0 100644
--- a/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
+++ b/services/tests/servicestests/src/com/android/server/audio/NoOpAudioSystemAdapter.java
@@ -48,11 +48,10 @@
//-----------------------------------------------------------------
// Overrides of AudioSystemAdapter
@Override
- public int setDeviceConnectionState(int device, int state, String deviceAddress,
- String deviceName, int codecFormat) {
- Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, %s, %s, 0x%s",
- Integer.toHexString(device), state, deviceAddress, deviceName,
- Integer.toHexString(codecFormat)));
+ public int setDeviceConnectionState(AudioDeviceAttributes attributes, int state,
+ int codecFormat) {
+ Log.i(TAG, String.format("setDeviceConnectionState(0x%s, %d, 0x%s",
+ attributes.toString(), state, Integer.toHexString(codecFormat)));
return AudioSystem.AUDIO_STATUS_OK;
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
index 83fa7ac..b4bb04d 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/InputControllerTest.java
@@ -21,7 +21,6 @@
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.hardware.display.DisplayManagerInternal;
@@ -30,7 +29,6 @@
import android.hardware.input.InputManagerInternal;
import android.os.Binder;
import android.os.IBinder;
-import android.os.IInputConstants;
import android.platform.test.annotations.Presubmit;
import android.view.Display;
import android.view.DisplayInfo;
@@ -81,17 +79,15 @@
}
@Test
- public void unregisterInputDevice_allMiceUnregistered_unsetValues() {
+ public void unregisterInputDevice_allMiceUnregistered_clearPointerDisplayId() {
final IBinder deviceToken = new Binder();
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
- verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f));
+ doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(
eq(Display.INVALID_DISPLAY));
- verify(mInputManagerInternalMock).setPointerAcceleration(
- eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION));
}
@Test
@@ -100,14 +96,11 @@
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken,
/* displayId= */ 1);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
- verify(mInputManagerInternalMock).setPointerAcceleration(eq(1f));
final IBinder deviceToken2 = new Binder();
mInputController.createMouse("name", /*vendorId= */ 1, /*productId= */ 1, deviceToken2,
/* displayId= */ 2);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(2));
mInputController.unregisterInputDevice(deviceToken);
verify(mInputManagerInternalMock).setVirtualMousePointerDisplayId(eq(1));
- verify(mInputManagerInternalMock, times(0)).setPointerAcceleration(
- eq((float) IInputConstants.DEFAULT_POINTER_ACCELERATION));
}
}
diff --git a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
index 33540c8..3b4aece 100644
--- a/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/companion/virtual/VirtualDeviceManagerServiceTest.java
@@ -19,6 +19,8 @@
import static com.google.common.truth.Truth.assertWithMessage;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -27,6 +29,7 @@
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
@@ -117,6 +120,8 @@
LocalServices.addService(DisplayManagerInternal.class, mDisplayManagerInternalMock);
doNothing().when(mInputManagerInternalMock).setVirtualMousePointerDisplayId(anyInt());
+ doNothing().when(mInputManagerInternalMock).setPointerAcceleration(anyFloat(), anyInt());
+ doNothing().when(mInputManagerInternalMock).setPointerIconVisible(anyBoolean(), anyInt());
LocalServices.removeServiceForTest(InputManagerInternal.class);
LocalServices.addService(InputManagerInternal.class, mInputManagerInternalMock);
@@ -353,7 +358,7 @@
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
- mInputController.mActivePointerDisplayId = 1;
+ doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendButtonEvent(BINDER, new VirtualMouseButtonEvent.Builder()
.setButtonCode(buttonCode)
.setAction(action).build());
@@ -394,7 +399,7 @@
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
- mInputController.mActivePointerDisplayId = 1;
+ doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendRelativeEvent(BINDER, new VirtualMouseRelativeEvent.Builder()
.setRelativeX(x).setRelativeY(y).build());
verify(mNativeWrapperMock).writeRelativeEvent(fd, x, y);
@@ -435,7 +440,7 @@
mInputController.mInputDeviceDescriptors.put(BINDER,
new InputController.InputDeviceDescriptor(fd, () -> {}, /* type= */ 2,
/* displayId= */ 1, PHYS));
- mInputController.mActivePointerDisplayId = 1;
+ doReturn(1).when(mInputManagerInternalMock).getVirtualMousePointerDisplayId();
mDeviceImpl.sendScrollEvent(BINDER, new VirtualMouseScrollEvent.Builder()
.setXAxisMovement(x)
.setYAxisMovement(y).build());
@@ -508,4 +513,19 @@
verify(mNativeWrapperMock).writeTouchEvent(fd, pointerId, toolType, action, x, y, pressure,
majorAxisSize);
}
+
+ @Test
+ public void setShowPointerIcon_setsValueForAllDisplays() {
+ mDeviceImpl.mVirtualDisplayIds.add(1);
+ mDeviceImpl.mVirtualDisplayIds.add(2);
+ mDeviceImpl.mVirtualDisplayIds.add(3);
+ mDeviceImpl.createVirtualMouse(1, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
+ mDeviceImpl.createVirtualMouse(2, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
+ mDeviceImpl.createVirtualMouse(3, DEVICE_NAME, VENDOR_ID, PRODUCT_ID, BINDER);
+ mDeviceImpl.setShowPointerIcon(false);
+ verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(false), anyInt());
+ verify(mInputManagerInternalMock, never()).setPointerIconVisible(eq(true), anyInt());
+ mDeviceImpl.setShowPointerIcon(true);
+ verify(mInputManagerInternalMock, times(3)).setPointerIconVisible(eq(true), anyInt());
+ }
}
diff --git a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
index c877bd1..48e2122 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/DevicePolicyManagerTest.java
@@ -120,6 +120,7 @@
import android.hardware.usb.UsbManager;
import android.net.ProfileNetworkPreference;
import android.net.Uri;
+import android.net.wifi.WifiSsid;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
@@ -164,6 +165,7 @@
import java.io.File;
import java.net.InetSocketAddress;
import java.net.Proxy;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -7779,6 +7781,7 @@
verify(getServices().userManagerInternal, never())
.setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean());
+ DpmTestUtils.assertRestrictions(new Bundle(), dpm.getUserRestrictions(admin1));
}
}
}
@@ -7810,6 +7813,9 @@
eq(true));
reset(getServices().userManagerInternal);
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(restriction),
+ dpm.getUserRestrictions(admin1));
+
dpm.clearUserRestriction(admin1, restriction);
reset(getServices().userManagerInternal);
}
@@ -8056,6 +8062,28 @@
}
@Test
+ public void testGetPermissionGrantState_financeDo_notReadPhoneStatePermission_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.getPermissionGrantState(admin1, admin1.getPackageName(),
+ permission.READ_CALENDAR));
+ }
+
+ @Test
+ public void testGetPermissionGrantState_financeDo_notDeviceOwnerPackage_throwsException()
+ throws Exception {
+ setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
+
+ assertExpectException(SecurityException.class, /* messageRegex= */ null,
+ () -> dpm.getPermissionGrantState(admin1, "com.android.foo.package",
+ permission.READ_PHONE_STATE));
+ }
+
+ @Test
public void testSetUsbDataSignalingEnabled_noDeviceOwnerOrPoOfOrgOwnedDevice() {
assertThrows(SecurityException.class,
() -> dpm.setUsbDataSignalingEnabled(true));
@@ -8334,8 +8362,10 @@
@Test
public void testSetSsidAllowlist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
- final Set<String> ssids = Collections.singleton("ssid1");
- WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
}
@@ -8343,8 +8373,10 @@
public void testSetSsidAllowlist_asDeviceOwner() throws Exception {
setDeviceOwner();
- final Set<String> ssids = Collections.singleton("ssid1");
- WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
dpm.setWifiSsidPolicy(policy);
assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
@@ -8359,8 +8391,12 @@
configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
mContext.binder.callingUid = managedProfileAdminUid;
- final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
- WifiSsidPolicy policy = WifiSsidPolicy.createAllowlistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)),
+ WifiSsid.fromBytes("ssid2".getBytes(StandardCharsets.UTF_8)),
+ WifiSsid.fromBytes("ssid3".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids);
dpm.setWifiSsidPolicy(policy);
assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
@@ -8371,15 +8407,17 @@
public void testSetSsidAllowlist_emptyList() throws Exception {
setDeviceOwner();
- final Set<String> ssids = new ArraySet<>();
+ final Set<WifiSsid> ssids = new ArraySet<>();
assertThrows(IllegalArgumentException.class,
- () -> WifiSsidPolicy.createAllowlistPolicy(ssids));
+ () -> new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_ALLOWLIST, ssids));
}
@Test
public void testSetSsidDenylist_noDeviceOwnerOrPoOfOrgOwnedDevice() {
- final Set<String> ssids = Collections.singleton("ssid1");
- WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
assertThrows(SecurityException.class, () -> dpm.setWifiSsidPolicy(policy));
}
@@ -8387,8 +8425,10 @@
public void testSetSsidDenylist_asDeviceOwner() throws Exception {
setDeviceOwner();
- final Set<String> ssids = Collections.singleton("ssid1");
- WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
dpm.setWifiSsidPolicy(policy);
assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
@@ -8403,8 +8443,12 @@
configureProfileOwnerOfOrgOwnedDevice(admin1, managedProfileUserId);
mContext.binder.callingUid = managedProfileAdminUid;
- final Set<String> ssids = new ArraySet<>(Arrays.asList("ssid1", "ssid2", "ssid3"));
- WifiSsidPolicy policy = WifiSsidPolicy.createDenylistPolicy(ssids);
+ final Set<WifiSsid> ssids = new ArraySet<>(
+ Arrays.asList(WifiSsid.fromBytes("ssid1".getBytes(StandardCharsets.UTF_8)),
+ WifiSsid.fromBytes("ssid2".getBytes(StandardCharsets.UTF_8)),
+ WifiSsid.fromBytes("ssid3".getBytes(StandardCharsets.UTF_8))));
+ WifiSsidPolicy policy = new WifiSsidPolicy(
+ WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids);
dpm.setWifiSsidPolicy(policy);
assertThat(dpm.getWifiSsidPolicy().getSsids()).isEqualTo(ssids);
assertThat(dpm.getWifiSsidPolicy().getPolicyType()).isEqualTo(
@@ -8415,9 +8459,9 @@
public void testSetSsidDenylist_emptyList() throws Exception {
setDeviceOwner();
- final Set<String> ssids = new ArraySet<>();
+ final Set<WifiSsid> ssids = new ArraySet<>();
assertThrows(IllegalArgumentException.class,
- () -> WifiSsidPolicy.createDenylistPolicy(ssids));
+ () -> new WifiSsidPolicy(WifiSsidPolicy.WIFI_SSID_POLICY_TYPE_DENYLIST, ssids));
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
index 4caa85c..f5a5689 100644
--- a/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/display/AutomaticBrightnessControllerTest.java
@@ -81,6 +81,7 @@
@Mock HysteresisLevels mScreenBrightnessThresholds;
@Mock Handler mNoOpHandler;
@Mock HighBrightnessModeController mHbmController;
+ @Mock BrightnessThrottler mBrightnessThrottler;
@Before
public void setUp() {
@@ -128,12 +129,15 @@
INITIAL_LIGHT_SENSOR_RATE, BRIGHTENING_LIGHT_DEBOUNCE_CONFIG,
DARKENING_LIGHT_DEBOUNCE_CONFIG, RESET_AMBIENT_LUX_AFTER_WARMUP_CONFIG,
mAmbientBrightnessThresholds, mScreenBrightnessThresholds,
- mContext, mHbmController, mIdleBrightnessMappingStrategy,
+ mContext, mHbmController, mBrightnessThrottler, mIdleBrightnessMappingStrategy,
AMBIENT_LIGHT_HORIZON_SHORT, AMBIENT_LIGHT_HORIZON_LONG
);
when(mHbmController.getCurrentBrightnessMax()).thenReturn(BRIGHTNESS_MAX_FLOAT);
when(mHbmController.getCurrentBrightnessMin()).thenReturn(BRIGHTNESS_MIN_FLOAT);
+ // Disable brightness throttling by default. Individual tests can enable it as needed.
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mBrightnessThrottler.isThrottled()).thenReturn(false);
// Configure the brightness controller and grab an instance of the sensor listener,
// through which we can deliver fake (for test) sensor values.
@@ -420,4 +424,47 @@
assertEquals(600f, hysteresisLevels.getBrighteningThreshold(500f), EPSILON);
assertEquals(250f, hysteresisLevels.getDarkeningThreshold(500f), EPSILON);
}
+
+ @Test
+ public void testBrightnessGetsThrottled() throws Exception {
+ Sensor lightSensor = TestUtils.createSensor(Sensor.TYPE_LIGHT, "Light Sensor");
+ mController = setupController(lightSensor);
+
+ ArgumentCaptor<SensorEventListener> listenerCaptor =
+ ArgumentCaptor.forClass(SensorEventListener.class);
+ verify(mSensorManager).registerListener(listenerCaptor.capture(), eq(lightSensor),
+ eq(INITIAL_LIGHT_SENSOR_RATE * 1000), any(Handler.class));
+ SensorEventListener listener = listenerCaptor.getValue();
+
+ // Set up system to return max brightness at 100 lux
+ final float normalizedBrightness = BRIGHTNESS_MAX_FLOAT;
+ final float lux = 100.0f;
+ when(mAmbientBrightnessThresholds.getBrighteningThreshold(lux))
+ .thenReturn(lux);
+ when(mAmbientBrightnessThresholds.getDarkeningThreshold(lux))
+ .thenReturn(lux);
+ when(mBrightnessMappingStrategy.getBrightness(eq(lux), eq(null), anyInt()))
+ .thenReturn(normalizedBrightness);
+
+ // Sensor reads 100 lux. We should get max brightness.
+ listener.onSensorChanged(TestUtils.createSensorEvent(lightSensor, (int) lux));
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+
+ // Apply throttling and notify ABC (simulates DisplayPowerController#updatePowerState())
+ final float throttledBrightness = 0.123f;
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(throttledBrightness);
+ when(mBrightnessThrottler.isThrottled()).thenReturn(true);
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
+ BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
+ 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+ assertEquals(throttledBrightness, mController.getAutomaticScreenBrightness(), 0.0f);
+
+ // Remove throttling and notify ABC again
+ when(mBrightnessThrottler.getBrightnessCap()).thenReturn(BRIGHTNESS_MAX_FLOAT);
+ when(mBrightnessThrottler.isThrottled()).thenReturn(false);
+ mController.configure(AUTO_BRIGHTNESS_ENABLED, null /* configuration */,
+ BRIGHTNESS_MAX_FLOAT /* brightness */, false /* userChangedBrightness */,
+ 0 /* adjustment */, false /* userChanged */, DisplayPowerRequest.POLICY_BRIGHT);
+ assertEquals(BRIGHTNESS_MAX_FLOAT, mController.getAutomaticScreenBrightness(), 0.0f);
+ }
}
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/FakeHdmiCecConfig.java b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
index d441143..0028969 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/FakeHdmiCecConfig.java
@@ -165,6 +165,17 @@
R.bool.config_cecTvSendStandbyOnSleepDisabled_default);
doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSetMenuLanguage_userConfigurable);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSetMenuLanguageEnabled_allowed);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSetMenuLanguageEnabled_default);
+ doReturn(true).when(resources).getBoolean(
+ R.bool.config_cecSetMenuLanguageDisabled_allowed);
+ doReturn(false).when(resources).getBoolean(
+ R.bool.config_cecSetMenuLanguageDisabled_default);
+
+ doReturn(true).when(resources).getBoolean(
R.bool.config_cecRcProfileTv_userConfigurable);
doReturn(true).when(resources).getBoolean(
R.bool.config_cecRcProfileTvNone_allowed);
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 85d30a6..8e756ae 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecConfigTest.java
@@ -83,6 +83,7 @@
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -121,6 +122,7 @@
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -159,6 +161,7 @@
HdmiControlManager.CEC_SETTING_NAME_VOLUME_CONTROL_MODE,
HdmiControlManager.CEC_SETTING_NAME_TV_WAKE_ON_ONE_TOUCH_PLAY,
HdmiControlManager.CEC_SETTING_NAME_TV_SEND_STANDBY_ON_SLEEP,
+ HdmiControlManager.CEC_SETTING_NAME_SET_MENU_LANGUAGE,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_TV,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_ROOT_MENU,
HdmiControlManager.CEC_SETTING_NAME_RC_PROFILE_SOURCE_HANDLES_SETUP_MENU,
@@ -317,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/hdmi/HdmiCecLocalDeviceTvTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
index b6c4bc2..a9812ab 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiCecLocalDeviceTvTest.java
@@ -62,6 +62,23 @@
public class HdmiCecLocalDeviceTvTest {
private static final int TIMEOUT_MS = HdmiConfig.TIMEOUT_MS + 1;
+ private static final String[] SADS_NOT_TO_QUERY = new String[]{
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MPEG1,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_AAC,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTS,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ATRAC,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_ONEBITAUDIO,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DDP,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DTSHD,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_TRUEHD,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_DST,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_WMAPRO,
+ HdmiControlManager.CEC_SETTING_NAME_QUERY_SAD_MAX};
+ private static final HdmiCecMessage SAD_QUERY =
+ HdmiCecMessageBuilder.buildRequestShortAudioDescriptor(ADDR_TV, ADDR_AUDIO_SYSTEM,
+ new int[]{Constants.AUDIO_CODEC_LPCM, Constants.AUDIO_CODEC_DD,
+ Constants.AUDIO_CODEC_MP3, Constants.AUDIO_CODEC_MPEG2});
+
private HdmiControlService mHdmiControlService;
private HdmiCecController mHdmiCecController;
private HdmiCecLocalDeviceTv mHdmiCecLocalDeviceTv;
@@ -136,6 +153,10 @@
mNativeWrapper.setPhysicalAddress(mTvPhysicalAddress);
mTestLooper.dispatchAll();
mTvLogicalAddress = mHdmiCecLocalDeviceTv.getDeviceInfo().getLogicalAddress();
+ for (String sad : SADS_NOT_TO_QUERY) {
+ mHdmiControlService.getHdmiCecConfig().setIntValue(
+ sad, HdmiControlManager.QUERY_SAD_DISABLED);
+ }
mNativeWrapper.clearResultMessages();
}
@@ -442,6 +463,7 @@
ADDR_TV,
ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY);
}
@Test
@@ -463,6 +485,7 @@
ADDR_TV,
ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).doesNotContain(reportArcInitiated);
+ assertThat(mNativeWrapper.getResultMessages()).doesNotContain(SAD_QUERY);
}
@Test
@@ -485,6 +508,7 @@
ADDR_TV,
ADDR_AUDIO_SYSTEM);
assertThat(mNativeWrapper.getResultMessages()).contains(reportArcInitiated);
+ assertThat(mNativeWrapper.getResultMessages()).contains(SAD_QUERY);
}
@Test
diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
index 7751ef5..c48a974 100644
--- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java
@@ -42,6 +42,7 @@
import android.hardware.hdmi.HdmiPortInfo;
import android.hardware.hdmi.IHdmiCecVolumeControlFeatureListener;
import android.hardware.hdmi.IHdmiControlStatusChangeListener;
+import android.hardware.hdmi.IHdmiVendorCommandListener;
import android.os.Binder;
import android.os.Looper;
import android.os.RemoteException;
@@ -747,6 +748,114 @@
}
@Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdNoIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandNoId =
+ HdmiCecMessageBuilder.buildVendorCommand(sourceAddress, destAddress, params);
+ mNativeWrapper.onCecMessage(vendorCommandNoId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isFalse();
+ }
+
+ @Test
+ public void addVendorCommandListener_receiveCallback_VendorCmdWithIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, vendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isTrue();
+ assertThat(vendorCmdListener.mParamsCorrect).isTrue();
+ assertThat(vendorCmdListener.mHasVendorId).isTrue();
+ }
+
+ @Test
+ public void addVendorCommandListener_noCallback_VendorCmdDiffIdTest() {
+ int destAddress = mHdmiControlServiceSpy.playback().getDeviceInfo().getLogicalAddress();
+ int sourceAddress = Constants.ADDR_TV;
+ byte[] params = {0x00, 0x01, 0x02, 0x03};
+ int vendorId = 0x123456;
+ int diffVendorId = 0x345678;
+
+ VendorCommandListener vendorCmdListener =
+ new VendorCommandListener(sourceAddress, destAddress, params, vendorId);
+ mHdmiControlServiceSpy.addVendorCommandListener(vendorCmdListener, vendorId);
+ mTestLooper.dispatchAll();
+
+ HdmiCecMessage vendorCommandWithDiffId =
+ HdmiCecMessageBuilder.buildVendorCommandWithId(
+ sourceAddress, destAddress, diffVendorId, params);
+ mNativeWrapper.onCecMessage(vendorCommandWithDiffId);
+ mTestLooper.dispatchAll();
+ assertThat(vendorCmdListener.mVendorCommandCallbackReceived).isFalse();
+ }
+
+ private static class VendorCommandListener extends IHdmiVendorCommandListener.Stub {
+ boolean mVendorCommandCallbackReceived = false;
+ boolean mParamsCorrect = false;
+ boolean mHasVendorId = false;
+
+ int mSourceAddress;
+ int mDestAddress;
+ byte[] mParams;
+ int mVendorId;
+
+ VendorCommandListener(int sourceAddress, int destAddress, byte[] params, int vendorId) {
+ this.mSourceAddress = sourceAddress;
+ this.mDestAddress = destAddress;
+ this.mParams = params.clone();
+ this.mVendorId = vendorId;
+ }
+
+ @Override
+ public void onReceived(
+ int sourceAddress, int destAddress, byte[] params, boolean hasVendorId) {
+ mVendorCommandCallbackReceived = true;
+ if (mSourceAddress == sourceAddress && mDestAddress == destAddress) {
+ byte[] expectedParams;
+ if (hasVendorId) {
+ // If the command has vendor ID, we have to add it to mParams.
+ expectedParams = new byte[params.length];
+ expectedParams[0] = (byte) ((mVendorId >> 16) & 0xFF);
+ expectedParams[1] = (byte) ((mVendorId >> 8) & 0xFF);
+ expectedParams[2] = (byte) (mVendorId & 0xFF);
+ System.arraycopy(mParams, 0, expectedParams, 3, mParams.length);
+ } else {
+ expectedParams = params.clone();
+ }
+ if (Arrays.equals(expectedParams, params)) {
+ mParamsCorrect = true;
+ }
+ }
+ mHasVendorId = hasVendorId;
+ }
+
+ @Override
+ public void onControlStateChanged(boolean enabled, int reason) {}
+ }
+
+ @Test
public void dispatchMessageToLocalDevice_broadcastMessage_returnsHandled() {
HdmiCecMessage message = HdmiCecMessageBuilder.buildStandby(
Constants.ADDR_TV,
diff --git a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
index 94cf20f..a7b045f 100644
--- a/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/net/NetworkPolicyManagerServiceTest.java
@@ -2237,7 +2237,7 @@
private void setSubscriptionPlans(int subId, SubscriptionPlan[] plans, String callingPackage)
throws InterruptedException {
- mService.setSubscriptionPlans(subId, plans, callingPackage);
+ mService.setSubscriptionPlans(subId, plans, 0, callingPackage);
// setSubscriptionPlans() triggers async events, wait for those to be completed before
// moving forward as they could interfere with the tests later.
postMsgAndWaitForCompletion();
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/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
index ac83642..ed4dee1 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageManagerSettingsTests.java
@@ -43,7 +43,6 @@
import android.content.pm.PackageManager;
import android.content.pm.SuspendDialogInfo;
import android.content.pm.UserInfo;
-import android.content.pm.parsing.FrameworkParsingPackageUtils;
import android.os.BaseBundle;
import android.os.PersistableBundle;
import android.os.Process;
@@ -87,7 +86,6 @@
import java.io.IOException;
import java.security.PublicKey;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
@@ -671,6 +669,23 @@
null /*usesStaticLibrariesVersions*/,
null /*mimeGroups*/,
UUID.randomUUID());
+ origPkgSetting01.setUserState(0, 100, 1, true, false, false, false, 0, null, false,
+ false, "lastDisabledCaller", new ArraySet<>(new String[]{"enabledComponent1"}),
+ new ArraySet<>(new String[]{"disabledComponent1"}), 0, 0, "harmfulAppWarning",
+ "splashScreenTheme", 1000L);
+ final PersistableBundle appExtras1 = createPersistableBundle(
+ PACKAGE_NAME_1, 1L, 0.01, true, "appString1");
+ final PersistableBundle launcherExtras1 = createPersistableBundle(
+ PACKAGE_NAME_1, 10L, 0.1, false, "launcherString1");
+ final SuspendDialogInfo dialogInfo1 = new SuspendDialogInfo.Builder()
+ .setIcon(0x11220001)
+ .setTitle("String Title")
+ .setMessage("1st message")
+ .setNeutralButtonText(0x11220003)
+ .setNeutralButtonAction(BUTTON_ACTION_MORE_DETAILS)
+ .build();
+ origPkgSetting01.modifyUserState(0).putSuspendParams("suspendingPackage1",
+ SuspendParams.getInstanceOrNull(dialogInfo1, appExtras1, launcherExtras1));
final PackageSetting testPkgSetting01 = new PackageSetting(
PACKAGE_NAME /*pkgName*/,
REAL_PACKAGE_NAME /*realPkgName*/,
@@ -691,6 +706,8 @@
UUID.randomUUID());
testPkgSetting01.copyPackageSetting(origPkgSetting01);
verifySettingCopy(origPkgSetting01, testPkgSetting01);
+ verifyUserStatesCopy(origPkgSetting01.readUserState(0),
+ testPkgSetting01.readUserState(0));
}
/** Update package */
@@ -724,8 +741,7 @@
assertThat(testPkgSetting01.getFlags(), is(0));
assertThat(testPkgSetting01.getPrivateFlags(), is(0));
final PackageUserState userState = testPkgSetting01.readUserState(0);
- final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
- verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
+ verifyUserState(userState, false /*notLaunched*/,
false /*stopped*/, false /*installed*/);
}
@@ -760,11 +776,7 @@
assertThat(testPkgSetting01.getFlags(), is(ApplicationInfo.FLAG_SYSTEM));
assertThat(testPkgSetting01.getPrivateFlags(), is(ApplicationInfo.PRIVATE_FLAG_PRIVILEGED));
final PackageUserState userState = testPkgSetting01.readUserState(0);
- final PackageUserState oldUserState = oldPkgSetting01.readUserState(0);
- // WARNING: When creating a shallow copy of the PackageSetting we do NOT create
- // new contained objects. For example, this means that changes to the user state
- // in testPkgSetting01 will also change the user state in its copy.
- verifyUserState(userState, oldUserState, false /*userStateChanged*/, false /*notLaunched*/,
+ verifyUserState(userState, false /*notLaunched*/,
false /*stopped*/, true /*installed*/);
}
@@ -839,8 +851,7 @@
assertNotSame(testPkgSetting01.getSignatures(), originalSignatures);
assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
- verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
- false /*notLaunched*/, false /*stopped*/, true /*installed*/);
+ verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
/** Create a new non-system PackageSetting */
@@ -880,8 +891,7 @@
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
// by default, the package is considered stopped
final PackageUserState userState = testPkgSetting01.readUserState(0);
- verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
- true /*notLaunched*/, true /*stopped*/, true /*installed*/);
+ verifyUserState(userState, true /*notLaunched*/, true /*stopped*/, true /*installed*/);
}
/** Create PackageSetting for a shared user */
@@ -923,8 +933,7 @@
assertThat(testPkgSetting01.getSecondaryCpuAbi(), is("x86"));
assertThat(testPkgSetting01.getVersionCode(), is(INITIAL_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
- verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
- false /*notLaunched*/, false /*stopped*/, true /*installed*/);
+ verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
/** Create a new PackageSetting based on a disabled package setting */
@@ -968,8 +977,7 @@
assertNotSame(testPkgSetting01.getSignatures(), disabledSignatures);
assertThat(testPkgSetting01.getVersionCode(), is(UPDATED_VERSION_CODE));
final PackageUserState userState = testPkgSetting01.readUserState(0);
- verifyUserState(userState, null /*oldUserState*/, false /*userStateChanged*/,
- false /*notLaunched*/, false /*stopped*/, true /*installed*/);
+ verifyUserState(userState, false /*notLaunched*/, false /*stopped*/, true /*installed*/);
}
@Test
@@ -1080,29 +1088,8 @@
assertThat(countDownLatch.getCount(), is(0L));
}
- private <T> void assertArrayEquals(T[] a, T[] b) {
- assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
- Arrays.equals(a, b));
- }
-
- private void assertArrayEquals(int[] a, int[] b) {
- assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
- Arrays.equals(a, b));
- }
-
- private void assertArrayEquals(long[] a, long[] b) {
- assertTrue("Expected: " + Arrays.toString(a) + ", actual: " + Arrays.toString(b),
- Arrays.equals(a, b));
- }
-
- private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
- boolean userStateChanged) {
- verifyUserState(userState, oldUserState, userStateChanged, false /*notLaunched*/,
- false /*stopped*/, true /*installed*/);
- }
-
- private void verifyUserState(PackageUserState userState, PackageUserState oldUserState,
- boolean userStateChanged, boolean notLaunched, boolean stopped, boolean installed) {
+ private void verifyUserState(PackageUserState userState,
+ boolean notLaunched, boolean stopped, boolean installed) {
assertThat(userState.getEnabledState(), is(0));
assertThat(userState.isHidden(), is(false));
assertThat(userState.isInstalled(), is(installed));
@@ -1110,9 +1097,6 @@
assertThat(userState.isStopped(), is(stopped));
assertThat(userState.isSuspended(), is(false));
assertThat(userState.getDistractionFlags(), is(0));
- if (oldUserState != null) {
- assertThat(userState.equals(oldUserState), is(not(userStateChanged)));
- }
}
private void verifyKeySetData(PackageKeySetData originalData, PackageKeySetData testData) {
@@ -1177,6 +1161,57 @@
assertThat(origPkgSetting.getVolumeUuid(), is(testPkgSetting.getVolumeUuid()));
}
+ private void verifyUserStatesCopy(PackageUserStateInternal origPus,
+ PackageUserStateInternal testPus) {
+ assertThat(userStateEquals(origPus, testPus), is(true));
+ // Verify suspendParams are copied over
+ assertThat(origPus.getSuspendParams(), is(notNullValue()));
+ assertThat(testPus.getSuspendParams(), is(notNullValue()));
+ SuspendParams origSuspendParams = origPus.getSuspendParams().valueAt(0);
+ SuspendParams testSuspendParams = testPus.getSuspendParams().valueAt(0);
+ assertThat(origSuspendParams.getDialogInfo().equals(testSuspendParams.getDialogInfo()),
+ is(true));
+ assertThat(BaseBundle.kindofEquals(
+ origSuspendParams.getAppExtras(), testSuspendParams.getAppExtras()), is(true));
+ assertThat(BaseBundle.kindofEquals(origSuspendParams.getLauncherExtras(),
+ testSuspendParams.getLauncherExtras()), is(true));
+ // Verify that disabledComponents and enabledComponents are copied
+ assertThat(origPus.getDisabledComponents(), is(notNullValue()));
+ assertThat(origPus.getDisabledComponents().equals(testPus.getDisabledComponents()),
+ is(true));
+ assertThat(origPus.getEnabledComponents(), is(notNullValue()));
+ assertThat(origPus.getEnabledComponents().equals(testPus.getEnabledComponents()),
+ is(true));
+ }
+
+ private boolean userStateEquals(PackageUserState userState, PackageUserState oldUserState) {
+ return userState.isHidden() == oldUserState.isHidden()
+ && userState.isStopped() == oldUserState.isStopped()
+ && userState.isInstalled() == oldUserState.isInstalled()
+ && userState.isSuspended() == oldUserState.isSuspended()
+ && userState.isNotLaunched() == oldUserState.isNotLaunched()
+ && userState.isInstantApp() == oldUserState.isInstantApp()
+ && userState.isVirtualPreload() == oldUserState.isVirtualPreload()
+ && (userState.getAllOverlayPaths() != null
+ ? userState.getAllOverlayPaths().equals(oldUserState.getAllOverlayPaths())
+ : oldUserState.getOverlayPaths() == null)
+ && userState.getCeDataInode() == oldUserState.getCeDataInode()
+ && userState.getDistractionFlags() == oldUserState.getDistractionFlags()
+ && userState.getFirstInstallTime() == oldUserState.getFirstInstallTime()
+ && userState.getEnabledState() == oldUserState.getEnabledState()
+ && userState.getHarmfulAppWarning().equals(oldUserState.getHarmfulAppWarning())
+ && userState.getInstallReason() == oldUserState.getInstallReason()
+ && userState.getLastDisableAppCaller().equals(
+ oldUserState.getLastDisableAppCaller())
+ && (userState.getSharedLibraryOverlayPaths() != null
+ ? userState.getSharedLibraryOverlayPaths().equals(
+ oldUserState.getSharedLibraryOverlayPaths())
+ : oldUserState.getSharedLibraryOverlayPaths() == null)
+ && userState.getSplashScreenTheme().equals(
+ oldUserState.getSplashScreenTheme())
+ && userState.getUninstallReason() == oldUserState.getUninstallReason();
+ }
+
private SharedUserSetting createSharedUserSetting(Settings settings, String userName,
int sharedUserId, int pkgFlags, int pkgPrivateFlags) {
return settings.addSharedUserLPw(
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
index 6c72369..a0edb85 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
@@ -236,7 +236,7 @@
testUserState1.setSuspendParams(paramsMap1);
PackageUserStateImpl testUserState2 =
- new PackageUserStateImpl(testUserState1);
+ new PackageUserStateImpl(null, testUserState1);
assertThat(testUserState1.equals(testUserState2), is(true));
testUserState2.setSuspendParams(paramsMap2);
// Should not be equal since suspendParams maps are different
@@ -250,12 +250,12 @@
userState1.setDistractionFlags(PackageManager.RESTRICTION_HIDE_FROM_SUGGESTIONS);
final PackageUserStateImpl copyOfUserState1 =
- new PackageUserStateImpl(userState1);
+ new PackageUserStateImpl(null, userState1);
assertThat(userState1.getDistractionFlags(), is(copyOfUserState1.getDistractionFlags()));
assertThat(userState1.equals(copyOfUserState1), is(true));
final PackageUserStateImpl userState2 =
- new PackageUserStateImpl(userState1);
+ new PackageUserStateImpl(null, userState1);
userState2.setDistractionFlags(PackageManager.RESTRICTION_HIDE_NOTIFICATIONS);
assertThat(userState1.equals(userState2), is(false));
}
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..fb3a626 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/tests/wmtests/src/com/android/server/wm/TransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
index fd523f0..d8c653ce 100644
--- a/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/TransitionTests.java
@@ -49,7 +49,6 @@
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.SurfaceControl;
-import android.view.TransactionCommittedListener;
import android.window.IDisplayAreaOrganizer;
import android.window.ITaskOrganizer;
import android.window.ITransitionPlayer;
@@ -533,8 +532,8 @@
assertTrue(asyncRotationController.isTargetToken(statusBar.mToken));
final SurfaceControl.Transaction startTransaction = mock(SurfaceControl.Transaction.class);
- final ArgumentCaptor<TransactionCommittedListener> listenerCaptor =
- ArgumentCaptor.forClass(TransactionCommittedListener.class);
+ final ArgumentCaptor<SurfaceControl.TransactionCommittedListener> listenerCaptor =
+ ArgumentCaptor.forClass(SurfaceControl.TransactionCommittedListener.class);
player.onTransactionReady(startTransaction);
verify(startTransaction).addTransactionCommittedListener(any(), listenerCaptor.capture());
@@ -739,6 +738,9 @@
}
from = from.getParent();
}
+ if (end.asDisplayArea() != null) {
+ end.asDisplayArea().mOrganizer = organizer;
+ }
}
/** Fill the change map with all the parents of top. Change maps are usually fully populated */
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
index c56b614..746f2b5 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowProcessControllerTests.java
@@ -21,6 +21,7 @@
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
+import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.ActivityRecord.State.PAUSED;
@@ -374,12 +375,22 @@
final ActivityRecord activity = createActivityRecord(mWpc);
activity.mVisibleRequested = true;
doReturn(true).when(activity).applyAppSpecificConfig(anyInt(), any());
- mWpc.updateAppSpecificSettingsForAllActivities(Configuration.UI_MODE_NIGHT_YES,
- LocaleList.forLanguageTags("en-XA"));
+ mWpc.updateAppSpecificSettingsForAllActivitiesInPackage(DEFAULT_COMPONENT_PACKAGE_NAME,
+ Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"));
verify(activity).ensureActivityConfiguration(anyInt(), anyBoolean());
}
@Test
+ public void testTopActivityUiModeChangeForDifferentPackage_noScheduledConfigChange() {
+ final ActivityRecord activity = createActivityRecord(mWpc);
+ activity.mVisibleRequested = true;
+ mWpc.updateAppSpecificSettingsForAllActivitiesInPackage("com.different.package",
+ Configuration.UI_MODE_NIGHT_YES, LocaleList.forLanguageTags("en-XA"));
+ verify(activity, never()).applyAppSpecificConfig(anyInt(), any());
+ verify(activity, never()).ensureActivityConfiguration(anyInt(), anyBoolean());
+ }
+
+ @Test
public void testTopActivityDisplayAreaMatchesTopMostActivity_noActivities() {
assertNull(mWpc.getTopActivityDisplayArea());
}
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/services/usb/java/com/android/server/usb/UsbAlsaDevice.java b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
index 85b1de5..337e1f9 100644
--- a/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
+++ b/services/usb/java/com/android/server/usb/UsbAlsaDevice.java
@@ -17,6 +17,7 @@
package com.android.server.usb;
import android.annotation.NonNull;
+import android.media.AudioDeviceAttributes;
import android.media.AudioSystem;
import android.media.IAudioService;
import android.os.RemoteException;
@@ -213,24 +214,25 @@
int outputState = (enable && connected) ? 1 : 0;
if (outputState != mOutputState) {
mOutputState = outputState;
- mAudioService.setWiredDeviceConnectionState(device, outputState,
- alsaCardDeviceString,
- mDeviceName, TAG);
+ AudioDeviceAttributes attributes = new AudioDeviceAttributes(device,
+ alsaCardDeviceString, mDeviceName);
+ mAudioService.setWiredDeviceConnectionState(attributes, outputState, TAG);
}
}
// Input Device
if (mHasInput) {
- int device = mIsInputHeadset ? AudioSystem.DEVICE_IN_USB_HEADSET
+ int device = mIsInputHeadset
+ ? AudioSystem.DEVICE_IN_USB_HEADSET
: AudioSystem.DEVICE_IN_USB_DEVICE;
boolean connected = isInputJackConnected();
Slog.i(TAG, "INPUT JACK connected: " + connected);
int inputState = (enable && connected) ? 1 : 0;
if (inputState != mInputState) {
mInputState = inputState;
- mAudioService.setWiredDeviceConnectionState(
- device, inputState, alsaCardDeviceString,
- mDeviceName, TAG);
+ AudioDeviceAttributes attributes = new AudioDeviceAttributes(device,
+ alsaCardDeviceString, mDeviceName);
+ mAudioService.setWiredDeviceConnectionState(attributes, inputState, TAG);
}
}
} catch (RemoteException e) {
diff --git a/services/wallpapereffectsgeneration/Android.bp b/services/wallpapereffectsgeneration/Android.bp
new file mode 100644
index 0000000..4dbb0fd
--- /dev/null
+++ b/services/wallpapereffectsgeneration/Android.bp
@@ -0,0 +1,22 @@
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "frameworks_base_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["frameworks_base_license"],
+}
+
+filegroup {
+ name: "services.wallpapereffectsgeneration-sources",
+ srcs: ["java/**/*.java"],
+ path: "java",
+ visibility: ["//frameworks/base/services"],
+}
+
+java_library_static {
+ name: "services.wallpapereffectsgeneration",
+ defaults: ["platform_service_defaults"],
+ srcs: [":services.wallpapereffectsgeneration-sources"],
+ libs: ["services.core"],
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java
new file mode 100644
index 0000000..c228daf
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/RemoteWallpaperEffectsGenerationService.java
@@ -0,0 +1,113 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.content.ComponentName;
+import android.content.Context;
+import android.os.IBinder;
+import android.service.wallpapereffectsgeneration.IWallpaperEffectsGenerationService;
+import android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService;
+import android.text.format.DateUtils;
+
+import com.android.internal.infra.AbstractMultiplePendingRequestsRemoteService;
+
+
+/**
+ * Proxy to the
+ * {@link android.service.wallpapereffectsgeneration.WallpaperEffectsGenerationService}
+ * implementation in another process.
+ */
+public class RemoteWallpaperEffectsGenerationService extends
+ AbstractMultiplePendingRequestsRemoteService<RemoteWallpaperEffectsGenerationService,
+ IWallpaperEffectsGenerationService> {
+
+ private static final String TAG =
+ RemoteWallpaperEffectsGenerationService.class.getSimpleName();
+
+ private static final long TIMEOUT_REMOTE_REQUEST_MILLIS = 2 * DateUtils.SECOND_IN_MILLIS;
+
+ private final RemoteWallpaperEffectsGenerationServiceCallback mCallback;
+
+ public RemoteWallpaperEffectsGenerationService(Context context,
+ ComponentName componentName, int userId,
+ RemoteWallpaperEffectsGenerationServiceCallback callback,
+ boolean bindInstantServiceAllowed,
+ boolean verbose) {
+ super(context, WallpaperEffectsGenerationService.SERVICE_INTERFACE,
+ componentName, userId, callback,
+ context.getMainThreadHandler(),
+ bindInstantServiceAllowed ? Context.BIND_ALLOW_INSTANT : 0,
+ verbose, /* initialCapacity= */ 1);
+ mCallback = callback;
+ }
+
+ @Override
+ protected IWallpaperEffectsGenerationService getServiceInterface(IBinder service) {
+ return IWallpaperEffectsGenerationService.Stub.asInterface(service);
+ }
+
+ @Override
+ protected long getTimeoutIdleBindMillis() {
+ return PERMANENT_BOUND_TIMEOUT_MS;
+ }
+
+ @Override
+ protected long getRemoteRequestMillis() {
+ return TIMEOUT_REMOTE_REQUEST_MILLIS;
+ }
+
+ /**
+ * Schedules a request to bind to the remote service.
+ */
+ public void reconnect() {
+ super.scheduleBind();
+ }
+
+ /**
+ * Schedule async request on remote service.
+ */
+ public void scheduleOnResolvedService(
+ @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
+ scheduleAsyncRequest(request);
+ }
+
+ /**
+ * Execute async request on remote service immediately instead of sending it to Handler queue.
+ */
+ public void executeOnResolvedService(
+ @NonNull AsyncRequest<IWallpaperEffectsGenerationService> request) {
+ executeAsyncRequest(request);
+ }
+
+ /**
+ * Notifies server (WallpaperEffectsGenerationPerUserService) about unexpected events..
+ */
+ public interface RemoteWallpaperEffectsGenerationServiceCallback
+ extends VultureCallback<RemoteWallpaperEffectsGenerationService> {
+ /**
+ * Notifies change in connected state of the remote service.
+ */
+ void onConnectedStateChanged(boolean connected);
+ }
+
+ @Override // from AbstractRemoteService
+ protected void handleOnConnectedStateChanged(boolean connected) {
+ if (mCallback != null) {
+ mCallback.onConnectedStateChanged(connected);
+ }
+ }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java
new file mode 100644
index 0000000..0d0b3e0
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerService.java
@@ -0,0 +1,185 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import static android.Manifest.permission.MANAGE_WALLPAPER_EFFECTS_GENERATION;
+import static android.app.ActivityManagerInternal.ALLOW_NON_FULL;
+import static android.content.Context.WALLPAPER_EFFECTS_GENERATION_SERVICE;
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManagerInternal;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+import android.app.wallpapereffectsgeneration.IWallpaperEffectsGenerationManager;
+import android.content.Context;
+import android.os.Binder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.Slog;
+
+import com.android.server.LocalServices;
+import com.android.server.infra.AbstractMasterSystemService;
+import com.android.server.infra.FrameworkResourcesServiceNameResolver;
+import com.android.server.wm.ActivityTaskManagerInternal;
+
+import java.io.FileDescriptor;
+import java.util.function.Consumer;
+
+/**
+ * A service used to return wallpaper effect given a request.
+ */
+public class WallpaperEffectsGenerationManagerService extends
+ AbstractMasterSystemService<WallpaperEffectsGenerationManagerService,
+ WallpaperEffectsGenerationPerUserService> {
+ private static final String TAG =
+ WallpaperEffectsGenerationManagerService.class.getSimpleName();
+ private static final boolean DEBUG = false;
+ private static final int MAX_TEMP_SERVICE_DURATION_MS = 1_000 * 60 * 2; // 2 minutes
+ private final ActivityTaskManagerInternal mActivityTaskManagerInternal;
+
+ public WallpaperEffectsGenerationManagerService(Context context) {
+ super(context,
+ new FrameworkResourcesServiceNameResolver(context,
+ com.android.internal.R.string.config_defaultWallpaperEffectsGenerationService),
+ null,
+ PACKAGE_UPDATE_POLICY_NO_REFRESH | PACKAGE_RESTART_POLICY_NO_REFRESH);
+ mActivityTaskManagerInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
+ }
+
+ @Override
+ protected WallpaperEffectsGenerationPerUserService newServiceLocked(int resolvedUserId,
+ boolean disabled) {
+ return new WallpaperEffectsGenerationPerUserService(this, mLock, resolvedUserId);
+ }
+
+ @Override
+ public void onStart() {
+ publishBinderService(WALLPAPER_EFFECTS_GENERATION_SERVICE,
+ new WallpaperEffectsGenerationManagerStub());
+ }
+
+ @Override
+ protected void enforceCallingPermissionForManagement() {
+ getContext().enforceCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION, TAG);
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageUpdatedLocked(@UserIdInt int userId) {
+ final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageUpdatedLocked();
+ }
+ }
+
+ @Override // from AbstractMasterSystemService
+ protected void onServicePackageRestartedLocked(@UserIdInt int userId) {
+ final WallpaperEffectsGenerationPerUserService service = peekServiceForUserLocked(userId);
+ if (service != null) {
+ service.onPackageRestartedLocked();
+ }
+ }
+
+ @Override
+ protected int getMaximumTemporaryServiceDurationMs() {
+ return MAX_TEMP_SERVICE_DURATION_MS;
+ }
+
+ private class WallpaperEffectsGenerationManagerStub
+ extends IWallpaperEffectsGenerationManager.Stub {
+ @Override
+ public void generateCinematicEffect(@NonNull CinematicEffectRequest request,
+ @NonNull ICinematicEffectListener listener) {
+ if (!runForUserLocked("generateCinematicEffect", (service) ->
+ service.onGenerateCinematicEffectLocked(request, listener))) {
+ try {
+ listener.onCinematicEffectGenerated(
+ new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
+ request.getTaskId()).build());
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.d(TAG, "fail to invoke cinematic effect listener for task["
+ + request.getTaskId() + "]");
+ }
+ }
+ }
+ }
+
+ @Override
+ public void returnCinematicEffectResponse(@NonNull CinematicEffectResponse response) {
+ runForUserLocked("returnCinematicResponse", (service) ->
+ service.onReturnCinematicEffectResponseLocked(response));
+ }
+
+ public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+ @Nullable FileDescriptor err,
+ @NonNull String[] args, @Nullable ShellCallback callback,
+ @NonNull ResultReceiver resultReceiver) {
+ new WallpaperEffectsGenerationManagerServiceShellCommand(
+ WallpaperEffectsGenerationManagerService.this)
+ .exec(this, in, out, err, args, callback, resultReceiver);
+ }
+
+ /**
+ * Execute the operation for the user locked. Return true if
+ * WallpaperEffectsGenerationPerUserService is found for the user.
+ * Otherwise return false.
+ */
+ private boolean runForUserLocked(@NonNull final String func,
+ @NonNull final Consumer<WallpaperEffectsGenerationPerUserService> c) {
+ ActivityManagerInternal am = LocalServices.getService(ActivityManagerInternal.class);
+ final int userId = am.handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
+ Binder.getCallingUserHandle().getIdentifier(), false, ALLOW_NON_FULL,
+ null, null);
+ if (DEBUG) {
+ Slog.d(TAG, "runForUserLocked:" + func + " from pid=" + Binder.getCallingPid()
+ + ", uid=" + Binder.getCallingUid());
+ }
+ Context ctx = getContext();
+ if (!(ctx.checkCallingPermission(MANAGE_WALLPAPER_EFFECTS_GENERATION)
+ == PERMISSION_GRANTED
+ || mServiceNameResolver.isTemporary(userId)
+ || mActivityTaskManagerInternal.isCallerRecents(Binder.getCallingUid()))) {
+ String msg = "Permission Denial: Cannot call " + func + " from pid="
+ + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid();
+ Slog.w(TAG, msg);
+ throw new SecurityException(msg);
+ }
+ final long origId = Binder.clearCallingIdentity();
+ boolean accepted = false;
+ try {
+ synchronized (mLock) {
+ final WallpaperEffectsGenerationPerUserService service =
+ getServiceForUserLocked(userId);
+ if (service != null) {
+ accepted = true;
+ c.accept(service);
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+
+ return accepted;
+ }
+ }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java
new file mode 100644
index 0000000..fc6f75f
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationManagerServiceShellCommand.java
@@ -0,0 +1,85 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.annotation.NonNull;
+import android.os.ShellCommand;
+
+import java.io.PrintWriter;
+
+/**
+ * The shell command implementation for the WallpaperEffectsGenerationService.
+ */
+public class WallpaperEffectsGenerationManagerServiceShellCommand extends ShellCommand {
+
+ private static final String TAG =
+ WallpaperEffectsGenerationManagerServiceShellCommand.class.getSimpleName();
+
+ private final WallpaperEffectsGenerationManagerService mService;
+
+ public WallpaperEffectsGenerationManagerServiceShellCommand(
+ @NonNull WallpaperEffectsGenerationManagerService service) {
+ mService = service;
+ }
+
+ @Override
+ public int onCommand(String cmd) {
+ if (cmd == null) {
+ return handleDefaultCommands(cmd);
+ }
+ final PrintWriter pw = getOutPrintWriter();
+ switch (cmd) {
+ case "set": {
+ final String what = getNextArgRequired();
+ switch (what) {
+ case "temporary-service": {
+ final int userId = Integer.parseInt(getNextArgRequired());
+ String serviceName = getNextArg();
+ if (serviceName == null) {
+ mService.resetTemporaryService(userId);
+ pw.println("WallpaperEffectsGenerationService temporarily reset. ");
+ return 0;
+ }
+ final int duration = Integer.parseInt(getNextArgRequired());
+ mService.setTemporaryService(userId, serviceName, duration);
+ pw.println("WallpaperEffectsGenerationService temporarily set to "
+ + serviceName + " for " + duration + "ms");
+ break;
+ }
+ }
+ }
+ break;
+ default:
+ return handleDefaultCommands(cmd);
+ }
+ return 0;
+ }
+
+ @Override
+ public void onHelp() {
+ try (PrintWriter pw = getOutPrintWriter()) {
+ pw.println("WallpaperEffectsGenerationService commands:");
+ pw.println(" help");
+ pw.println(" Prints this help text.");
+ pw.println("");
+ pw.println(" set temporary-service USER_ID [COMPONENT_NAME DURATION]");
+ pw.println(" Temporarily (for DURATION ms) changes the service implemtation.");
+ pw.println(" To reset, call with just the USER_ID argument.");
+ pw.println("");
+ }
+ }
+}
diff --git a/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
new file mode 100644
index 0000000..d541051
--- /dev/null
+++ b/services/wallpapereffectsgeneration/java/com/android/server/wallpapereffectsgeneration/WallpaperEffectsGenerationPerUserService.java
@@ -0,0 +1,274 @@
+/*
+ * 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.wallpapereffectsgeneration;
+
+import android.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.app.AppGlobals;
+import android.app.wallpapereffectsgeneration.CinematicEffectRequest;
+import android.app.wallpapereffectsgeneration.CinematicEffectResponse;
+import android.app.wallpapereffectsgeneration.ICinematicEffectListener;
+import android.content.ComponentName;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ServiceInfo;
+import android.os.RemoteException;
+import android.util.Slog;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.server.infra.AbstractPerUserSystemService;
+
+/**
+ * Per-user instance of {@link WallpaperEffectsGenerationManagerService}.
+ */
+public class WallpaperEffectsGenerationPerUserService extends
+ AbstractPerUserSystemService<WallpaperEffectsGenerationPerUserService,
+ WallpaperEffectsGenerationManagerService> implements
+ RemoteWallpaperEffectsGenerationService.RemoteWallpaperEffectsGenerationServiceCallback {
+
+ private static final String TAG =
+ WallpaperEffectsGenerationPerUserService.class.getSimpleName();
+
+ @GuardedBy("mLock")
+ private CinematicEffectListenerWrapper mCinematicEffectListenerWrapper;
+
+ @Nullable
+ @GuardedBy("mLock")
+ private RemoteWallpaperEffectsGenerationService mRemoteService;
+
+ protected WallpaperEffectsGenerationPerUserService(
+ WallpaperEffectsGenerationManagerService master,
+ Object lock, int userId) {
+ super(master, lock, userId);
+ }
+
+ @Override // from PerUserSystemService
+ protected ServiceInfo newServiceInfoLocked(@NonNull ComponentName serviceComponent)
+ throws NameNotFoundException {
+ ServiceInfo si;
+ try {
+ si = AppGlobals.getPackageManager().getServiceInfo(serviceComponent,
+ PackageManager.GET_META_DATA, mUserId);
+ } catch (RemoteException e) {
+ throw new NameNotFoundException("Could not get service for " + serviceComponent);
+ }
+ if (!Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE.equals(si.permission)) {
+ Slog.w(TAG, "WallpaperEffectsGenerationService from '" + si.packageName
+ + "' does not require permission "
+ + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
+ throw new SecurityException("Service does not require permission "
+ + Manifest.permission.BIND_WALLPAPER_EFFECTS_GENERATION_SERVICE);
+ }
+ return si;
+ }
+
+ @GuardedBy("mLock")
+ @Override // from PerUserSystemService
+ protected boolean updateLocked(boolean disabled) {
+ final boolean enabledChanged = super.updateLocked(disabled);
+ updateRemoteServiceLocked();
+ return enabledChanged;
+ }
+
+ /**
+ * Notifies the service of a new cinematic effect generation request.
+ */
+ @GuardedBy("mLock")
+ public void onGenerateCinematicEffectLocked(
+ @NonNull CinematicEffectRequest cinematicEffectRequest,
+ @NonNull ICinematicEffectListener cinematicEffectListener) {
+ String newTaskId = cinematicEffectRequest.getTaskId();
+ // Previous request is still being processed.
+ if (mCinematicEffectListenerWrapper != null) {
+ if (mCinematicEffectListenerWrapper.mTaskId.equals(newTaskId)) {
+ invokeCinematicListenerAndCleanup(
+ new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_PENDING, newTaskId)
+ .build()
+ );
+ } else {
+ invokeCinematicListenerAndCleanup(
+ new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_TOO_MANY_REQUESTS,
+ newTaskId).build()
+ );
+ }
+ return;
+ }
+ RemoteWallpaperEffectsGenerationService remoteService = ensureRemoteServiceLocked();
+ if (remoteService != null) {
+ remoteService.executeOnResolvedService(
+ s -> s.onGenerateCinematicEffect(cinematicEffectRequest));
+ mCinematicEffectListenerWrapper =
+ new CinematicEffectListenerWrapper(newTaskId, cinematicEffectListener);
+ } else {
+ if (isDebug()) {
+ Slog.d(TAG, "Remote service not found");
+ }
+ try {
+ cinematicEffectListener.onCinematicEffectGenerated(
+ createErrorCinematicEffectResponse(newTaskId));
+ } catch (RemoteException e) {
+ if (isDebug()) {
+ Slog.d(TAG, "Failed to invoke cinematic effect listener for task [" + newTaskId
+ + "]");
+ }
+ }
+ }
+ }
+
+ /**
+ * Notifies the service of a generated cinematic effect response.
+ */
+ @GuardedBy("mLock")
+ public void onReturnCinematicEffectResponseLocked(
+ @NonNull CinematicEffectResponse cinematicEffectResponse) {
+ invokeCinematicListenerAndCleanup(cinematicEffectResponse);
+ }
+
+ @GuardedBy("mLock")
+ private void updateRemoteServiceLocked() {
+ if (mRemoteService != null) {
+ mRemoteService.destroy();
+ mRemoteService = null;
+ }
+ // End existing response and clean up listener for next request.
+ if (mCinematicEffectListenerWrapper != null) {
+ invokeCinematicListenerAndCleanup(
+ createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
+ }
+
+ }
+
+ void onPackageUpdatedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageUpdatedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ void onPackageRestartedLocked() {
+ if (isDebug()) {
+ Slog.v(TAG, "onPackageRestartedLocked()");
+ }
+ destroyAndRebindRemoteService();
+ }
+
+ private void destroyAndRebindRemoteService() {
+ if (mRemoteService == null) {
+ return;
+ }
+
+ if (isDebug()) {
+ Slog.d(TAG, "Destroying the old remote service.");
+ }
+ mRemoteService.destroy();
+ mRemoteService = null;
+ mRemoteService = ensureRemoteServiceLocked();
+ if (mRemoteService != null) {
+ if (isDebug()) {
+ Slog.d(TAG, "Rebinding to the new remote service.");
+ }
+ mRemoteService.reconnect();
+ }
+ // Clean up listener for next request.
+ if (mCinematicEffectListenerWrapper != null) {
+ invokeCinematicListenerAndCleanup(
+ createErrorCinematicEffectResponse(mCinematicEffectListenerWrapper.mTaskId));
+ }
+ }
+
+ private CinematicEffectResponse createErrorCinematicEffectResponse(String taskId) {
+ return new CinematicEffectResponse.Builder(
+ CinematicEffectResponse.CINEMATIC_EFFECT_STATUS_ERROR,
+ taskId).build();
+ }
+
+ @GuardedBy("mLock")
+ private void invokeCinematicListenerAndCleanup(
+ CinematicEffectResponse cinematicEffectResponse) {
+ try {
+ if (mCinematicEffectListenerWrapper != null
+ && mCinematicEffectListenerWrapper.mListener != null) {
+ mCinematicEffectListenerWrapper.mListener.onCinematicEffectGenerated(
+ cinematicEffectResponse);
+ } else {
+ if (isDebug()) {
+ Slog.w(TAG, "Cinematic effect listener not found for task["
+ + mCinematicEffectListenerWrapper.mTaskId + "]");
+ }
+ }
+ } catch (RemoteException e) {
+ if (isDebug()) {
+ Slog.w(TAG, "Error invoking cinematic effect listener for task["
+ + mCinematicEffectListenerWrapper.mTaskId + "]");
+ }
+ } finally {
+ mCinematicEffectListenerWrapper = null;
+ }
+ }
+
+ @GuardedBy("mLock")
+ @Nullable
+ private RemoteWallpaperEffectsGenerationService ensureRemoteServiceLocked() {
+ if (mRemoteService == null) {
+ final String serviceName = getComponentNameLocked();
+ if (serviceName == null) {
+ if (mMaster.verbose) {
+ Slog.v(TAG, "ensureRemoteServiceLocked(): not set");
+ }
+ return null;
+ }
+ ComponentName serviceComponent = ComponentName.unflattenFromString(serviceName);
+
+ mRemoteService = new RemoteWallpaperEffectsGenerationService(getContext(),
+ serviceComponent, mUserId, this,
+ mMaster.isBindInstantServiceAllowed(), mMaster.verbose);
+ }
+
+ return mRemoteService;
+ }
+
+ @Override // from RemoteWallpaperEffectsGenerationService
+ public void onServiceDied(RemoteWallpaperEffectsGenerationService service) {
+ Slog.w(TAG, "remote wallpaper effects generation service died");
+ updateRemoteServiceLocked();
+ }
+
+ @Override // from RemoteWallpaperEffectsGenerationService
+ public void onConnectedStateChanged(boolean connected) {
+ if (!connected) {
+ Slog.w(TAG, "remote wallpaper effects generation service disconnected");
+ updateRemoteServiceLocked();
+ }
+ }
+
+ private static final class CinematicEffectListenerWrapper {
+ @NonNull
+ private final String mTaskId;
+ @NonNull
+ private final ICinematicEffectListener mListener;
+
+ CinematicEffectListenerWrapper(
+ @NonNull final String taskId,
+ @NonNull final ICinematicEffectListener listener) {
+ mTaskId = taskId;
+ mListener = listener;
+ }
+ }
+}
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index e560f34..2141c794 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -356,13 +356,17 @@
"android.telecom.extra.INCOMING_CALL_EXTRAS";
/**
- * Optional extra for {@link #ACTION_INCOMING_CALL} containing a boolean to indicate that the
- * call has an externally generated ringer. Used by the HfpClientConnectionService when In Band
- * Ringtone is enabled to prevent two ringers from being generated.
+ * Optional extra for {@link #addNewIncomingCall(PhoneAccountHandle, Bundle)} used to indicate
+ * that a call has an in-band ringtone associated with it. This is used when the device is
+ * acting as an HFP headset and the Bluetooth stack has received an in-band ringtone from the
+ * the HFP host which must be played instead of any local ringtone the device would otherwise
+ * have generated.
+ *
* @hide
*/
- public static final String EXTRA_CALL_EXTERNAL_RINGER =
- "android.telecom.extra.CALL_EXTERNAL_RINGER";
+ @SystemApi
+ public static final String EXTRA_CALL_HAS_IN_BAND_RINGTONE =
+ "android.telecom.extra.CALL_HAS_IN_BAND_RINGTONE";
/**
* Optional extra for {@link android.content.Intent#ACTION_CALL} and
@@ -1489,9 +1493,14 @@
* when placing calls. The user may still need to enable the {@link PhoneAccount} within
* the phone app settings before the account is usable.
* <p>
+ * Note: Each package is limited to 10 {@link PhoneAccount} registrations.
+ * <p>
* A {@link SecurityException} will be thrown if an app tries to register a
* {@link PhoneAccountHandle} where the package name specified within
* {@link PhoneAccountHandle#getComponentName()} does not match the package name of the app.
+ * <p>
+ * A {@link IllegalArgumentException} will be thrown if an app tries to register a
+ * {@link PhoneAccount} when the upper bound limit, 10, has already been reached.
*
* @param account The complete {@link PhoneAccount}.
*/
diff --git a/telephony/java/android/service/euicc/EuiccService.java b/telephony/java/android/service/euicc/EuiccService.java
index 184e154..30ed7c2 100644
--- a/telephony/java/android/service/euicc/EuiccService.java
+++ b/telephony/java/android/service/euicc/EuiccService.java
@@ -257,6 +257,13 @@
"android.service.euicc.extra.RESOLUTION_CARD_ID";
/**
+ * Intent extra set for resolution requests containing an int indicating the subscription id
+ * to be enabled.
+ */
+ public static final String EXTRA_RESOLUTION_SUBSCRIPTION_ID =
+ "android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID";
+
+ /**
* Intent extra set for resolution requests containing an int indicating the current port index.
*/
public static final String EXTRA_RESOLUTION_PORT_INDEX =
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/CarrierConfigManager.java b/telephony/java/android/telephony/CarrierConfigManager.java
index e0e7913..3c277b7 100644
--- a/telephony/java/android/telephony/CarrierConfigManager.java
+++ b/telephony/java/android/telephony/CarrierConfigManager.java
@@ -4540,9 +4540,7 @@
* Passing this value as {@link #KEY_SUBSCRIPTION_GROUP_UUID_STRING} will remove the
* subscription from a group instead of adding it to a group.
*
- * TODO: Expose in a future release.
- *
- * @hide
+ * <p>This value will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String REMOVE_GROUP_UUID_STRING = "00000000-0000-0000-0000-000000000000";
@@ -4555,9 +4553,7 @@
* <p>If set to {@link #REMOVE_GROUP_UUID_STRING}, then the subscription will be removed from
* its current group.
*
- * TODO: unhide this key.
- *
- * @hide
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
*/
public static final String KEY_SUBSCRIPTION_GROUP_UUID_STRING =
"subscription_group_uuid_string";
@@ -4605,17 +4601,15 @@
"data_switch_validation_min_gap_long";
/**
- * A boolean property indicating whether this subscription should be managed as an opportunistic
- * subscription.
- *
- * If true, then this subscription will be selected based on available coverage and will not be
- * available for a user in settings menus for selecting macro network providers. If unset,
- * defaults to “false”.
- *
- * TODO: unhide this key.
- *
- * @hide
- */
+ * A boolean property indicating whether this subscription should be managed as an opportunistic
+ * subscription.
+ *
+ * If true, then this subscription will be selected based on available coverage and will not be
+ * available for a user in settings menus for selecting macro network providers. If unset,
+ * defaults to “false”.
+ *
+ * <p>This key will work all the way back to {@link android.os.Build.VERSION_CODES#Q}.
+ */
public static final String KEY_IS_OPPORTUNISTIC_SUBSCRIPTION_BOOL =
"is_opportunistic_subscription_bool";
diff --git a/telephony/java/android/telephony/ImsiEncryptionInfo.java b/telephony/java/android/telephony/ImsiEncryptionInfo.java
index 4978692..82333a4 100644
--- a/telephony/java/android/telephony/ImsiEncryptionInfo.java
+++ b/telephony/java/android/telephony/ImsiEncryptionInfo.java
@@ -46,16 +46,17 @@
private final int keyType;
//Date-Time in UTC when the key will expire.
private final Date expirationTime;
+ private final int carrierId;
/** @hide */
public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
- byte[] key, Date expirationTime) {
- this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime);
+ byte[] key, Date expirationTime, int carrierId) {
+ this(mcc, mnc, keyType, keyIdentifier, makeKeyObject(key), expirationTime, carrierId);
}
/** @hide */
public ImsiEncryptionInfo(String mcc, String mnc, int keyType, String keyIdentifier,
- PublicKey publicKey, Date expirationTime) {
+ PublicKey publicKey, Date expirationTime, int carrierId) {
// todo need to validate that ImsiEncryptionInfo is being created with the correct params.
// Including validating that the public key is in "X.509" format. This will be done in
// a subsequent CL.
@@ -65,6 +66,7 @@
this.publicKey = publicKey;
this.keyIdentifier = keyIdentifier;
this.expirationTime = expirationTime;
+ this.carrierId = carrierId;
}
/** @hide */
@@ -78,6 +80,7 @@
keyIdentifier = in.readString();
keyType = in.readInt();
expirationTime = new Date(in.readLong());
+ carrierId = in.readInt();
}
/** @hide */
@@ -90,6 +93,11 @@
return this.mcc;
}
+ /** @hide */
+ public int getCarrierId() {
+ return carrierId;
+ }
+
/**
* Returns key identifier, a string that helps the authentication server to locate the
* private key to decrypt the permanent identity, or {@code null} when uavailable.
@@ -157,6 +165,7 @@
dest.writeString(keyIdentifier);
dest.writeInt(keyType);
dest.writeLong(expirationTime.getTime());
+ dest.writeInt(carrierId);
}
@Override
@@ -164,10 +173,11 @@
return "[ImsiEncryptionInfo "
+ "mcc=" + mcc
+ " mnc=" + mnc
- + " publicKey=" + publicKey
+ + ", publicKey=" + publicKey
+ ", keyIdentifier=" + keyIdentifier
+ ", keyType=" + keyType
+ ", expirationTime=" + expirationTime
+ + ", carrier_id=" + carrierId
+ "]";
}
}
diff --git a/telephony/java/android/telephony/SmsMessage.java b/telephony/java/android/telephony/SmsMessage.java
index 5a12865..e0145e6 100644
--- a/telephony/java/android/telephony/SmsMessage.java
+++ b/telephony/java/android/telephony/SmsMessage.java
@@ -79,7 +79,7 @@
public static final int ENCODING_8BIT = 2;
public static final int ENCODING_16BIT = 3;
/**
- * @hide This value is not defined in global standard. Only in Korea, this is used.
+ * This value is not defined in global standard. Only in Korea, this is used.
*/
public static final int ENCODING_KSC5601 = 4;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index 250e55c..0aaafef 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -2863,10 +2863,43 @@
* outlined above.
* @throws IllegalArgumentException if plans don't meet the requirements
* defined in {@link SubscriptionPlan}.
+ * @deprecated use {@link #setSubscriptionPlans(int, List, long)} instead.
*/
+ @Deprecated
public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans) {
+ setSubscriptionPlans(subId, plans, 0);
+ }
+
+ /**
+ * Set the description of the billing relationship plan between a carrier
+ * and a specific subscriber.
+ * <p>
+ * This method is only accessible to the following narrow set of apps:
+ * <ul>
+ * <li>The carrier app for this subscriberId, as determined by
+ * {@link TelephonyManager#hasCarrierPrivileges()}.
+ * <li>The carrier app explicitly delegated access through
+ * {@link CarrierConfigManager#KEY_CONFIG_PLANS_PACKAGE_OVERRIDE_STRING}.
+ * </ul>
+ *
+ * @param subId the subscriber this relationship applies to. An empty list
+ * may be sent to clear any existing plans.
+ * @param plans the list of plans. The first plan is always the primary and
+ * most important plan. Any additional plans are secondary and
+ * may not be displayed or used by decision making logic.
+ * @param expirationDurationMillis the duration after which the subscription plans
+ * will be automatically cleared, or {@code 0} to leave the plans until
+ * explicitly cleared, or the next reboot, whichever happens first.
+ * @throws SecurityException if the caller doesn't meet the requirements
+ * outlined above.
+ * @throws IllegalArgumentException if plans don't meet the requirements
+ * defined in {@link SubscriptionPlan}.
+ */
+ public void setSubscriptionPlans(int subId, @NonNull List<SubscriptionPlan> plans,
+ @DurationMillisLong long expirationDurationMillis) {
getNetworkPolicyManager().setSubscriptionPlans(subId,
- plans.toArray(new SubscriptionPlan[plans.size()]), mContext.getOpPackageName());
+ plans.toArray(new SubscriptionPlan[0]), expirationDurationMillis,
+ mContext.getOpPackageName());
}
/**
@@ -2885,17 +2918,17 @@
* @param subId the subscriber this override applies to.
* @param overrideUnmetered set if the billing relationship should be
* considered unmetered.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
setSubscriptionOverrideUnmetered(subId, overrideUnmetered,
- TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
}
/**
@@ -2917,8 +2950,8 @@
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
@@ -2926,10 +2959,10 @@
*/
public void setSubscriptionOverrideUnmetered(int subId, boolean overrideUnmetered,
@NonNull @Annotation.NetworkType int[] networkTypes,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
final int overrideValue = overrideUnmetered ? SUBSCRIPTION_OVERRIDE_UNMETERED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_UNMETERED,
- overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
}
/**
@@ -2949,17 +2982,17 @@
* @param subId the subscriber this override applies to.
* @param overrideCongested set if the subscription should be considered
* congested.
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
* outlined above.
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
setSubscriptionOverrideCongested(subId, overrideCongested,
- TelephonyManager.getAllNetworkTypes(), timeoutMillis);
+ TelephonyManager.getAllNetworkTypes(), expirationDurationMillis);
}
/**
@@ -2982,8 +3015,8 @@
* @param networkTypes the network types this override applies to. If no
* network types are specified, override values will be ignored.
* {@see TelephonyManager#getAllNetworkTypes()}
- * @param timeoutMillis the timeout after which the requested override will
- * be automatically cleared, or {@code 0} to leave in the
+ * @param expirationDurationMillis the duration after which the requested override
+ * will be automatically cleared, or {@code 0} to leave in the
* requested state until explicitly cleared, or the next reboot,
* whichever happens first.
* @throws SecurityException if the caller doesn't meet the requirements
@@ -2991,10 +3024,10 @@
*/
public void setSubscriptionOverrideCongested(int subId, boolean overrideCongested,
@NonNull @Annotation.NetworkType int[] networkTypes,
- @DurationMillisLong long timeoutMillis) {
+ @DurationMillisLong long expirationDurationMillis) {
final int overrideValue = overrideCongested ? SUBSCRIPTION_OVERRIDE_CONGESTED : 0;
getNetworkPolicyManager().setSubscriptionOverride(subId, SUBSCRIPTION_OVERRIDE_CONGESTED,
- overrideValue, networkTypes, timeoutMillis, mContext.getOpPackageName());
+ overrideValue, networkTypes, expirationDurationMillis, mContext.getOpPackageName());
}
/**
diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java
index baccb26..ba1a6ed 100644
--- a/telephony/java/android/telephony/TelephonyManager.java
+++ b/telephony/java/android/telephony/TelephonyManager.java
@@ -9199,7 +9199,8 @@
* @param allowedNetworkTypes The bitmask of allowed network types.
* @return true on success; false on any failure.
* @hide
- * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead.
+ * @deprecated Use {@link #setAllowedNetworkTypesForReason} instead with reason
+ * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}.
*/
@Deprecated
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@@ -9233,10 +9234,7 @@
/**
* To indicate allowed network type change is requested by user.
- *
- * @hide
*/
- @SystemApi
public static final int ALLOWED_NETWORK_TYPES_REASON_USER = 0;
/**
@@ -9255,10 +9253,7 @@
* Carrier configuration won't affect the settings configured through
* other reasons and will result in allowing network types that are in both
* configurations (i.e intersection of both sets).
- *
- * @hide
*/
- @SystemApi
public static final int ALLOWED_NETWORK_TYPES_REASON_CARRIER = 2;
/**
@@ -9272,35 +9267,23 @@
/**
* Set the allowed network types of the device and provide the reason triggering the allowed
* network change.
+ * <p>Requires permission: android.Manifest.MODIFY_PHONE_STATE or
+ * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
+ *
* This can be called for following reasons
* <ol>
* <li>Allowed network types control by USER {@link #ALLOWED_NETWORK_TYPES_REASON_USER}
- * <li>Allowed network types control by power manager
- * {@link #ALLOWED_NETWORK_TYPES_REASON_POWER}
* <li>Allowed network types control by carrier {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER}
- * <li>Allowed network types control by the user-controlled "Allow 2G" toggle
- * {@link #ALLOWED_NETWORK_TYPES_REASON_ENABLE_2G}
* </ol>
* This API will result in allowing an intersection of allowed network types for all reasons,
* including the configuration done through other reasons.
*
- * The functionality of this API with the parameter
- * {@link #ALLOWED_NETWORK_TYPES_REASON_CARRIER} is the same as the API
- * {@link TelephonyManager#setAllowedNetworkTypes}. Use this API instead of
- * {@link TelephonyManager#setAllowedNetworkTypes}.
- * <p>
- * If {@link android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported}
- * ({@link TelephonyManager#CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK}) returns true, then
- * setAllowedNetworkTypesBitmap is used on the radio interface. Otherwise,
- * setPreferredNetworkTypesBitmap is used instead.
- *
* @param reason the reason the allowed network type change is taking place
- * @param allowedNetworkTypes The bitmask of allowed network types.
+ * @param allowedNetworkTypes The bitmask of allowed network type
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
- * @hide
+ * @throws SecurityException if the caller does not have the required privileges
*/
- @SystemApi
@RequiresPermission(android.Manifest.permission.MODIFY_PHONE_STATE)
@RequiresFeature(
enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
@@ -9330,18 +9313,19 @@
*
* {@link #getAllowedNetworkTypesForReason} returns allowed network type for a
* specific reason.
+ * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
+ * that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
*
* @param reason the reason the allowed network type change is taking place
* @return the allowed network type bitmask
* @throws IllegalStateException if the Telephony process is not currently available.
* @throws IllegalArgumentException if invalid AllowedNetworkTypesReason is passed.
- * @hide
+ * @throws SecurityException if the caller does not have the required permission/privileges
*/
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(
enforcement = "android.telephony.TelephonyManager#isRadioInterfaceCapabilitySupported",
value = TelephonyManager.CAPABILITY_USES_ALLOWED_NETWORK_TYPES_BITMASK)
- @SystemApi
public @NetworkTypeBitMask long getAllowedNetworkTypesForReason(
@AllowedNetworkTypesReason int reason) {
if (!isValidAllowedNetworkTypesReason(reason)) {
@@ -13560,127 +13544,88 @@
// 2G
/**
* network type bitmask unknown.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_UNKNOWN = 0L;
/**
* network type bitmask indicating the support of radio tech GSM.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_GSM = (1 << (NETWORK_TYPE_GSM -1));
/**
* network type bitmask indicating the support of radio tech GPRS.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_GPRS = (1 << (NETWORK_TYPE_GPRS -1));
/**
* network type bitmask indicating the support of radio tech EDGE.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_EDGE = (1 << (NETWORK_TYPE_EDGE -1));
/**
* network type bitmask indicating the support of radio tech CDMA(IS95A/IS95B).
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_CDMA = (1 << (NETWORK_TYPE_CDMA -1));
/**
* network type bitmask indicating the support of radio tech 1xRTT.
- * @hide
*/
- @SystemApi
+ @SuppressLint("AllUpper")
public static final long NETWORK_TYPE_BITMASK_1xRTT = (1 << (NETWORK_TYPE_1xRTT - 1));
// 3G
/**
* network type bitmask indicating the support of radio tech EVDO 0.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_EVDO_0 = (1 << (NETWORK_TYPE_EVDO_0 -1));
/**
* network type bitmask indicating the support of radio tech EVDO A.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_EVDO_A = (1 << (NETWORK_TYPE_EVDO_A - 1));
/**
* network type bitmask indicating the support of radio tech EVDO B.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_EVDO_B = (1 << (NETWORK_TYPE_EVDO_B -1));
/**
* network type bitmask indicating the support of radio tech EHRPD.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_EHRPD = (1 << (NETWORK_TYPE_EHRPD -1));
/**
* network type bitmask indicating the support of radio tech HSUPA.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_HSUPA = (1 << (NETWORK_TYPE_HSUPA -1));
/**
* network type bitmask indicating the support of radio tech HSDPA.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_HSDPA = (1 << (NETWORK_TYPE_HSDPA -1));
/**
* network type bitmask indicating the support of radio tech HSPA.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_HSPA = (1 << (NETWORK_TYPE_HSPA -1));
/**
* network type bitmask indicating the support of radio tech HSPAP.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_HSPAP = (1 << (NETWORK_TYPE_HSPAP -1));
/**
* network type bitmask indicating the support of radio tech UMTS.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_UMTS = (1 << (NETWORK_TYPE_UMTS -1));
/**
* network type bitmask indicating the support of radio tech TD_SCDMA.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_TD_SCDMA = (1 << (NETWORK_TYPE_TD_SCDMA -1));
// 4G
/**
* network type bitmask indicating the support of radio tech LTE.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_LTE = (1 << (NETWORK_TYPE_LTE -1));
/**
* network type bitmask indicating the support of radio tech LTE CA (carrier aggregation).
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_LTE_CA = (1 << (NETWORK_TYPE_LTE_CA -1));
/**
* network type bitmask indicating the support of radio tech NR(New Radio) 5G.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_NR = (1 << (NETWORK_TYPE_NR -1));
/**
* network type bitmask indicating the support of radio tech IWLAN.
- * @hide
*/
- @SystemApi
public static final long NETWORK_TYPE_BITMASK_IWLAN = (1 << (NETWORK_TYPE_IWLAN -1));
/** @hide */
@@ -13735,12 +13680,11 @@
/**
* @return Modem supported radio access family bitmask
*
- * <p>Requires permission: {@link android.Manifest.permission#READ_PRIVILEGED_PHONE_STATE} or
+ * <p>Requires permission: android.Manifest.READ_PRIVILEGED_PHONE_STATE or
* that the calling app has carrier privileges (see {@link #hasCarrierPrivileges}).
- * @hide
+ *
+ * @throws SecurityException if the caller does not have the required permission
*/
- @SystemApi
- @TestApi
@RequiresPermission(android.Manifest.permission.READ_PRIVILEGED_PHONE_STATE)
@RequiresFeature(PackageManager.FEATURE_TELEPHONY_RADIO_ACCESS)
public @NetworkTypeBitMask long getSupportedRadioAccessFamily() {
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/DataServiceCallback.java b/telephony/java/android/telephony/data/DataServiceCallback.java
index 1ff6ec1..ec73471 100644
--- a/telephony/java/android/telephony/data/DataServiceCallback.java
+++ b/telephony/java/android/telephony/data/DataServiceCallback.java
@@ -261,9 +261,10 @@
}
/**
- * Unthrottles the APN on the current transport. There is no matching "APN throttle" method.
- * Instead, the APN is throttled when {@link IDataService#setupDataCall} fails within
- * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * Unthrottles the APN on the current transport.
+ * The APN is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+ * throttled until this method is called.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
@@ -284,9 +285,9 @@
/**
* Unthrottles the DataProfile on the current transport.
- * There is no matching "DataProfile throttle" method.
- * Instead, the DataProfile is throttled when {@link IDataService#setupDataCall} fails within
- * the time specified by {@link DataCallResponse#getRetryDurationMillis}.
+ * The DataProfile is throttled when {@link IDataService#setupDataCall} fails within
+ * the time specified by {@link DataCallResponse#getRetryDurationMillis} and will remain
+ * throttled until this method is called.
* <p/>
* see: {@link DataCallResponse#getRetryDurationMillis}
*
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/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
index d3ad9e8..3af5450 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/BitmapTransitionView.kt
@@ -41,7 +41,7 @@
ImageDecoder.createSource(context.resources, R.drawable.very_large_photo))
private val mShaderA = BitmapShader(mImageA, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
private val mShaderB = BitmapShader(mImageB, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP)
- private val mShader = RuntimeShader(AGSL, false)
+ private val mShader = RuntimeShader(AGSL)
private var mCurrentProgress = -1f
private var mForwardProgress = true
private var mCurrentAnimator = ValueAnimator.ofFloat(-1f, 1f)
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
index 65d168b..fafe60b 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/ColorFiltersMutateActivity.java
@@ -83,7 +83,7 @@
mBlendPaint = new Paint();
mBlendPaint.setColorFilter(new PorterDuffColorFilter(0, PorterDuff.Mode.SRC_OVER));
- mRuntimeShader = new RuntimeShader(sSkSL, false);
+ mRuntimeShader = new RuntimeShader(sSkSL);
mRuntimeShader.setFloatUniform("param1", mShaderParam1);
mRuntimeShader.setInputShader("bitmapShader", new BitmapShader(mBitmap1,
Shader.TileMode.CLAMP,
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
index 06280d2..3c71b96 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RenderEffectViewActivity.kt
@@ -30,7 +30,7 @@
class RenderEffectViewActivity : Activity() {
- private val mDropsShader = RuntimeShader(dropsAGSL, false)
+ private val mDropsShader = RuntimeShader(dropsAGSL)
private var mDropsAnimator = ValueAnimator.ofFloat(0f, 1f)
private var mStartTime = System.currentTimeMillis()
private lateinit var mScratchesImage: Bitmap
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
index 73c4b8a..b78907c 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/RippleActivity.java
@@ -109,7 +109,7 @@
p.setColor(mColor);
mPaint = CanvasProperty.createPaint(p);
- mRuntimeShader = new RuntimeShader(sSkSL, false);
+ mRuntimeShader = new RuntimeShader(sSkSL);
mRuntimeShader.setFloatUniform("in_maxRadius", MAX_RADIUS);
}
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
index 12e338c..2990c9e 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/StretchShaderActivity.java
@@ -69,7 +69,7 @@
linearLayout.setOrientation(LinearLayout.VERTICAL);
mBitmap = ((BitmapDrawable) getDrawable(R.drawable.sunset1)).getBitmap();
- mRuntimeShader = new RuntimeShader(SKSL, false);
+ mRuntimeShader = new RuntimeShader(SKSL);
BitmapShader bitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP,
Shader.TileMode.CLAMP);
diff --git a/tests/SilkFX/AndroidManifest.xml b/tests/SilkFX/AndroidManifest.xml
index c30d761..21256d8 100644
--- a/tests/SilkFX/AndroidManifest.xml
+++ b/tests/SilkFX/AndroidManifest.xml
@@ -20,17 +20,20 @@
<uses-sdk android:minSdkVersion="30"/>
<uses-permission android:name="android.permission.CONTROL_DISPLAY_BRIGHTNESS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<application android:label="SilkFX"
android:theme="@android:style/Theme.Material">
<activity android:name=".Main"
android:label="SilkFX Demos"
+ android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.LAUNCHER"/>
+ <category android:name="android.intent.category.LEANBACK_LAUNCHER"/>
</intent-filter>
</activity>
@@ -41,13 +44,16 @@
<activity android:name=".materials.GlassActivity"
android:label="Glass Examples"
- android:banner="@drawable/background1"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
- <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name=".materials.BackgroundBlurActivity"
+ android:theme="@style/Theme.BackgroundBlurTheme"
+ android:exported="true">
+ </activity>
+
</application>
</manifest>
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/tests/SilkFX/res/drawable/background_blur_drawable.xml
similarity index 76%
copy from packages/SystemUI/res/values-w500dp/dimens.xml
copy to tests/SilkFX/res/drawable/background_blur_drawable.xml
index 5ce5cee..173ca99 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/tests/SilkFX/res/drawable/background_blur_drawable.xml
@@ -1,5 +1,3 @@
-<?xml version="1.0" encoding="utf-8"?>
-
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -15,7 +13,8 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<resources>
- <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#20FFFFFF"/>
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/packages/SystemUI/res/values-w500dp/dimens.xml b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
similarity index 81%
rename from packages/SystemUI/res/values-w500dp/dimens.xml
rename to tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
index 5ce5cee..bd8942d 100644
--- a/packages/SystemUI/res/values-w500dp/dimens.xml
+++ b/tests/SilkFX/res/drawable/blur_activity_background_drawable_white.xml
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
-
<!--
~ Copyright (C) 2022 The Android Open Source Project
~
@@ -15,7 +14,7 @@
~ See the License for the specific language governing permissions and
~ limitations under the License.
-->
-
-<resources>
- <dimen name="controls_padding_horizontal">75dp</dimen>
-</resources>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <corners android:radius="10dp"/>
+</shape>
diff --git a/tests/SilkFX/res/layout/activity_background_blur.xml b/tests/SilkFX/res/layout/activity_background_blur.xml
new file mode 100644
index 0000000..f13c088
--- /dev/null
+++ b/tests/SilkFX/res/layout/activity_background_blur.xml
@@ -0,0 +1,173 @@
+<?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.
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/background"
+ android:layout_width="390dp"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:padding="15dp"
+ android:orientation="vertical"
+ tools:context=".materials.BackgroundBlurActivity">
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:padding="10dp"
+ android:textColor="#ffffffff"
+ android:text="Hello blurry world!"/>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background blur"/>
+
+ <SeekBar
+ android:id="@+id/set_background_blur"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content"/>
+ <TextView
+ android:id="@+id/background_blur_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Background alpha"/>
+
+ <SeekBar
+ android:id="@+id/set_background_alpha"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/background_alpha"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textColor="#ffffffff"
+ android:ems="3"
+ android:gravity="center"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Blur behind"/>
+
+ <SeekBar
+ android:id="@+id/set_blur_behind"
+ android:min="0"
+ android:max="300"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/blur_behind_radius"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"
+ android:textColor="#ffffffff"
+ android:text="Dim amount"/>
+
+ <SeekBar
+ android:id="@+id/set_dim_amount"
+ android:min="0"
+ android:max="100"
+ android:layout_width="160dp"
+ android:layout_height="wrap_content" />
+ <TextView
+ android:id="@+id/dim_amount"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="center"
+ android:textColor="#ffffffff"
+ android:paddingLeft="10dp"
+ android:paddingRight="10dp"
+ android:ems="3"
+ android:text="TODO"/>
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:layout_marginTop="5dp"
+ android:orientation="vertical"
+ android:gravity="center">
+
+ <Button
+ android:id="@+id/toggle_blur_enabled"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="Disable blur"
+ android:onClick="toggleForceBlurDisabled"/>
+
+ <Button
+ android:id="@+id/toggle_battery_saving_mode"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:text="TODO"
+ android:onClick="toggleBatterySavingMode"/>
+ </LinearLayout>
+ <requestFocus/>
+
+</LinearLayout>
diff --git a/tests/SilkFX/res/values/style.xml b/tests/SilkFX/res/values/style.xml
new file mode 100644
index 0000000..66edbb5
--- /dev/null
+++ b/tests/SilkFX/res/values/style.xml
@@ -0,0 +1,31 @@
+<?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.
+ -->
+<!-- Styles for immersive actions UI. -->
+<resources xmlns:android="http://schemas.android.com/apk/res/android">
+ <style name="Theme.BackgroundBlurTheme" parent= "Theme.AppCompat.Dialog">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBlurBehindEnabled">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ <item name="android:windowElevation">0dp</item>
+ <item name="buttonStyle">@style/AppTheme.Button</item>
+ <item name="colorAccent">#bbffffff</item>
+ </style>
+ <style name="AppTheme.Button" parent="Widget.AppCompat.Button">
+ <item name="android:textColor">#ffffffff</item>
+ </style>
+
+</resources>
diff --git a/tests/SilkFX/src/com/android/test/silkfx/Main.kt b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
index 9ed8d2f..7132ae8 100644
--- a/tests/SilkFX/src/com/android/test/silkfx/Main.kt
+++ b/tests/SilkFX/src/com/android/test/silkfx/Main.kt
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
@@ -30,6 +30,7 @@
import com.android.test.silkfx.app.EXTRA_TITLE
import com.android.test.silkfx.hdr.GlowActivity
import com.android.test.silkfx.materials.GlassActivity
+import com.android.test.silkfx.materials.BackgroundBlurActivity
import kotlin.reflect.KClass
class Demo(val name: String, val makeIntent: (Context) -> Intent) {
@@ -51,7 +52,8 @@
Demo("Blingy Notifications", R.layout.bling_notifications)
)),
DemoGroup("Materials", listOf(
- Demo("Glass", GlassActivity::class)
+ Demo("Glass", GlassActivity::class),
+ Demo("Background Blur", BackgroundBlurActivity::class)
))
)
@@ -126,4 +128,4 @@
AllDemos.forEachIndexed { index, _ -> list.expandGroup(index) }
}
-}
\ No newline at end of file
+}
diff --git a/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
new file mode 100644
index 0000000..9d17d38
--- /dev/null
+++ b/tests/SilkFX/src/com/android/test/silkfx/materials/BackgroundBlurActivity.kt
@@ -0,0 +1,189 @@
+/*
+ * 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.test.silkfx.materials
+
+import android.app.Activity
+import android.content.Intent
+import android.content.Context
+import android.graphics.Bitmap
+import android.graphics.BitmapFactory
+import android.graphics.Color
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.PaintDrawable
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.provider.Settings
+import android.util.TypedValue
+import android.view.View
+import android.view.WindowManager
+import android.widget.ImageView
+import android.widget.SeekBar
+import android.widget.Switch
+import android.widget.TextView
+import com.android.test.silkfx.R
+import com.android.internal.graphics.drawable.BackgroundBlurDrawable
+import android.widget.LinearLayout
+import android.widget.Button
+
+import android.view.ViewRootImpl
+
+class BackgroundBlurActivity : Activity(), SeekBar.OnSeekBarChangeListener {
+ var mBackgroundDrawable = PaintDrawable(Color.WHITE)
+ var mBackgroundBlurRadius = 50
+ var mAlphaWithBlur = 0.2f
+ var mAlphaNoBlur = 0.5f
+
+ var mBlurBehindRadius = 10
+ var mDimAmountWithBlur = 0.2f
+ var mDimAmountNoBlur = 0.2f
+
+ var mBlurForceDisabled = false
+ var mBatterySavingModeOn = false
+
+ lateinit var blurBackgroundSeekBar: SeekBar
+ lateinit var backgroundAlphaSeekBar : SeekBar
+ lateinit var blurBehindSeekBar : SeekBar
+ lateinit var dimAmountSeekBar : SeekBar
+
+ val blurEnabledListener = { enabled : Boolean ->
+ blurBackgroundSeekBar.setProgress(mBackgroundBlurRadius)
+ blurBehindSeekBar.setProgress(mBlurBehindRadius)
+
+ if (enabled) {
+ setBackgroundBlur(mBackgroundBlurRadius)
+ setBackgroundColorAlpha(mAlphaWithBlur)
+
+ setBlurBehind(mBlurBehindRadius)
+ setDimAmount(mDimAmountWithBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaWithBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountWithBlur * 100).toInt())
+ } else {
+ setBackgroundColorAlpha(mAlphaNoBlur)
+ setDimAmount(mDimAmountNoBlur)
+
+ backgroundAlphaSeekBar.setProgress((mAlphaNoBlur * 100).toInt())
+ dimAmountSeekBar.setProgress((mDimAmountNoBlur * 100).toInt())
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(R.layout.activity_background_blur)
+
+ window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
+ window.addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND)
+
+ mBackgroundDrawable.setCornerRadius(30f)
+ window.setBackgroundDrawable(mBackgroundDrawable)
+
+ mBatterySavingModeOn =
+ Settings.Global.getInt(getContentResolver(), Settings.Global.LOW_POWER_MODE, 0) == 1
+ setBatterySavingModeOn(mBatterySavingModeOn)
+
+ blurBackgroundSeekBar = requireViewById(R.id.set_background_blur)
+ backgroundAlphaSeekBar = requireViewById(R.id.set_background_alpha)
+ blurBehindSeekBar = requireViewById(R.id.set_blur_behind)
+ dimAmountSeekBar = requireViewById(R.id.set_dim_amount)
+
+ arrayOf(blurBackgroundSeekBar, backgroundAlphaSeekBar, blurBehindSeekBar, dimAmountSeekBar)
+ .forEach {
+ it.setOnSeekBarChangeListener(this)
+ }
+ }
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ getWindowManager().addCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ getWindowManager().removeCrossWindowBlurEnabledListener(blurEnabledListener)
+ }
+
+ override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
+ when (seekBar) {
+ blurBackgroundSeekBar -> setBackgroundBlur(progress)
+ backgroundAlphaSeekBar -> setBackgroundColorAlpha(progress / 100.0f)
+ blurBehindSeekBar -> setBlurBehind(progress)
+ dimAmountSeekBar -> setDimAmount(progress / 100.0f)
+ else -> throw IllegalArgumentException("Unknown seek bar")
+ }
+ }
+
+ override fun onStartTrackingTouch(seekBar: SeekBar?) {}
+ override fun onStopTrackingTouch(seekBar: SeekBar?) {}
+
+ fun setBlurDisabled(disabled: Boolean) {
+ mBlurForceDisabled = disabled
+ Settings.Global.putInt(getContentResolver(), Settings.Global.DISABLE_WINDOW_BLURS,
+ if (mBlurForceDisabled) 1 else 0)
+ (findViewById(R.id.toggle_blur_enabled) as Button)
+ .setText(if (mBlurForceDisabled) "Enable blurs" else "Disable blurs")
+ }
+
+ fun toggleForceBlurDisabled(v: View) {
+ setBlurDisabled(!mBlurForceDisabled)
+ }
+
+ fun setBackgroundBlur(radius: Int) {
+ mBackgroundBlurRadius = radius
+ (findViewById(R.id.background_blur_radius) as TextView).setText(radius.toString())
+ window.setBackgroundBlurRadius(mBackgroundBlurRadius)
+ }
+
+ fun setBlurBehind(radius: Int) {
+ mBlurBehindRadius = radius
+ (findViewById(R.id.blur_behind_radius) as TextView).setText(radius.toString())
+ window.getAttributes().setBlurBehindRadius(mBlurBehindRadius)
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setDimAmount(amount: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mDimAmountWithBlur = amount
+ } else {
+ mDimAmountNoBlur = amount
+ }
+ (findViewById(R.id.dim_amount) as TextView).setText("%.2f".format(amount))
+ window.getAttributes().dimAmount = amount
+ window.setAttributes(window.getAttributes())
+ }
+
+ fun setBatterySavingModeOn(on: Boolean) {
+ mBatterySavingModeOn = on
+ Settings.Global.putInt(getContentResolver(),
+ Settings.Global.LOW_POWER_MODE, if (on) 1 else 0)
+ (findViewById(R.id.toggle_battery_saving_mode) as Button).setText(
+ if (on) "Exit low power mode" else "Enter low power mode")
+ }
+
+ fun toggleBatterySavingMode(v: View) {
+ setBatterySavingModeOn(!mBatterySavingModeOn)
+ }
+
+ fun setBackgroundColorAlpha(alpha: Float) {
+ if (getWindowManager().isCrossWindowBlurEnabled()) {
+ mAlphaWithBlur = alpha
+ } else {
+ mAlphaNoBlur = alpha
+ }
+ (findViewById(R.id.background_alpha) as TextView).setText("%.2f".format(alpha))
+ mBackgroundDrawable.setAlpha((alpha * 255f).toInt())
+ getWindowManager().updateViewLayout(window.getDecorView(), window.getAttributes())
+ }
+}
diff --git a/tests/benchmarks/Android.bp b/tests/benchmarks/Android.bp
deleted file mode 100644
index f87ca2e..0000000
--- a/tests/benchmarks/Android.bp
+++ /dev/null
@@ -1,38 +0,0 @@
-// Copyright (C) 2015 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.
-
-// build framework base core benchmarks
-// ============================================================
-
-package {
- // See: http://go/android-license-faq
- // A large-scale-change added 'default_applicable_licenses' to import
- // all of the 'license_kinds' from "frameworks_base_license"
- // to get the below license kinds:
- // SPDX-license-identifier-Apache-2.0
- default_applicable_licenses: ["frameworks_base_license"],
-}
-
-java_library {
- name: "networkStatsFactory-benchmarks",
- installable: true,
-
- srcs: ["src/**/*.java"],
-
- libs: [
- "caliper-api-target",
- "services.core",
- ],
-
-}
diff --git a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java b/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
deleted file mode 100644
index ef014f0..0000000
--- a/tests/benchmarks/src/com/android/server/net/NetworkStatsFactoryBenchmark.java
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2012 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.net;
-
-import android.net.NetworkStats;
-import android.os.SystemClock;
-import com.android.server.net.NetworkStatsFactory;
-import com.google.caliper.AfterExperiment;
-import com.google.caliper.BeforeExperiment;
-import java.io.File;
-
-public class NetworkStatsFactoryBenchmark {
- private File mStats;
-
- // TODO: consider staging stats file with different number of rows
-
- @BeforeExperiment
- protected void setUp() {
- mStats = new File("/proc/net/xt_qtaguid/stats");
- }
-
- @AfterExperiment
- protected void tearDown() {
- mStats = null;
- }
-
- public void timeReadNetworkStatsDetailJava(int reps) throws Exception {
- for (int i = 0; i < reps; i++) {
- NetworkStatsFactory.javaReadNetworkStatsDetail(mStats, NetworkStats.UID_ALL,
- // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
- // Fixed compilation problem but needs addressing properly.
- new String[0], 999);
- }
- }
-
- public void timeReadNetworkStatsDetailNative(int reps) {
- for (int i = 0; i < reps; i++) {
- final NetworkStats stats = new NetworkStats(SystemClock.elapsedRealtime(), 0);
- NetworkStatsFactory.nativeReadNetworkStatsDetail(
- stats, mStats.getAbsolutePath(), NetworkStats.UID_ALL,
- // Looks like this was broken by change d0c5b9abed60b7bc056d026bf0f2b2235410fb70
- // Fixed compilation problem but needs addressing properly.
- new String[0], 999, false);
- }
- }
-}
diff --git a/tests/benchmarks/src/com/android/server/net/OWNERS b/tests/benchmarks/src/com/android/server/net/OWNERS
deleted file mode 100644
index aa87958..0000000
--- a/tests/benchmarks/src/com/android/server/net/OWNERS
+++ /dev/null
@@ -1 +0,0 @@
-include /services/core/java/com/android/server/net/OWNERS
diff --git a/tests/componentalias/AndroidTest-template.xml b/tests/componentalias/AndroidTest-template.xml
index 2d46217..afdfe79 100644
--- a/tests/componentalias/AndroidTest-template.xml
+++ b/tests/componentalias/AndroidTest-template.xml
@@ -21,8 +21,6 @@
<option name="test-file-name" value="ComponentAliasTests2.apk" />
</target_preparer>
<target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option name="run-command" value="am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android" />
-
<!-- Exempt the helper APKs from the BG restriction, so they can start BG services. -->
<option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests" />
<option name="run-command" value="cmd deviceidle whitelist +android.content.componentalias.tests.sub1" />
diff --git a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
index 89db2f7..9658d6f 100644
--- a/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
+++ b/tests/componentalias/src/android/content/componentalias/tests/BaseComponentAliasTest.java
@@ -17,6 +17,7 @@
import android.content.ComponentName;
import android.content.Context;
+import android.os.Build;
import android.provider.DeviceConfig;
import android.util.Log;
@@ -27,6 +28,7 @@
import com.android.compatibility.common.util.TestUtils;
import org.junit.AfterClass;
+import org.junit.Assume;
import org.junit.Before;
import java.util.function.Consumer;
@@ -37,7 +39,11 @@
protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
@Before
- public void enableComponentAlias() throws Exception {
+ public void enableComponentAliasWithCompatFlag() throws Exception {
+ Assume.assumeTrue(Build.isDebuggable());
+ ShellUtils.runShellCommand(
+ "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
+ sDeviceConfig.set("enable_experimental_component_alias", "");
sDeviceConfig.set("component_alias_overrides", "");
// Make sure the feature is actually enabled.
@@ -49,6 +55,8 @@
@AfterClass
public static void restoreDeviceConfig() throws Exception {
+ ShellUtils.runShellCommand(
+ "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
sDeviceConfig.close();
}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java
new file mode 100644
index 0000000..52c6d5b
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasEnableWithDeviceConfigTest.java
@@ -0,0 +1,63 @@
+/*
+ * 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.content.componentalias.tests;
+
+import android.os.Build;
+import android.provider.DeviceConfig;
+
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.ShellUtils;
+import com.android.compatibility.common.util.TestUtils;
+
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.Test;
+
+public class ComponentAliasEnableWithDeviceConfigTest {
+ protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
+
+ @AfterClass
+ public static void restoreDeviceConfig() throws Exception {
+ sDeviceConfig.close();
+ }
+
+ @Test
+ public void enableComponentAliasWithCompatFlag() throws Exception {
+ Assume.assumeTrue(Build.isDebuggable());
+
+ sDeviceConfig.set("component_alias_overrides", "");
+
+ // First, disable with both compat-id and device config.
+ ShellUtils.runShellCommand(
+ "am compat disable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
+ sDeviceConfig.set("enable_experimental_component_alias", "");
+
+ TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+ return ShellUtils.runShellCommand("dumpsys activity component-alias")
+ .indexOf("Enabled: false") > 0;
+ });
+
+ // Then, enable by device config.
+ sDeviceConfig.set("enable_experimental_component_alias", "true");
+
+ // Make sure the feature is actually enabled.
+ TestUtils.waitUntil("Wait until component alias is actually enabled", () -> {
+ return ShellUtils.runShellCommand("dumpsys activity component-alias")
+ .indexOf("Enabled: true") > 0;
+ });
+ }
+}
diff --git a/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java
new file mode 100644
index 0000000..7935476
--- /dev/null
+++ b/tests/componentalias/src/android/content/componentalias/tests/ComponentAliasNotSupportedOnUserBuildTest.java
@@ -0,0 +1,60 @@
+/*
+ * 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.content.componentalias.tests;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.os.Build;
+import android.provider.DeviceConfig;
+
+import com.android.compatibility.common.util.DeviceConfigStateHelper;
+import com.android.compatibility.common.util.ShellUtils;
+
+import org.junit.AfterClass;
+import org.junit.Assume;
+import org.junit.Test;
+
+/**
+ * Test to make sure component-alias can't be enabled on user builds.
+ */
+public class ComponentAliasNotSupportedOnUserBuildTest {
+ protected static final DeviceConfigStateHelper sDeviceConfig = new DeviceConfigStateHelper(
+ DeviceConfig.NAMESPACE_ACTIVITY_MANAGER);
+
+ @AfterClass
+ public static void restoreDeviceConfig() throws Exception {
+ sDeviceConfig.close();
+ }
+
+ @Test
+ public void enableComponentAliasWithCompatFlag() throws Exception {
+ Assume.assumeFalse(Build.isDebuggable());
+
+ // Try to enable it by both the device config and compat-id.
+ sDeviceConfig.set("enable_experimental_component_alias", "true");
+ ShellUtils.runShellCommand(
+ "am compat enable --no-kill USE_EXPERIMENTAL_COMPONENT_ALIAS android");
+
+ // Sleep for an arbitrary amount of time, so the config would sink in, if there was
+ // no "not on user builds" check.
+
+ Thread.sleep(5000);
+
+ // Make sure the feature is still disabled.
+ assertThat(ShellUtils.runShellCommand("dumpsys activity component-alias")
+ .indexOf("Enabled: false") > 0).isTrue();
+ }
+}
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>
diff --git a/tools/fonts/fontchain_linter.py b/tools/fonts/fontchain_linter.py
index 99f77fe..2c2c918 100755
--- a/tools/fonts/fontchain_linter.py
+++ b/tools/fonts/fontchain_linter.py
@@ -635,23 +635,11 @@
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- if _emoji_sequences.get(sequence, None) == 'Emoji_Tag_Sequence':
- # Add reverse of all emoji ZWJ sequences, which are added to the
- # fonts as a workaround to get the sequences work in RTL text.
- # TODO: test if these are actually needed by Minikin/HarfBuzz.
- reversed_seq = reverse_emoji(sequence)
- all_sequences.add(reversed_seq)
- equivalent_emoji[reversed_seq] = sequence
for sequence in adjusted_emoji_zwj_sequences.keys():
sequence = tuple(ch for ch in sequence if ch != EMOJI_VS)
all_sequences.add(sequence)
sequence_pieces.update(sequence)
- # Add reverse of all emoji ZWJ sequences, which are added to the fonts
- # as a workaround to get the sequences work in RTL text.
- reversed_seq = reverse_emoji(sequence)
- all_sequences.add(reversed_seq)
- equivalent_emoji[reversed_seq] = sequence
for first, second in SAME_FLAG_MAPPINGS:
equivalent_emoji[first] = second