Merge "Copy the device manager role holder to the other profile during provisioning"
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/framework/java/com/android/server/usage/AppStandbyInternal.java b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
index ddcc746..13ecd25 100644
--- a/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
+++ b/apex/jobscheduler/framework/java/com/android/server/usage/AppStandbyInternal.java
@@ -3,6 +3,7 @@
import android.annotation.CurrentTimeMillisLong;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.AppStandbyInfo;
import android.app.usage.UsageStatsManager.ForcedReasons;
import android.app.usage.UsageStatsManager.StandbyBuckets;
@@ -223,4 +224,12 @@
* a broadcast.
*/
long getBroadcastResponseWindowDurationMs();
+
+ /**
+ * Returns the process state threshold that should be used for deciding whether or not an app
+ * is in the background in the context of recording broadcast response stats. Apps whose
+ * process state is higher than this threshold state should be considered to be in background.
+ */
+ @ProcessState
+ int getBroadcastResponseFgThresholdState();
}
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/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
index 050e3df..b843dca 100644
--- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
+++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java
@@ -356,6 +356,14 @@
ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS;
/**
+ * Process state threshold that is used for deciding whether or not an app is in the background
+ * in the context of recording broadcast response stats. Apps whose process state is higher
+ * than this threshold state will be considered to be in background.
+ */
+ volatile int mBroadcastResponseFgThresholdState =
+ ConstantsObserver.DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE;
+
+ /**
* Whether we should allow apps into the
* {@link android.app.usage.UsageStatsManager#STANDBY_BUCKET_RESTRICTED} bucket or not.
* If false, any attempts to put an app into the bucket will put the app into the
@@ -1788,6 +1796,11 @@
}
@Override
+ public int getBroadcastResponseFgThresholdState() {
+ return mBroadcastResponseFgThresholdState;
+ }
+
+ @Override
public void flushToDisk() {
synchronized (mAppIdleLock) {
mAppIdleHistory.writeAppIdleTimes(mInjector.elapsedRealtime());
@@ -2058,6 +2071,10 @@
TimeUtils.formatDuration(mBroadcastResponseWindowDurationMillis, pw);
pw.println();
+ pw.print(" mBroadcastResponseFgThresholdState=");
+ pw.print(ActivityManager.procStateToString(mBroadcastResponseFgThresholdState));
+ pw.println();
+
pw.println();
pw.print("mAppIdleEnabled="); pw.print(mAppIdleEnabled);
pw.print(" mAllowRestrictedBucket=");
@@ -2491,6 +2508,8 @@
};
private static final String KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
"broadcast_response_window_timeout_ms";
+ private static final String KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
+ "broadcast_response_fg_threshold_state";
public static final long DEFAULT_CHECK_IDLE_INTERVAL_MS =
COMPRESS_TIME ? ONE_MINUTE : 4 * ONE_HOUR;
public static final long DEFAULT_STRONG_USAGE_TIMEOUT =
@@ -2522,6 +2541,8 @@
public static final boolean DEFAULT_CROSS_PROFILE_APPS_SHARE_STANDBY_BUCKETS = true;
public static final long DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS =
2 * ONE_MINUTE;
+ public static final int DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE =
+ ActivityManager.PROCESS_STATE_TOP;
ConstantsObserver(Handler handler) {
super(handler);
@@ -2644,6 +2665,11 @@
KEY_BROADCAST_RESPONSE_WINDOW_DURATION_MS,
DEFAULT_BROADCAST_RESPONSE_WINDOW_DURATION_MS);
break;
+ case KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE:
+ mBroadcastResponseFgThresholdState = properties.getInt(
+ KEY_BROADCAST_RESPONSE_FG_THRESHOLD_STATE,
+ DEFAULT_BROADCAST_RESPONSE_FG_THRESHOLD_STATE);
+ break;
default:
if (!timeThresholdsUpdated
&& (name.startsWith(KEY_PREFIX_SCREEN_TIME_THRESHOLD)
diff --git a/apex/media/framework/Android.bp b/apex/media/framework/Android.bp
index d963e68..2c2af28 100644
--- a/apex/media/framework/Android.bp
+++ b/apex/media/framework/Android.bp
@@ -61,10 +61,13 @@
"test_com.android.media",
],
min_sdk_version: "29",
+ lint: {
+ strict_updatability_linting: true,
+ },
visibility: [
"//frameworks/av/apex:__subpackages__",
- "//frameworks/base", // For framework-all
"//frameworks/base/apex/media/service",
+ "//frameworks/base/api", // For framework-all
],
}
diff --git a/apex/media/service/Android.bp b/apex/media/service/Android.bp
index 2714809..834e5cb 100644
--- a/apex/media/service/Android.bp
+++ b/apex/media/service/Android.bp
@@ -47,6 +47,9 @@
jarjar_rules: "jarjar_rules.txt",
sdk_version: "system_server_current",
min_sdk_version: "29", // TODO: We may need to bump this at some point.
+ lint: {
+ strict_updatability_linting: true,
+ },
apex_available: [
"com.android.media",
],
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 e2325b0..d598974 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
@@ -729,6 +737,10 @@
field public static final int freezesText = 16843116; // 0x101016c
field public static final int fromAlpha = 16843210; // 0x10101ca
field public static final int fromDegrees = 16843187; // 0x10101b3
+ field public static final int fromExtendBottom;
+ field public static final int fromExtendLeft;
+ field public static final int fromExtendRight;
+ field public static final int fromExtendTop;
field public static final int fromId = 16843850; // 0x101044a
field public static final int fromScene = 16843741; // 0x10103dd
field public static final int fromXDelta = 16843206; // 0x10101c6
@@ -1431,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;
@@ -1574,6 +1588,10 @@
field public static final int titleTextStyle = 16843512; // 0x10102f8
field public static final int toAlpha = 16843211; // 0x10101cb
field public static final int toDegrees = 16843188; // 0x10101b4
+ field public static final int toExtendBottom;
+ field public static final int toExtendLeft;
+ field public static final int toExtendRight;
+ field public static final int toExtendTop;
field public static final int toId = 16843849; // 0x1010449
field public static final int toScene = 16843742; // 0x10103de
field public static final int toXDelta = 16843207; // 0x10101c7
@@ -3061,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);
@@ -3122,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
@@ -3573,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 {
@@ -3870,7 +3894,7 @@
}
public static interface ValueAnimator.AnimatorUpdateListener {
- method public void onAnimationUpdate(android.animation.ValueAnimator);
+ method public void onAnimationUpdate(@NonNull android.animation.ValueAnimator);
}
}
@@ -4051,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);
@@ -4857,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
@@ -4952,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();
@@ -6931,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);
@@ -7308,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);
@@ -7593,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";
@@ -7912,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
@@ -8573,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();
@@ -9806,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";
@@ -11911,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";
@@ -15449,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);
@@ -15748,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;
@@ -15788,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);
@@ -15952,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;
@@ -16850,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";
@@ -16879,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
@@ -16887,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
@@ -17317,12 +17295,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;
@@ -17416,6 +17396,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);
@@ -17440,6 +17422,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);
@@ -17651,9 +17634,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
@@ -18061,6 +18051,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();
@@ -18117,6 +18108,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();
@@ -18124,6 +18116,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
@@ -19659,10 +19652,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
}
@@ -20178,14 +20174,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 {
@@ -26374,75 +26373,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;
@@ -26569,50 +26499,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);
@@ -31417,6 +31303,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>);
@@ -31458,6 +31347,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);
@@ -31501,6 +31393,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[]);
@@ -31518,6 +31411,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);
@@ -39411,6 +39305,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);
@@ -39418,6 +39313,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 {
@@ -39435,6 +39331,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();
@@ -39443,6 +39340,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 {
@@ -39498,6 +39396,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";
@@ -41213,6 +41112,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";
@@ -41297,6 +41197,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";
@@ -41340,6 +41241,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
@@ -42981,6 +42883,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";
@@ -43075,7 +42978,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";
@@ -43258,6 +43162,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();
@@ -43318,6 +43223,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();
@@ -43363,6 +43269,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);
@@ -43399,6 +43306,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
@@ -43473,6 +43382,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
@@ -48935,16 +48864,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 {
@@ -49187,7 +49115,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();
@@ -49211,6 +49139,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();
@@ -49323,10 +49255,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();
@@ -49372,7 +49300,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);
@@ -49576,7 +49504,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();
@@ -51469,6 +51396,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);
@@ -51480,6 +51408,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
@@ -51495,12 +51424,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
@@ -51660,6 +51594,7 @@
method public boolean isSelected();
method public boolean isShowingHintText();
method public boolean isTextEntryKey();
+ method public boolean isTextSelectable();
method public boolean isVisibleToUser();
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View);
method @Deprecated public static android.view.accessibility.AccessibilityNodeInfo obtain(android.view.View, int);
@@ -51723,6 +51658,7 @@
method public void setStateDescription(@Nullable CharSequence);
method public void setText(CharSequence);
method public void setTextEntryKey(boolean);
+ method public void setTextSelectable(boolean);
method public void setTextSelection(int, int);
method public void setTooltipText(@Nullable CharSequence);
method public void setTouchDelegateInfo(@NonNull android.view.accessibility.AccessibilityNodeInfo.TouchDelegateInfo);
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt
index df61a96..36d54f5 100644
--- a/core/api/module-lib-current.txt
+++ b/core/api/module-lib-current.txt
@@ -54,6 +54,21 @@
method public void onCanceled(@NonNull android.app.PendingIntent);
}
+ public class PropertyInvalidatedCache<Query, Result> {
+ ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
+ method public final void disableForCurrentProcess();
+ method public final void invalidateCache();
+ method public static void invalidateCache(int, @NonNull String);
+ method @Nullable public final Result query(@NonNull Query);
+ field public static final int MODULE_BLUETOOTH = 2; // 0x2
+ }
+
+ public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
+ ctor public PropertyInvalidatedCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public class StatusBarManager {
method @RequiresPermission(android.Manifest.permission.STATUS_BAR) public void setExpansionDisabledForSimNetworkLock(boolean);
}
@@ -63,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 {
@@ -124,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
}
}
@@ -176,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);
@@ -186,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 {
@@ -262,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);
@@ -315,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();
}
@@ -421,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 8e4775f..02f05d2 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";
@@ -203,6 +206,7 @@
field public static final String MODIFY_PARENTAL_CONTROLS = "android.permission.MODIFY_PARENTAL_CONTROLS";
field public static final String MODIFY_QUIET_MODE = "android.permission.MODIFY_QUIET_MODE";
field public static final String MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE = "android.permission.MODIFY_SETTINGS_OVERRIDEABLE_BY_RESTORE";
+ field public static final String MODIFY_TOUCH_MODE_STATE = "android.permission.MODIFY_TOUCH_MODE_STATE";
field public static final String MOVE_PACKAGE = "android.permission.MOVE_PACKAGE";
field public static final String NETWORK_AIRPLANE_MODE = "android.permission.NETWORK_AIRPLANE_MODE";
field public static final String NETWORK_CARRIER_PROVISIONING = "android.permission.NETWORK_CARRIER_PROVISIONING";
@@ -907,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
}
@@ -1068,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();
@@ -1080,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);
@@ -1158,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";
@@ -1187,6 +1213,15 @@
field public static final String WORK_PROFILE_PAUSED_TITLE = "MediaProvider.WORK_PROFILE_PAUSED_TITLE";
}
+ public static final class DevicePolicyResources.Strings.PermissionController {
+ field public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE";
+ field public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+ field public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE = "PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+ field public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE = "PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE";
+ field public static final String LOCATION_AUTO_GRANTED_MESSAGE = "PermissionController.LOCATION_AUTO_GRANTED_MESSAGE";
+ field public static final String WORK_PROFILE_DEFAULT_APPS_TITLE = "PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE";
+ }
+
public final class DevicePolicyStringResource implements android.os.Parcelable {
ctor public DevicePolicyStringResource(@NonNull android.content.Context, @NonNull String, @StringRes int);
method public int describeContents();
@@ -2225,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;
}
@@ -2236,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 {
@@ -2254,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;
}
@@ -2271,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;
}
@@ -2296,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;
}
@@ -2314,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);
}
@@ -2377,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 {
@@ -2486,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();
@@ -2544,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 {
@@ -2615,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 {
@@ -2700,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";
@@ -2732,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";
@@ -3703,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 {
@@ -5797,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);
@@ -5958,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();
@@ -6904,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();
@@ -7824,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();
@@ -8208,23 +8370,6 @@
method public void release();
}
- public final class IpSecManager {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void applyTunnelModeTransform(@NonNull android.net.IpSecManager.IpSecTunnelInterface, int, @NonNull android.net.IpSecTransform) throws java.io.IOException;
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecManager.IpSecTunnelInterface createIpSecTunnelInterface(@NonNull java.net.InetAddress, @NonNull java.net.InetAddress, @NonNull android.net.Network) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException;
- }
-
- public static final class IpSecManager.IpSecTunnelInterface implements java.lang.AutoCloseable {
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void addAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method public void close();
- method @NonNull public String getInterfaceName();
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void removeAddress(@NonNull java.net.InetAddress, int) throws java.io.IOException;
- method @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public void setUnderlyingNetwork(@NonNull android.net.Network) throws java.io.IOException;
- }
-
- public static class IpSecTransform.Builder {
- method @NonNull @RequiresPermission(android.Manifest.permission.MANAGE_IPSEC_TUNNELS) public android.net.IpSecTransform buildTunnelModeTransform(@NonNull java.net.InetAddress, @NonNull android.net.IpSecManager.SecurityParameterIndex) throws java.io.IOException, android.net.IpSecManager.ResourceUnavailableException, android.net.IpSecManager.SpiUnavailableException;
- }
-
public final class MatchAllNetworkSpecifier extends android.net.NetworkSpecifier implements android.os.Parcelable {
ctor public MatchAllNetworkSpecifier();
method public int describeContents();
@@ -8286,48 +8431,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);
@@ -8359,19 +8462,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();
}
@@ -8552,23 +8642,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 {
@@ -9007,6 +9080,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;
}
@@ -9539,7 +9613,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();
@@ -9835,7 +9909,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
@@ -10635,6 +10710,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
@@ -10642,6 +10719,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";
}
@@ -10650,6 +10728,10 @@
method public void onSuccess(int, long);
}
+ public static final class AttentionService.ProximityCallback {
+ method public void onProximityUpdate(double);
+ }
+
}
package android.service.autofill {
@@ -11000,6 +11082,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
@@ -11073,8 +11156,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);
}
@@ -11084,6 +11169,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);
@@ -11502,6 +11592,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();
@@ -11512,13 +11603,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
@@ -11694,6 +11788,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 {
@@ -12097,6 +12202,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";
@@ -13040,7 +13146,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>);
@@ -13086,7 +13191,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();
@@ -13141,7 +13245,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);
@@ -13191,10 +13294,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
@@ -13234,26 +13335,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
@@ -14580,6 +14661,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 3571869..4132c64 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -2,7 +2,6 @@
package android {
public static final class Manifest.permission {
- field public static final String ACCESS_KEYGUARD_SECURE_STORAGE = "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE";
field public static final String ACCESS_NOTIFICATIONS = "android.permission.ACCESS_NOTIFICATIONS";
field public static final String ACTIVITY_EMBEDDING = "android.permission.ACTIVITY_EMBEDDING";
field public static final String APPROVE_INCIDENT_REPORTS = "android.permission.APPROVE_INCIDENT_REPORTS";
@@ -279,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);
}
@@ -288,8 +288,8 @@
}
public class KeyguardManager {
- method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean checkLock(int, @Nullable byte[]);
- method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean checkLock(int, @Nullable byte[]);
+ method @RequiresPermission(anyOf={android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS, "android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"}) public boolean setLock(int, @Nullable byte[], int, @Nullable byte[]);
}
public class LocaleManager {
@@ -356,6 +356,32 @@
ctor public PictureInPictureUiState(boolean);
}
+ public class PropertyInvalidatedCache<Query, Result> {
+ ctor public PropertyInvalidatedCache(int, int, @NonNull String, @NonNull String, @NonNull android.app.PropertyInvalidatedCache.QueryHandler<Query,Result>);
+ method @NonNull public static String createPropertyName(int, @NonNull String);
+ method public final void disableForCurrentProcess();
+ method public static void disableForTestMode();
+ method public final void disableInstance();
+ method public final void disableSystemWide();
+ method public final void forgetDisableLocal();
+ method public boolean getDisabledState();
+ method public final void invalidateCache();
+ method public static void invalidateCache(int, @NonNull String);
+ method public final boolean isDisabled();
+ method @Nullable public final Result query(@NonNull Query);
+ method public static void setTestMode(boolean);
+ method public void testPropertyName();
+ field public static final int MODULE_BLUETOOTH = 2; // 0x2
+ field public static final int MODULE_SYSTEM = 1; // 0x1
+ field public static final int MODULE_TEST = 0; // 0x0
+ }
+
+ public abstract static class PropertyInvalidatedCache.QueryHandler<Q, R> {
+ ctor public PropertyInvalidatedCache.QueryHandler();
+ method @Nullable public abstract R apply(@NonNull Q);
+ method public boolean shouldBypassCache(@NonNull Q);
+ }
+
public class StatusBarManager {
method public void cancelRequestAddTile(@NonNull String);
method public void clickNotification(@Nullable String, int, int, boolean);
@@ -472,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);
@@ -484,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
@@ -585,21 +614,8 @@
}
-package android.app.trust {
-
- public class TrustManager {
- method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void enableTrustAgentForUserForTest(@NonNull android.content.ComponentName, int);
- method @RequiresPermission(android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE) public void reportUserRequestedUnlock(int);
- }
-
-}
-
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);
@@ -650,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 {
@@ -784,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);
@@ -966,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 {
@@ -1588,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);
@@ -1608,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 {
@@ -1722,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 {
@@ -2374,14 +2384,6 @@
}
-package android.service.trust {
-
- public class TrustAgentService extends android.app.Service {
- method public void onUserRequestedUnlock();
- }
-
-}
-
package android.service.voice {
public class AlwaysOnHotwordDetector implements android.service.voice.HotwordDetector {
@@ -2508,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);
@@ -2790,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 "";
}
@@ -2806,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/accessibilityservice/TouchInteractionController.java b/core/java/android/accessibilityservice/TouchInteractionController.java
index bb2b8d4..735df80 100644
--- a/core/java/android/accessibilityservice/TouchInteractionController.java
+++ b/core/java/android/accessibilityservice/TouchInteractionController.java
@@ -376,7 +376,7 @@
throw new IllegalStateException(
"State transitions are not allowed without first adding a callback.");
}
- if (mState != STATE_TOUCH_INTERACTING) {
+ if (mState != STATE_TOUCH_INTERACTING && mState != STATE_DRAGGING) {
throw new IllegalStateException(
"State transitions are not allowed in " + stateToString(mState));
}
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..db865ce 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
@@ -8742,17 +8780,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/PropertyInvalidatedCache.java b/core/java/android/app/PropertyInvalidatedCache.java
index ec8d989..715de14 100644
--- a/core/java/android/app/PropertyInvalidatedCache.java
+++ b/core/java/android/app/PropertyInvalidatedCache.java
@@ -16,7 +16,11 @@
package android.app;
+import android.annotation.IntDef;
import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
@@ -27,9 +31,10 @@
import android.util.Log;
import com.android.internal.annotations.GuardedBy;
-import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.FastPrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
@@ -104,17 +109,21 @@
* <pre>
* public class ActivityThread {
* ...
+ * private final PropertyInvalidatedCache.QueryHandler<Integer, Birthday> mBirthdayQuery =
+ * new PropertyInvalidatedCache.QueryHandler<Integer, Birthday>() {
+ * {@literal @}Override
+ * public Birthday apply(Integer) {
+ * return GetService("birthdayd").getUserBirthday(userId);
+ * }
+ * };
* private static final int BDAY_CACHE_MAX = 8; // Maximum birthdays to cache
* private static final String BDAY_CACHE_KEY = "cache_key.birthdayd";
* private final PropertyInvalidatedCache<Integer, Birthday%> mBirthdayCache = new
- * PropertyInvalidatedCache<Integer, Birthday%>(BDAY_CACHE_MAX, BDAY_CACHE_KEY) {
- * {@literal @}Override
- * protected Birthday recompute(Integer userId) {
- * return GetService("birthdayd").getUserBirthday(userId);
- * }
- * };
+ * PropertyInvalidatedCache<Integer, Birthday%>(
+ * BDAY_CACHE_MAX, MODULE_SYSTEM, "getUserBirthday", mBirthdayQuery);
+ *
* public void disableUserBirthdayCache() {
- * mBirthdayCache.disableLocal();
+ * mBirthdayCache.disableForCurrentProcess();
* }
* public void invalidateUserBirthdayCache() {
* mBirthdayCache.invalidateCache();
@@ -221,10 +230,124 @@
*
* @param <Query> The class used to index cache entries: must be hashable and comparable
* @param <Result> The class holding cache entries; use a boxed primitive if possible
- *
- * {@hide}
+ * @hide
*/
-public abstract class PropertyInvalidatedCache<Query, Result> {
+@SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+@TestApi
+public class PropertyInvalidatedCache<Query, Result> {
+ /**
+ * This is a configuration class that customizes a cache instance.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static abstract class QueryHandler<Q,R> {
+ /**
+ * Compute a result given a query. The semantics are those of Functor.
+ */
+ public abstract @Nullable R apply(@NonNull Q query);
+
+ /**
+ * Return true if a query should not use the cache. The default implementation
+ * always uses the cache.
+ */
+ public boolean shouldBypassCache(@NonNull Q query) {
+ return false;
+ }
+ };
+
+ /**
+ * The system properties used by caches should be of the form <prefix>.<module>.<api>,
+ * where the prefix is "cache_key", the module is one of the constants below, and the
+ * api is any string. The ability to write the property (which happens during
+ * invalidation) depends on SELinux rules; these rules are defined against
+ * <prefix>.<module>. Therefore, the module chosen for a cache property must match
+ * the permissions granted to the processes that contain the corresponding caches.
+ * @hide
+ */
+ @IntDef(prefix = { "MODULE_" }, value = {
+ MODULE_TEST,
+ MODULE_SYSTEM,
+ MODULE_BLUETOOTH
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface Module {}
+
+ /**
+ * The module used for unit tests and cts tests. It is expected that no process in
+ * the system has permissions to write properties with this module.
+ * @hide
+ */
+ @TestApi
+ public static final int MODULE_TEST = 0;
+
+ /**
+ * The module used for system server/framework caches. This is not visible outside
+ * the system processes.
+ * @hide
+ */
+ @TestApi
+ public static final int MODULE_SYSTEM = 1;
+
+ /**
+ * The module used for bluetooth caches.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static final int MODULE_BLUETOOTH = 2;
+
+ // A static array mapping module constants to strings.
+ private final static String[] sModuleNames =
+ { "test", "system_server", "bluetooth" };
+
+ /**
+ * Construct a system property that matches the rules described above. The module is
+ * one of the permitted values above. The API is a string that is a legal Java simple
+ * identifier. The api is modified to conform to the system property style guide by
+ * replacing every upper case letter with an underscore and the lower case equivalent.
+ * There is no requirement that the apiName be the name of an actual API.
+ *
+ * Be aware that SystemProperties has a maximum length which is private to the
+ * implementation. The current maximum is 92 characters. If this method creates a
+ * property name that is too long, SystemProperties.set() will fail without a good
+ * error message.
+ * @hide
+ */
+ @TestApi
+ public static @NonNull String createPropertyName(@Module int module, @NonNull String apiName) {
+ char[] api = apiName.toCharArray();
+ int upper = 0;
+ for (int i = 0; i < api.length; i++) {
+ if (Character.isUpperCase(api[i])) {
+ upper++;
+ }
+ }
+ char[] suffix = new char[api.length + upper];
+ int j = 0;
+ for (int i = 0; i < api.length; i++) {
+ if (Character.isJavaIdentifierPart(api[i])) {
+ if (Character.isUpperCase(api[i])) {
+ suffix[j++] = '_';
+ suffix[j++] = Character.toLowerCase(api[i]);
+ } else {
+ suffix[j++] = api[i];
+ }
+ } else {
+ throw new IllegalArgumentException("invalid api name");
+ }
+ }
+
+ String moduleName = null;
+ try {
+ moduleName = sModuleNames[module];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ throw new IllegalArgumentException("invalid module " + module);
+ }
+
+ return "cache_key." + moduleName + "." + new String(suffix);
+ }
+
/**
* Reserved nonce values. Use isReservedNonce() to test for a reserved value. Note
* that all values cause the cache to be skipped.
@@ -335,6 +458,25 @@
*/
private final String mCacheName;
+ /**
+ * The function that computes a Result, given a Query. This function is called on a
+ * cache miss.
+ */
+ private QueryHandler<Query, Result> mComputer;
+
+ /**
+ * A default function that delegates to the deprecated recompute() method.
+ */
+ private static class DefaultComputer<Query, Result> extends QueryHandler<Query, Result> {
+ final PropertyInvalidatedCache<Query, Result> mCache;
+ DefaultComputer(PropertyInvalidatedCache<Query, Result> cache) {
+ mCache = cache;
+ }
+ public Result apply(Query query) {
+ return mCache.recompute(query);
+ }
+ }
+
@GuardedBy("mLock")
private final LinkedHashMap<Query, Result> mCache;
@@ -359,8 +501,13 @@
* property name. New clients should prefer the constructor that takes an explicit
* cache name.
*
+ * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
+ * constructor.
+ *
* @param maxEntries Maximum number of entries to cache; LRU discard
* @param propertyName Name of the system property holding the cache invalidation nonce.
+ *
+ * @hide
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName) {
this(maxEntries, propertyName, propertyName);
@@ -369,32 +516,73 @@
/**
* Make a new property invalidated cache.
*
+ * TODO(216112648): deprecate this as a public interface, in favor of the four-argument
+ * constructor.
+ *
* @param maxEntries Maximum number of entries to cache; LRU discard
* @param propertyName Name of the system property holding the cache invalidation nonce
* @param cacheName Name of this cache in debug and dumpsys
+ * @hide
*/
public PropertyInvalidatedCache(int maxEntries, @NonNull String propertyName,
@NonNull String cacheName) {
mPropertyName = propertyName;
mCacheName = cacheName;
mMaxEntries = maxEntries;
- mCache = new LinkedHashMap<Query, Result>(
+ mComputer = new DefaultComputer<>(this);
+ mCache = createMap();
+ registerCache();
+ }
+
+ /**
+ * Make a new property invalidated cache. The key is computed from the module and api
+ * parameters.
+ *
+ * @param maxEntries Maximum number of entries to cache; LRU discard
+ * @param module The module under which the cache key should be placed.
+ * @param api The api this cache front-ends. The api must be a Java identifier but
+ * need not be an actual api.
+ * @param cacheName Name of this cache in debug and dumpsys
+ * @param computer The code to compute values that are not in the cache.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public PropertyInvalidatedCache(int maxEntries, @Module int module, @NonNull String api,
+ @NonNull String cacheName, @NonNull QueryHandler<Query, Result> computer) {
+ mPropertyName = createPropertyName(module, api);
+ mCacheName = cacheName;
+ mMaxEntries = maxEntries;
+ mComputer = computer;
+ mCache = createMap();
+ registerCache();
+ }
+
+ // Create a map. This should be called only from the constructor.
+ private LinkedHashMap<Query, Result> createMap() {
+ return new LinkedHashMap<Query, Result>(
2 /* start small */,
0.75f /* default load factor */,
true /* LRU access order */) {
+ @GuardedBy("mLock")
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
final int size = size();
if (size > mHighWaterMark) {
mHighWaterMark = size;
}
- if (size > maxEntries) {
+ if (size > mMaxEntries) {
mMissOverflow++;
return true;
}
return false;
}
- };
+ };
+ }
+
+ // Register the map in the global list. If the cache is disabled globally, disable it
+ // now.
+ private void registerCache() {
synchronized (sCorkLock) {
sCaches.put(this, null);
if (sDisabledKeys.contains(mCacheName)) {
@@ -418,8 +606,9 @@
/**
* Enable or disable testing. The testing property map is cleared every time this
* method is called.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public static void setTestMode(boolean mode) {
sTesting = mode;
synchronized (sTestingPropertyMap) {
@@ -431,13 +620,22 @@
* Enable testing the specific cache key. Only keys in the map are subject to testing.
* There is no method to stop testing a property name. Just disable the test mode.
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public static void testPropertyName(@NonNull String name) {
+ private static void testPropertyName(@NonNull String name) {
synchronized (sTestingPropertyMap) {
sTestingPropertyMap.put(name, (long) NONCE_UNSET);
}
}
+ /**
+ * Enable testing the specific cache key. Only keys in the map are subject to testing.
+ * There is no method to stop testing a property name. Just disable the test mode.
+ * @hide
+ */
+ @TestApi
+ public void testPropertyName() {
+ testPropertyName(mPropertyName);
+ }
+
// Read the system property associated with the current cache. This method uses the
// handle for faster reading.
private long getCurrentNonce() {
@@ -490,6 +688,9 @@
/**
* Forget all cached values.
+ * TODO(216112648) remove this as a public API. Clients should invalidate caches, not clear
+ * them.
+ * @hide
*/
public final void clear() {
synchronized (mLock) {
@@ -505,22 +706,29 @@
* Fetch a result from scratch in case it's not in the cache at all. Called unlocked: may
* block. If this function returns null, the result of the cache query is null. There is no
* "negative cache" in the query: we don't cache null results at all.
+ * TODO(216112648): deprecate this as a public interface, in favor of an instance of
+ * QueryHandler.
+ * @hide
*/
- public abstract @NonNull Result recompute(@NonNull Query query);
+ public Result recompute(@NonNull Query query) {
+ return mComputer.apply(query);
+ }
/**
* Return true if the query should bypass the cache. The default behavior is to
* always use the cache but the method can be overridden for a specific class.
+ * TODO(216112648): deprecate this as a public interface, in favor of an instance of
+ * QueryHandler.
+ * @hide
*/
public boolean bypass(@NonNull Query query) {
- return false;
+ return mComputer.shouldBypassCache(query);
}
/**
- * Determines if a pair of responses are considered equal. Used to determine whether a
- * cache is inadvertently returning stale results when VERIFY is set to true. Some
- * existing clients override this method, but it is now deprecated in favor of a valid
- * equals() method on the Result class.
+ * Determines if a pair of responses are considered equal. Used to determine whether
+ * a cache is inadvertently returning stale results when VERIFY is set to true.
+ * @hide
*/
public boolean resultEquals(Result cachedResult, Result fetchedResult) {
// If a service crashes and returns a null result, the cached value remains valid.
@@ -541,6 +749,7 @@
* the meantime (if the nonce has changed in the meantime, we drop the cache and try the
* whole query again), or 3) null, which causes the old value to be removed from the cache
* and null to be returned as the result of the cache query.
+ * @hide
*/
protected Result refresh(Result oldResult, Query query) {
return oldResult;
@@ -551,7 +760,7 @@
* testing. To disable a cache in normal code, use disableLocal().
* @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public final void disableInstance() {
synchronized (mLock) {
mDisabled = true;
@@ -580,9 +789,10 @@
* disabled remain disabled (the "disabled" setting is sticky). However, new caches
* with this name will not be disabled. It is not an error if the cache name is not
* found in the list of disabled caches.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public final void clearDisableLocal() {
+ @TestApi
+ public final void forgetDisableLocal() {
synchronized (sCorkLock) {
sDisabledKeys.remove(mCacheName);
}
@@ -592,25 +802,43 @@
* Disable this cache in the current process, and all other caches that use the same
* name. This does not affect caches that have a different name but use the same
* property.
+ * TODO(216112648) Remove this in favor of disableForCurrentProcess().
+ * @hide
*/
public final void disableLocal() {
+ disableForCurrentProcess();
+ }
+
+ /**
+ * Disable this cache in the current process, and all other caches that use the same
+ * name. This does not affect caches that have a different name but use the same
+ * property.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public final void disableForCurrentProcess() {
disableLocal(mCacheName);
}
/**
- * Return whether the cache is disabled in this process.
+ * Return whether a cache instance is disabled.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
- public final boolean isDisabledLocal() {
+ @TestApi
+ public final boolean isDisabled() {
return mDisabled || !sEnabled;
}
/**
* Get a value from the cache or recompute it.
+ * @hide
*/
- public @NonNull Result query(@NonNull Query query) {
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public final @Nullable Result query(@NonNull Query query) {
// Let access to mDisabled race: it's atomic anyway.
- long currentNonce = (!isDisabledLocal()) ? getCurrentNonce() : NONCE_DISABLED;
+ long currentNonce = (!isDisabled()) ? getCurrentNonce() : NONCE_DISABLED;
if (bypass(query)) {
currentNonce = NONCE_BYPASS;
}
@@ -724,8 +952,9 @@
* When multiple caches share a single property value, using an instance method on one of
* the cache objects to invalidate all of the cache objects becomes confusing and you should
* just use the static version of this function.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public final void disableSystemWide() {
disableSystemWide(mPropertyName);
}
@@ -746,16 +975,33 @@
/**
* Non-static convenience version of invalidateCache() for situations in which only a single
* PropertyInvalidatedCache is keyed on a particular property value.
+ * @hide
*/
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
public final void invalidateCache() {
invalidateCache(mPropertyName);
}
/**
+ * Invalidate caches in all processes that are keyed for the module and api.
+ * @hide
+ */
+ @SystemApi(client=SystemApi.Client.MODULE_LIBRARIES)
+ @TestApi
+ public static void invalidateCache(@Module int module, @NonNull String api) {
+ invalidateCache(createPropertyName(module, api));
+ }
+
+ /**
* Invalidate PropertyInvalidatedCache caches in all processes that are keyed on
* {@var name}. This function is synchronous: caches are invalidated upon return.
*
+ * TODO(216112648) make this method private in favor of the two-argument (module, api)
+ * override.
+ *
* @param name Name of the cache-key property to invalidate
+ * @hide
*/
public static void invalidateCache(@NonNull String name) {
if (!sEnabled) {
@@ -824,6 +1070,7 @@
* corkInvalidations() and uncorkInvalidations() must be called in pairs.
*
* @param name Name of the cache-key property to cork
+ * @hide
*/
public static void corkInvalidations(@NonNull String name) {
if (!sEnabled) {
@@ -871,6 +1118,7 @@
* transitioning it to normal operation (unless explicitly disabled system-wide).
*
* @param name Name of the cache-key property to uncork
+ * @hide
*/
public static void uncorkInvalidations(@NonNull String name) {
if (!sEnabled) {
@@ -916,6 +1164,7 @@
* The auto-cork delay is configurable but it should not be too long. The purpose of
* the delay is to minimize the number of times a server writes to the system property
* when invalidating the cache. One write every 50ms does not hurt system performance.
+ * @hide
*/
public static final class AutoCorker {
public static final int DEFAULT_AUTO_CORK_DELAY_MS = 50;
@@ -1043,6 +1292,8 @@
* Return the query as a string, to be used in debug messages. New clients should not
* override this, but should instead add the necessary toString() method to the Query
* class.
+ * TODO(216112648) add a method in the QueryHandler and deprecate this API.
+ * @hide
*/
protected @NonNull String queryToString(@NonNull Query query) {
return Objects.toString(query);
@@ -1054,8 +1305,9 @@
* process does not have privileges to write SystemProperties. Once disabled it is not
* possible to re-enable caching in the current process. If a client wants to
* temporarily disable caching, use the corking mechanism.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public static void disableForTestMode() {
Log.d(TAG, "disabling all caches in the process");
sEnabled = false;
@@ -1064,10 +1316,11 @@
/**
* Report the disabled status of this cache instance. The return value does not
* reflect status of the property key.
+ * @hide
*/
- @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
+ @TestApi
public boolean getDisabledState() {
- return isDisabledLocal();
+ return isDisabled();
}
/**
@@ -1133,7 +1386,8 @@
}
/**
- * Dumps the contents of every cache in the process to the provided ParcelFileDescriptor.
+ * Dumps contents of every cache in the process to the provided ParcelFileDescriptor.
+ * @hide
*/
public static void dumpCacheInfo(@NonNull ParcelFileDescriptor pfd, @NonNull String[] args) {
ArrayList<PropertyInvalidatedCache> activeCaches;
@@ -1174,6 +1428,7 @@
/**
* Trim memory by clearing all the caches.
+ * @hide
*/
public static void onTrimMemory() {
for (PropertyInvalidatedCache pic : getActiveCaches()) {
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 edb6b69..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
@@ -633,7 +654,7 @@
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} to signal that an update
* to the role holder is required.
*
- * <p>This result code must be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
+ * <p>This result code can be accompanied by {@link #EXTRA_ROLE_HOLDER_STATE}.
*
* @hide
*/
@@ -641,15 +662,19 @@
public static final int RESULT_UPDATE_ROLE_HOLDER = 2;
/**
- * A {@link Bundle} extra which describes the state of the role holder at the time when it
- * returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
+ * A {@link PersistableBundle} extra which the role holder can use to describe its own state
+ * when it returns {@link #RESULT_UPDATE_ROLE_HOLDER}.
*
- * <p>After the update completes, the role holder's {@link
- * #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
+ * <p>If {@link #RESULT_UPDATE_ROLE_HOLDER} was accompanied by this extra, after the update
+ * completes, the role holder's {@link #ACTION_ROLE_HOLDER_PROVISION_MANAGED_PROFILE} or {@link
* #ACTION_ROLE_HOLDER_PROVISION_MANAGED_DEVICE_FROM_TRUSTED_SOURCE} intent will be relaunched,
* which will contain this extra. It is the role holder's responsibility to restore its
* state from this extra.
*
+ * <p>The content of this {@link PersistableBundle} is entirely up to the role holder. It
+ * should contain anything the role holder needs to restore its original state when it gets
+ * restarted.
+ *
* @hide
*/
@SystemApi
@@ -1326,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";
@@ -3250,6 +3278,7 @@
*
* @hide
*/
+ @TestApi
public static final int DEVICE_OWNER_TYPE_DEFAULT = 0;
/**
@@ -3257,6 +3286,7 @@
*
* @hide
*/
+ @TestApi
public static final int DEVICE_OWNER_TYPE_FINANCED = 1;
/**
@@ -3552,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) {
@@ -3565,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.
@@ -10109,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
@@ -10137,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
@@ -10154,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
*/
@@ -14872,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) {
@@ -14901,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();
@@ -15169,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
@@ -15330,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 cf349b0..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;
@@ -93,6 +97,12 @@
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_PERSONAL_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.SWITCH_TO_WORK_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.MediaProvider.WORK_PROFILE_PAUSED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.LOCATION_AUTO_GRANTED_MESSAGE;
+import static android.app.admin.DevicePolicyResources.Strings.PermissionController.WORK_PROFILE_DEFAULT_APPS_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_PERSONAL;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_CATEGORY_WORK;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ACCESSIBILITY_PERSONAL_ACCOUNT_TITLE;
@@ -117,8 +127,8 @@
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_LOCK_DEVICE;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_APPS_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_BUG_REPORT_WARNING;
-import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_NETWORK_LOGS_WARNING;
+import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_SECURITY_LOGS_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_USAGE_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_SEE_WORK_DATA_WARNING;
import static android.app.admin.DevicePolicyResources.Strings.Settings.ADMIN_CAN_WIPE_DEVICE;
@@ -500,7 +510,17 @@
WORK_PROFILE_PRIVACY_POLICY_INFO, CONNECTED_APPS_SEARCH_KEYWORDS,
WORK_PROFILE_UNIFICATION_SEARCH_KEYWORDS, ACCOUNTS_SEARCH_KEYWORDS,
CONTROLLED_BY_ADMIN_SUMMARY, WORK_PROFILE_USER_LABEL, WORK_CATEGORY_HEADER,
- PERSONAL_CATEGORY_HEADER
+ PERSONAL_CATEGORY_HEADER,
+
+ // PermissionController Strings
+ 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,
+
+ // 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 {
}
@@ -690,11 +710,14 @@
private static Set<String> buildStringsSet() {
Set<String> strings = new HashSet<>();
+ strings.addAll(Settings.buildStringsSet());
strings.addAll(Launcher.buildStringsSet());
strings.addAll(SystemUi.buildStringsSet());
strings.addAll(Core.buildStringsSet());
strings.addAll(DocumentsUi.buildStringsSet());
strings.addAll(MediaProvider.buildStringsSet());
+ strings.addAll(PermissionController.buildStringsSet());
+ strings.addAll(Dialer.buildStringsSet());
return strings;
}
@@ -2854,5 +2877,120 @@
return strings;
}
}
+
+ /**
+ * Class containing the identifiers used to update device management-related system strings
+ * in the PermissionController module.
+ */
+ public static final class PermissionController {
+
+ private PermissionController() {
+ }
+
+ private static final String PREFIX = "PermissionController.";
+
+ /**
+ * Title for settings page to show default apps for work.
+ */
+ public static final String WORK_PROFILE_DEFAULT_APPS_TITLE =
+ PREFIX + "WORK_PROFILE_DEFAULT_APPS_TITLE";
+
+ /**
+ * Summary indicating that a home role holder app is missing work profile support.
+ */
+ public static final String HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE =
+ PREFIX + "HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the background access is denied by an
+ * admin.
+ */
+ public static final String BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the background access is enabled by
+ * an admin.
+ */
+ public static final String BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Summary of a permission switch in Settings when the foreground access is enabled by
+ * an admin.
+ */
+ public static final String FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE =
+ PREFIX + "FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE";
+
+ /**
+ * Body of the notification shown to notify the user that the location permission has
+ * been granted to an app, accepts app name as a param.
+ */
+ public static final String LOCATION_AUTO_GRANTED_MESSAGE =
+ PREFIX + "LOCATION_AUTO_GRANTED_MESSAGE";
+
+ /**
+ * @hide
+ */
+ static Set<String> buildStringsSet() {
+ Set<String> strings = new HashSet<>();
+ strings.add(WORK_PROFILE_DEFAULT_APPS_TITLE);
+ strings.add(HOME_MISSING_WORK_PROFILE_SUPPORT_MESSAGE);
+ strings.add(BACKGROUND_ACCESS_DISABLED_BY_ADMIN_MESSAGE);
+ strings.add(BACKGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE);
+ strings.add(FOREGROUND_ACCESS_ENABLED_BY_ADMIN_MESSAGE);
+ strings.add(LOCATION_AUTO_GRANTED_MESSAGE);
+ 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/trust/ITrustManager.aidl b/core/java/android/app/trust/ITrustManager.aidl
index 7956a35..edabccf 100644
--- a/core/java/android/app/trust/ITrustManager.aidl
+++ b/core/java/android/app/trust/ITrustManager.aidl
@@ -17,7 +17,6 @@
package android.app.trust;
import android.app.trust.ITrustListener;
-import android.content.ComponentName;
import android.hardware.biometrics.BiometricSourceType;
/**
@@ -30,7 +29,6 @@
void reportUserRequestedUnlock(int userId);
void reportUnlockLockout(int timeoutMs, int userId);
void reportEnabledTrustAgentsChanged(int userId);
- void enableTrustAgentForUserForTest(in ComponentName componentName, int userId);
void registerTrustListener(in ITrustListener trustListener);
void unregisterTrustListener(in ITrustListener trustListener);
void reportKeyguardShowingChanged();
diff --git a/core/java/android/app/trust/TrustManager.java b/core/java/android/app/trust/TrustManager.java
index fba2d3e..70b7de0 100644
--- a/core/java/android/app/trust/TrustManager.java
+++ b/core/java/android/app/trust/TrustManager.java
@@ -16,14 +16,10 @@
package android.app.trust;
-import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
-
-import android.annotation.NonNull;
+import android.Manifest;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
-import android.annotation.TestApi;
import android.compat.annotation.UnsupportedAppUsage;
-import android.content.ComponentName;
import android.content.Context;
import android.hardware.biometrics.BiometricSourceType;
import android.os.Handler;
@@ -37,17 +33,9 @@
import java.util.List;
/**
- * Interface to the system service managing trust.
- *
- * <p>This class is for internal use only. This class is marked {@code @TestApi} to
- * enable testing the trust system including {@link android.service.trust.TrustAgentService}.
- * Methods which are currently not used in tests are marked @hide.
- *
- * @see com.android.server.trust.TrustManagerService
- *
+ * See {@link com.android.server.trust.TrustManagerService}
* @hide
*/
-@TestApi
@SystemService(Context.TRUST_SERVICE)
public class TrustManager {
@@ -63,8 +51,7 @@
private final ITrustManager mService;
private final ArrayMap<TrustListener, ITrustListener> mTrustListeners;
- /** @hide */
- public TrustManager(@NonNull IBinder b) {
+ public TrustManager(IBinder b) {
mService = ITrustManager.Stub.asInterface(b);
mTrustListeners = new ArrayMap<TrustListener, ITrustListener>();
}
@@ -75,10 +62,8 @@
*
* @param userId The id for the user to be locked/unlocked.
* @param locked The value for that user's locked state.
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
public void setDeviceLockedForUser(int userId, boolean locked) {
try {
mService.setDeviceLockedForUser(userId, locked);
@@ -93,11 +78,8 @@
* @param successful if true, the unlock attempt was successful.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- *
- * @hide
*/
@UnsupportedAppUsage
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUnlockAttempt(boolean successful, int userId) {
try {
mService.reportUnlockAttempt(successful, userId);
@@ -111,7 +93,6 @@
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUserRequestedUnlock(int userId) {
try {
mService.reportUserRequestedUnlock(userId);
@@ -131,10 +112,7 @@
* attempt to unlock the device again.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportUnlockLockout(int timeoutMs, int userId) {
try {
mService.reportUnlockLockout(timeoutMs, userId);
@@ -147,10 +125,7 @@
* Reports that the list of enabled trust agents changed for user {@param userId}.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportEnabledTrustAgentsChanged(int userId) {
try {
mService.reportEnabledTrustAgentsChanged(userId);
@@ -160,33 +135,10 @@
}
/**
- * Enables a trust agent.
- *
- * <p>The agent is specified by {@code componentName} and must be a subclass of
- * {@link android.service.trust.TrustAgentService} and otherwise meet the requirements
- * to be a trust agent.
- *
- * <p>This method can only be used in tests.
- *
- * @param componentName the trust agent to enable
- */
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
- public void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) {
- try {
- mService.enableTrustAgentForUserForTest(componentName, userId);
- } catch (RemoteException e) {
- throw e.rethrowFromSystemServer();
- }
- }
-
- /**
* Reports that the visibility of the keyguard has changed.
*
* Requires the {@link android.Manifest.permission#ACCESS_KEYGUARD_SECURE_STORAGE} permission.
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
public void reportKeyguardShowingChanged() {
try {
mService.reportKeyguardShowingChanged();
@@ -199,10 +151,7 @@
* Registers a listener for trust events.
*
* Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
- *
- * @hide
*/
- @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public void registerTrustListener(final TrustListener trustListener) {
try {
ITrustListener.Stub iTrustListener = new ITrustListener.Stub() {
@@ -243,10 +192,7 @@
* Unregisters a listener for trust events.
*
* Requires the {@link android.Manifest.permission#TRUST_LISTENER} permission.
- *
- * @hide
*/
- @RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public void unregisterTrustListener(final TrustListener trustListener) {
ITrustListener iTrustListener = mTrustListeners.remove(trustListener);
if (iTrustListener != null) {
@@ -261,8 +207,6 @@
/**
* @return whether {@param userId} has enabled and configured trust agents. Ignores short-term
* unavailability of trust due to {@link LockPatternUtils.StrongAuthTracker}.
- *
- * @hide
*/
@RequiresPermission(android.Manifest.permission.TRUST_LISTENER)
public boolean isTrustUsuallyManaged(int userId) {
@@ -279,10 +223,8 @@
* can be skipped.
*
* @param userId
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
public void unlockedByBiometricForUser(int userId, BiometricSourceType source) {
try {
mService.unlockedByBiometricForUser(userId, source);
@@ -293,10 +235,8 @@
/**
* Clears authentication by the specified biometric type for all users.
- *
- * @hide
*/
- @RequiresPermission(ACCESS_KEYGUARD_SECURE_STORAGE)
+ @RequiresPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE)
public void clearAllBiometricRecognized(BiometricSourceType source, int unlockedUser) {
try {
mService.clearAllBiometricRecognized(source, unlockedUser);
@@ -324,7 +264,6 @@
}
};
- /** @hide */
public interface TrustListener {
/**
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/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..a29bffe 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) {
@@ -2214,6 +2214,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 +2239,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 +3477,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.
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..95238ee 100644
--- a/core/java/android/hardware/camera2/CameraMetadata.java
+++ b/core/java/android/hardware/camera2/CameraMetadata.java
@@ -1210,6 +1210,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 +1366,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
//
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/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 5874385..9235ba1 100644
--- a/core/java/android/hardware/hdmi/HdmiControlManager.java
+++ b/core/java/android/hardware/hdmi/HdmiControlManager.java
@@ -105,6 +105,12 @@
"android.hardware.hdmi.extra.MESSAGE_EXTRA_PARAM1";
/**
+ * Used as an extra field in the Set Menu Language intent. Contains the requested locale.
+ * @hide
+ */
+ public static final String EXTRA_LOCALE = "android.hardware.hdmi.extra.LOCALE";
+
+ /**
* Volume value for mute state.
*/
public static final int AVR_VOLUME_MUTED = 101;
@@ -558,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.
@@ -812,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
@@ -977,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 223b8cc..b6e1b1f 100644
--- a/core/java/android/inputmethodservice/InputMethodService.java
+++ b/core/java/android/inputmethodservice/InputMethodService.java
@@ -346,7 +346,7 @@
*/
@AnyThread
public static boolean canImeRenderGesturalNavButtons() {
- return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, false);
+ return SystemProperties.getBoolean(PROP_CAN_RENDER_GESTURAL_NAV_BUTTONS, true);
}
/**
@@ -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 9cea82b..6f4fd76 100644
--- a/core/java/android/inputmethodservice/NavigationBarController.java
+++ b/core/java/android/inputmethodservice/NavigationBarController.java
@@ -29,6 +29,7 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
+import android.graphics.Color;
import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
@@ -158,6 +159,8 @@
@Nullable
private ValueAnimator mTintAnimator;
+ private boolean mDrawLegacyNavigationBarBackground;
+
Impl(@NonNull InputMethodService inputMethodService) {
mService = inputMethodService;
}
@@ -216,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);
}
@@ -226,9 +229,14 @@
mLastInsets = systemInsets;
}
- mNavigationBarFrame.setBackground(null);
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
- setIconTintInternal(calculateTargetDarkIntensity(mAppearance));
+ setIconTintInternal(calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground));
}
private void uninstallNavigationBarFrameIfNecessary() {
@@ -462,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);
}
@@ -478,7 +486,8 @@
return;
}
- final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance);
+ final float targetDarkIntensity = calculateTargetDarkIntensity(mAppearance,
+ mDrawLegacyNavigationBarBackground);
if (mTintAnimator != null) {
mTintAnimator.cancel();
@@ -506,18 +515,44 @@
}
@FloatRange(from = 0.0f, to = 1.0f)
- private static float calculateTargetDarkIntensity(@Appearance int appearance) {
- final boolean lightNavBar = (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
+ private static float calculateTargetDarkIntensity(@Appearance int appearance,
+ boolean drawLegacyNavigationBarBackground) {
+ final boolean lightNavBar = !drawLegacyNavigationBarBackground
+ && (appearance & APPEARANCE_LIGHT_NAVIGATION_BARS) != 0;
return lightNavBar ? 1.0f : 0.0f;
}
@Override
+ public boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDestroyed) {
+ return false;
+ }
+
+ if (drawLegacyNavigationBarBackground != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackground = drawLegacyNavigationBarBackground;
+ if (mNavigationBarFrame != null) {
+ if (mDrawLegacyNavigationBarBackground) {
+ mNavigationBarFrame.setBackgroundColor(Color.BLACK);
+ } else {
+ mNavigationBarFrame.setBackground(null);
+ }
+ scheduleRelayout();
+ }
+ onSystemBarAppearanceChanged(mAppearance);
+ }
+ return drawLegacyNavigationBarBackground;
+ }
+
+ @Override
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 4e9aec7..3f41458 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10665,6 +10665,15 @@
public static final String FAST_PAIR_SCAN_ENABLED = "fast_pair_scan_enabled";
/**
+ * Setting to store denylisted system languages by the CEC {@code <Set Menu Language>}
+ * confirmation dialog.
+ *
+ * @hide
+ */
+ public static final String HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST =
+ "hdmi_cec_set_menu_language_denylist";
+
+ /**
* These entries are considered common between the personal and the managed profile,
* since the managed profile doesn't get to change them.
*/
@@ -16807,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
*/
@@ -17313,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 5bd4235..8f6e1e0 100644
--- a/core/java/android/service/trust/TrustAgentService.java
+++ b/core/java/android/service/trust/TrustAgentService.java
@@ -21,7 +21,6 @@
import android.annotation.NonNull;
import android.annotation.SdkConstant;
import android.annotation.SystemApi;
-import android.annotation.TestApi;
import android.app.Service;
import android.app.admin.DevicePolicyManager;
import android.content.ComponentName;
@@ -120,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;
@@ -140,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;
@@ -310,11 +305,7 @@
* {@link #grantTrust(CharSequence, long, int)}.
*
* @see #FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE
- *
- * TODO(b/213631672): Remove @hide and @TestApi
- * @hide
*/
- @TestApi
public void onUserRequestedUnlock() {
}
@@ -626,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/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/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/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 6f5fea2..8f8c5a9 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -2731,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..22c66dc 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;
@@ -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)
@@ -31447,23 +31447,4 @@
}
return null;
}
-
- /**
- * Returns the {@link OnBackInvokedDispatcher} instance of the window this view is attached to.
- *
- * @return The {@link OnBackInvokedDispatcher} or {@code null} if the view is neither attached
- * to a window or a view tree with a decor.
- */
- @Nullable
- public OnBackInvokedDispatcher getOnBackInvokedDispatcher() {
- ViewParent parent = getParent();
- if (parent instanceof View) {
- return ((View) parent).getOnBackInvokedDispatcher();
- } else if (parent instanceof ViewRootImpl) {
- // Get the fallback dispatcher on {@link ViewRootImpl} if the view tree doesn't have
- // a {@link com.android.internal.policy.DecorView}.
- return ((ViewRootImpl) parent).getOnBackInvokedDispatcher();
- }
- return null;
- }
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 386b277..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/Window.java b/core/java/android/view/Window.java
index 2f96908..8401b7c 100644
--- a/core/java/android/view/Window.java
+++ b/core/java/android/view/Window.java
@@ -673,6 +673,24 @@
* @param appearance The newly applied appearance.
*/
void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance);
+
+ /**
+ * Called from
+ * {@link com.android.internal.policy.DecorView#updateColorViews(WindowInsets, boolean)}
+ * when {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground} is
+ * being updated.
+ *
+ * @param drawLegacyNavigationBarBackground the new value that is being set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackground}.
+ * @return The value to be set to
+ * {@link com.android.internal.policy.DecorView#mDrawLegacyNavigationBarBackgroundHandled}
+ * on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code true} to tell that the Window can render the legacy navigation bar
+ * background on behalf of the {@link com.android.internal.policy.DecorView}.
+ * {@code false} to let {@link com.android.internal.policy.DecorView} handle it.
+ */
+ boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground);
}
/**
@@ -1019,6 +1037,16 @@
}
}
+ /** @hide */
+ public final boolean onDrawLegacyNavigationBarBackgroundChanged(
+ boolean drawLegacyNavigationBarBackground) {
+ if (mDecorCallback == null) {
+ return false;
+ }
+ return mDecorCallback.onDrawLegacyNavigationBarBackgroundChanged(
+ drawLegacyNavigationBarBackground);
+ }
+
/**
* Set a callback for changes of area where caption will draw its content.
*
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/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
index 0a33d6c..a31cacf 100644
--- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java
+++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java
@@ -710,6 +710,8 @@
private static final int BOOLEAN_PROPERTY_IS_TEXT_ENTRY_KEY = 0x0400000;
+ private static final int BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE = 0x0800000;
+
/**
* Bits that provide the id of a virtual descendant of a view.
*/
@@ -2276,6 +2278,38 @@
}
/**
+ * Gets if the node has selectable text.
+ *
+ * <p>
+ * Services should use {@link #ACTION_SET_SELECTION} for selection. Editable text nodes must
+ * also be selectable. But not all UIs will populate this field, so services should consider
+ * 'isTextSelectable | isEditable' to ensure they don't miss nodes with selectable text.
+ * </p>
+ *
+ * @see #isEditable
+ * @return True if the node has selectable text.
+ */
+ public boolean isTextSelectable() {
+ return getBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE);
+ }
+
+ /**
+ * Sets if the node has selectable text.
+ * <p>
+ * <strong>Note:</strong> Cannot be called from an
+ * {@link android.accessibilityservice.AccessibilityService}.
+ * This class is made immutable before being delivered to an AccessibilityService.
+ * </p>
+ *
+ * @param selectableText True if the node has selectable text, false otherwise.
+ *
+ * @throws IllegalStateException If called from an AccessibilityService.
+ */
+ public void setTextSelectable(boolean selectableText) {
+ setBooleanProperty(BOOLEAN_PROPERTY_IS_TEXT_SELECTABLE, selectableText);
+ }
+
+ /**
* Gets if the node is editable.
*
* @return True if the node is editable, false otherwise.
@@ -4327,8 +4361,12 @@
return "ACTION_CANCEL_DRAG";
case R.id.accessibilityActionDragDrop:
return "ACTION_DROP";
- default:
+ default: {
+ if (action == R.id.accessibilityActionShowSuggestions) {
+ return "ACTION_SHOW_SUGGESTIONS";
+ }
return "ACTION_UNKNOWN";
+ }
}
}
@@ -4462,6 +4500,7 @@
builder.append("; importantForAccessibility: ").append(isImportantForAccessibility());
builder.append("; visible: ").append(isVisibleToUser());
builder.append("; actions: ").append(mActions);
+ builder.append("; isTextSelectable: ").append(isTextSelectable());
return builder.toString();
}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
index ab749ee..fadbdbb 100644
--- a/core/java/android/view/animation/Animation.java
+++ b/core/java/android/view/animation/Animation.java
@@ -865,6 +865,15 @@
}
/**
+ * @return if a window animation has outsets applied to it.
+ *
+ * @hide
+ */
+ public boolean hasExtension() {
+ return false;
+ }
+
+ /**
* If showBackground is {@code true} and this animation is applied on a window, then the windows
* in the animation will animate with the background associated with this window behind them.
*
@@ -942,6 +951,21 @@
}
/**
+ * Gets the transformation to apply a specific point in time. Implementations of this method
+ * should always be kept in sync with getTransformation.
+ *
+ * @param normalizedTime time between 0 and 1 where 0 is the start of the animation and 1 the
+ * end.
+ * @param outTransformation A transformation object that is provided by the
+ * caller and will be filled in by the animation.
+ * @hide
+ */
+ public void getTransformationAt(float normalizedTime, Transformation outTransformation) {
+ final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
+ applyTransformation(interpolatedTime, outTransformation);
+ }
+
+ /**
* Gets the transformation to apply at a specified point in time. Implementations of this
* method should always replace the specified Transformation or document they are doing
* otherwise.
@@ -987,8 +1011,7 @@
normalizedTime = 1.0f - normalizedTime;
}
- final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
- applyTransformation(interpolatedTime, outTransformation);
+ getTransformationAt(normalizedTime, outTransformation);
}
if (expired) {
@@ -1228,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;
@@ -1260,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/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
index 03c6ca6..a2f3544 100644
--- a/core/java/android/view/animation/AnimationSet.java
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -363,6 +363,26 @@
* The transformation of an animation set is the concatenation of all of its
* component animations.
*
+ * @see android.view.animation.Animation#getTransformationAt
+ * @hide
+ */
+ @Override
+ public void getTransformationAt(float interpolatedTime, Transformation t) {
+ final Transformation temp = mTempTransformation;
+
+ for (int i = mAnimations.size() - 1; i >= 0; --i) {
+ final Animation a = mAnimations.get(i);
+
+ temp.clear();
+ a.getTransformationAt(interpolatedTime, t);
+ t.compose(temp);
+ }
+ }
+
+ /**
+ * The transformation of an animation set is the concatenation of all of its
+ * component animations.
+ *
* @see android.view.animation.Animation#getTransformation
*/
@Override
@@ -517,4 +537,15 @@
public boolean willChangeBounds() {
return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
}
+
+ /** @hide */
+ @Override
+ public boolean hasExtension() {
+ for (Animation animation : mAnimations) {
+ if (animation.hasExtension()) {
+ return true;
+ }
+ }
+ return false;
+ }
}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
index 7ce0f45..7d1dc76 100644
--- a/core/java/android/view/animation/AnimationUtils.java
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -190,6 +190,8 @@
anim = new TranslateAnimation(c, attrs);
} else if (name.equals("cliprect")) {
anim = new ClipRectAnimation(c, attrs);
+ } else if (name.equals("extend")) {
+ anim = new ExtendAnimation(c, attrs);
} else {
throw new RuntimeException("Unknown animation name: " + parser.getName());
}
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
new file mode 100644
index 0000000..210eb8a
--- /dev/null
+++ b/core/java/android/view/animation/ExtendAnimation.java
@@ -0,0 +1,176 @@
+/*
+ * 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.view.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Insets;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the outset of an object.
+ *
+ * @hide
+ */
+public class ExtendAnimation extends Animation {
+ protected Insets mFromInsets = Insets.NONE;
+ protected Insets mToInsets = Insets.NONE;
+
+ private int mFromLeftType = ABSOLUTE;
+ private int mFromTopType = ABSOLUTE;
+ private int mFromRightType = ABSOLUTE;
+ private int mFromBottomType = ABSOLUTE;
+
+ private int mToLeftType = ABSOLUTE;
+ private int mToTopType = ABSOLUTE;
+ private int mToRightType = ABSOLUTE;
+ private int mToBottomType = ABSOLUTE;
+
+ private float mFromLeftValue;
+ private float mFromTopValue;
+ private float mFromRightValue;
+ private float mFromBottomValue;
+
+ private float mToLeftValue;
+ private float mToTopValue;
+ private float mToRightValue;
+ private float mToBottomValue;
+
+ /**
+ * Constructor used when an ExtendAnimation is loaded from a resource.
+ *
+ * @param context Application context to use
+ * @param attrs Attribute set from which to read values
+ */
+ public ExtendAnimation(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a = context.obtainStyledAttributes(attrs,
+ com.android.internal.R.styleable.ExtendAnimation);
+
+ Description d = Description.parseValue(a.peekValue(
+ 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), context);
+ mFromTopType = d.type;
+ mFromTopValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ 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), context);
+ mFromBottomType = d.type;
+ mFromBottomValue = d.value;
+
+
+ d = Description.parseValue(a.peekValue(
+ 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), context);
+ mToTopType = d.type;
+ mToTopValue = d.value;
+
+ d = Description.parseValue(a.peekValue(
+ 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), context);
+ mToBottomType = d.type;
+ mToBottomValue = d.value;
+
+ a.recycle();
+ }
+
+ /**
+ * Constructor to use when building an ExtendAnimation from code
+ *
+ * @param fromInsets the insets to animate from
+ * @param toInsets the insets to animate to
+ */
+ public ExtendAnimation(Insets fromInsets, Insets toInsets) {
+ if (fromInsets == null || toInsets == null) {
+ throw new RuntimeException("Expected non-null animation outsets");
+ }
+ mFromLeftValue = -fromInsets.left;
+ mFromTopValue = -fromInsets.top;
+ mFromRightValue = -fromInsets.right;
+ mFromBottomValue = -fromInsets.bottom;
+
+ mToLeftValue = -toInsets.left;
+ mToTopValue = -toInsets.top;
+ mToRightValue = -toInsets.right;
+ mToBottomValue = -toInsets.bottom;
+ }
+
+ /**
+ * Constructor to use when building an ExtendAnimation from code
+ */
+ public ExtendAnimation(int fromL, int fromT, int fromR, int fromB,
+ int toL, int toT, int toR, int toB) {
+ this(Insets.of(-fromL, -fromT, -fromR, -fromB), Insets.of(-toL, -toT, -toR, -toB));
+ }
+
+ @Override
+ protected void applyTransformation(float it, Transformation tr) {
+ int l = mFromInsets.left + (int) ((mToInsets.left - mFromInsets.left) * it);
+ int t = mFromInsets.top + (int) ((mToInsets.top - mFromInsets.top) * it);
+ int r = mFromInsets.right + (int) ((mToInsets.right - mFromInsets.right) * it);
+ int b = mFromInsets.bottom + (int) ((mToInsets.bottom - mFromInsets.bottom) * it);
+ tr.setInsets(l, t, r, b);
+ }
+
+ @Override
+ public boolean willChangeTransformationMatrix() {
+ return false;
+ }
+
+ /** @hide */
+ @Override
+ public boolean hasExtension() {
+ return mFromInsets.left < 0 || mFromInsets.top < 0 || mFromInsets.right < 0
+ || mFromInsets.bottom < 0;
+ }
+
+ @Override
+ public void initialize(int width, int height, int parentWidth, int parentHeight) {
+ super.initialize(width, height, parentWidth, parentHeight);
+ // We remove any negative extension (i.e. positive insets) and set those to 0
+ mFromInsets = Insets.min(Insets.of(
+ -(int) resolveSize(mFromLeftType, mFromLeftValue, width, parentWidth),
+ -(int) resolveSize(mFromTopType, mFromTopValue, height, parentHeight),
+ -(int) resolveSize(mFromRightType, mFromRightValue, width, parentWidth),
+ -(int) resolveSize(mFromBottomType, mFromBottomValue, height, parentHeight)
+ ), Insets.NONE);
+ mToInsets = Insets.min(Insets.of(
+ -(int) resolveSize(mToLeftType, mToLeftValue, width, parentWidth),
+ -(int) resolveSize(mToTopType, mToTopValue, height, parentHeight),
+ -(int) resolveSize(mToRightType, mToRightValue, width, parentWidth),
+ -(int) resolveSize(mToBottomType, mToBottomValue, height, parentHeight)
+ ), Insets.NONE);
+ }
+}
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/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/Transformation.java b/core/java/android/view/animation/Transformation.java
index b35a66e..bd62308 100644
--- a/core/java/android/view/animation/Transformation.java
+++ b/core/java/android/view/animation/Transformation.java
@@ -18,6 +18,7 @@
import android.annotation.FloatRange;
import android.compat.annotation.UnsupportedAppUsage;
+import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
@@ -53,6 +54,8 @@
private boolean mHasClipRect;
private Rect mClipRect = new Rect();
+ private Insets mInsets = Insets.NONE;
+
/**
* Creates a new transformation with alpha = 1 and the identity matrix.
*/
@@ -132,8 +135,9 @@
setClipRect(bounds);
}
}
+ setInsets(Insets.add(getInsets(), t.getInsets()));
}
-
+
/**
* Like {@link #compose(Transformation)} but does this.postConcat(t) of
* the transformation matrix.
@@ -160,7 +164,7 @@
public Matrix getMatrix() {
return mMatrix;
}
-
+
/**
* Sets the degree of transparency
* @param alpha 1.0 means fully opaqe and 0.0 means fully transparent
@@ -170,6 +174,13 @@
}
/**
+ * @return The degree of transparency
+ */
+ public float getAlpha() {
+ return mAlpha;
+ }
+
+ /**
* Sets the current Transform's clip rect
* @hide
*/
@@ -203,12 +214,29 @@
}
/**
- * @return The degree of transparency
+ * Sets the current Transform's insets
+ * @hide
*/
- public float getAlpha() {
- return mAlpha;
+ public void setInsets(Insets insets) {
+ mInsets = insets;
}
-
+
+ /**
+ * Sets the current Transform's insets
+ * @hide
+ */
+ public void setInsets(int left, int top, int right, int bottom) {
+ mInsets = Insets.of(left, top, right, bottom);
+ }
+
+ /**
+ * Returns the current Transform's outset rect
+ * @hide
+ */
+ public Insets getInsets() {
+ return mInsets;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder(64);
@@ -216,7 +244,7 @@
toShortString(sb);
return sb.toString();
}
-
+
/**
* Return a string representation of the transformation in a compact form.
*/
@@ -225,7 +253,7 @@
toShortString(sb);
return sb.toString();
}
-
+
/**
* @hide
*/
@@ -234,7 +262,7 @@
sb.append(" matrix="); sb.append(mMatrix.toShortString());
sb.append('}');
}
-
+
/**
* Print short string, to optimize dumping.
* @hide
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/widget/TextView.java b/core/java/android/widget/TextView.java
index 7c939ac..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();
}
@@ -12289,6 +12295,7 @@
EXTRA_DATA_RENDERING_INFO_KEY,
EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY
));
+ info.setTextSelectable(isTextSelectable());
} else {
info.setAvailableExtraData(Arrays.asList(
EXTRA_DATA_RENDERING_INFO_KEY
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 58645ec..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;
@@ -285,6 +283,7 @@
private Insets mBackgroundInsets = Insets.NONE;
private Insets mLastBackgroundInsets = Insets.NONE;
private boolean mDrawLegacyNavigationBarBackground;
+ private boolean mDrawLegacyNavigationBarBackgroundHandled;
private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
@@ -296,7 +295,6 @@
return true;
};
private Consumer<Boolean> mCrossWindowBlurEnabledListener;
- private final WindowOnBackInvokedDispatcher mOnBackInvokedDispatcher;
DecorView(Context context, int featureId, PhoneWindow window,
WindowManager.LayoutParams params) {
@@ -325,7 +323,6 @@
initResizingPaints();
mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
- mOnBackInvokedDispatcher = new WindowOnBackInvokedDispatcher();
}
void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
@@ -1171,6 +1168,9 @@
mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
&& (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
+ mDrawLegacyNavigationBarBackgroundHandled =
+ mWindow.onDrawLegacyNavigationBarBackgroundChanged(
+ mDrawLegacyNavigationBarBackground);
if (viewRoot != null) {
viewRoot.requestInvalidateRootRenderNode();
}
@@ -1263,7 +1263,7 @@
}
}
- if (forceConsumingNavBar) {
+ if (forceConsumingNavBar && !mDrawLegacyNavigationBarBackgroundHandled) {
mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
} else {
mBackgroundInsets = Insets.NONE;
@@ -1876,7 +1876,6 @@
}
mPendingInsetsController.detach();
- mOnBackInvokedDispatcher.detachFromWindow();
}
@Override
@@ -1921,11 +1920,6 @@
return mPendingInsetsController;
}
- @Override
- public WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher() {
- return mOnBackInvokedDispatcher;
- }
-
private ActionMode createActionMode(
int type, ActionMode.Callback2 callback, View originatingView) {
switch (type) {
@@ -2380,7 +2374,6 @@
}
}
}
- mOnBackInvokedDispatcher.clear();
}
@Override
@@ -2488,7 +2481,7 @@
}
private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
- if (!mDrawLegacyNavigationBarBackground) {
+ if (!mDrawLegacyNavigationBarBackground || mDrawLegacyNavigationBarBackgroundHandled) {
return;
}
View v = mNavigationColorViewState.view;
@@ -2662,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/view/RootViewSurfaceTaker.java b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
index 4b89bf508..3ab9a33 100644
--- a/core/java/com/android/internal/view/RootViewSurfaceTaker.java
+++ b/core/java/com/android/internal/view/RootViewSurfaceTaker.java
@@ -15,12 +15,10 @@
*/
package com.android.internal.view;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.view.InputQueue;
import android.view.PendingInsetsController;
import android.view.SurfaceHolder;
-import android.window.WindowOnBackInvokedDispatcher;
/** hahahah */
public interface RootViewSurfaceTaker {
@@ -31,6 +29,4 @@
InputQueue.Callback willYouTakeTheInputQueue();
void onRootViewScrollYChanged(int scrollY);
@Nullable PendingInsetsController providePendingInsetsController();
- /** @hide */
- @NonNull WindowOnBackInvokedDispatcher provideWindowOnBackInvokedDispatcher();
}
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java
index 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 4d30874..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;
}
@@ -2164,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 }
@@ -2211,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);
@@ -2387,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/Android.bp b/core/proto/android/server/Android.bp
new file mode 100644
index 0000000..362daa7
--- /dev/null
+++ b/core/proto/android/server/Android.bp
@@ -0,0 +1,28 @@
+//
+// 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 {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+filegroup {
+ name: "srcs_bluetooth_manager_service_proto",
+ srcs: [
+ "bluetooth_manager_service.proto",
+ ],
+ visibility: ["//packages/modules/Bluetooth:__subpackages__"],
+}
+
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 1c09fc83..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).
@@ -4627,6 +4698,13 @@
<permission android:name="android.permission.READ_FRAME_BUFFER"
android:protectionLevel="signature|recents" />
+ <!-- @SystemApi Allows an application to change the touch mode state.
+ Without this permission, an app can only change the touch mode
+ if it currently has focus.
+ @hide -->
+ <permission android:name="android.permission.MODIFY_TOUCH_MODE_STATE"
+ android:protectionLevel="signature" />
+
<!-- Allows an application to use InputFlinger's low level features.
@hide -->
<permission android:name="android.permission.ACCESS_INPUT_FLINGER"
@@ -5371,7 +5449,7 @@
android:protectionLevel="signature|setup|role" />
<!-- Allows access to keyguard secure storage. Only allowed for system processes.
- @hide @TestApi -->
+ @hide -->
<permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE"
android:protectionLevel="signature|setup" />
@@ -5817,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> -->
@@ -5916,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 -->
@@ -6512,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 7916ef4..d774fd4 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -2885,7 +2885,7 @@
<code>public void sayHello(View v)</code> method of your context
(typically, your Activity).
{@deprecated View actually traverses the Context
- hierarchy looking for the relevant method, which is fragile (an intermediate
+ hierarchy looking for the relevant method, which is fragile (an intermediate
ContextWrapper adding a same-named method would change behavior) and restricts
bytecode optimizers such as R8. Instead, use View.setOnClickListener.}-->
<attr name="onClick" format="string" />
@@ -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">
@@ -6975,6 +6975,34 @@
<attr name="toBottom" format="fraction" />
</declare-styleable>
+ <!-- Defines the ExtendAnimation used to extend windows during animations -->
+ <declare-styleable name="ExtendAnimation">
+ <!-- Defines the amount a window should be extended outward from the left at
+ the start of the animation. -->
+ <attr name="fromExtendLeft" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the top at
+ the start of the animation. -->
+ <attr name="fromExtendTop" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the right at
+ the start of the animation. -->
+ <attr name="fromExtendRight" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the bottom at
+ the start of the animation. -->
+ <attr name="fromExtendBottom" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the left by
+ the end of the animation by transitioning from the fromExtendLeft amount. -->
+ <attr name="toExtendLeft" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the top by
+ the end of the animation by transitioning from the fromExtendTop amount. -->
+ <attr name="toExtendTop" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the right by
+ the end of the animation by transitioning from the fromExtendRight amount. -->
+ <attr name="toExtendRight" format="float|fraction" />
+ <!-- Defines the amount a window should be extended outward from the bottom by
+ the end of the animation by transitioning from the fromExtendBottom amount. -->
+ <attr name="toExtendBottom" format="float|fraction" />
+ </declare-styleable>
+
<declare-styleable name="LayoutAnimation">
<!-- Fraction of the animation duration used to delay the beginning of
the animation of each child. -->
@@ -7827,7 +7855,7 @@
<!-- Name of a method on the Context used to inflate the menu that will be
called when the item is clicked.
- {@deprecated Menu actually traverses the Context hierarchy looking for the
+ {@deprecated Menu actually traverses the Context hierarchy looking for the
relevant method, which is fragile (an intermediate ContextWrapper adding a
same-named method would change behavior) and restricts bytecode optimizers
such as R8. Instead, use MenuItem.setOnMenuItemClickListener.} -->
@@ -8847,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 7e14dc9..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>
@@ -2891,6 +2897,11 @@
<string name="config_sensorUseStartedActivity" translatable="false"
>com.android.systemui/com.android.systemui.sensorprivacy.SensorUseStartedActivity</string>
+ <!-- Component name of the activity used to ask a user to confirm system language change after
+ receiving <Set Menu Language> CEC message. -->
+ <string name="config_hdmiCecSetMenuLanguageActivity"
+ >com.android.systemui/com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity</string>
+
<!-- Name of the dialog that is used to request the user's consent for a Platform VPN -->
<string name="config_platformVpnConfirmDialogComponent" translatable="false"
>com.android.vpndialogs/com.android.vpndialogs.PlatformVpnConfirmDialog</string>
@@ -4168,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.
@@ -5262,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>
@@ -5648,4 +5674,13 @@
<!-- The amount of time after becoming non-interactive (in ms) after which
Low Power Standby can activate. -->
<integer name="config_lowPowerStandbyNonInteractiveTimeout">5000</integer>
+
+
+ <!-- Mapping to select an Intent.EXTRA_DOCK_STATE value from extcon state
+ key-value pairs. Each entry is evaluated in order and is of the form:
+ "[EXTRA_DOCK_STATE value],key1=value1,key2=value2[,...]"
+ An entry with no key-value pairs is valid and can be used as a wildcard.
+ -->
+ <string-array name="config_dockExtconStateMapping">
+ </string-array>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index 2e96c65..d57f5ba 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3255,11 +3255,24 @@
<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" />
<public name="preferKeepClear" />
<public name="autoHandwritingEnabled" />
+ <public name="fromExtendLeft" />
+ <public name="fromExtendTop" />
+ <public name="fromExtendRight" />
+ <public name="fromExtendBottom" />
+ <public name="toExtendLeft" />
+ <public name="toExtendTop" />
+ <public name="toExtendRight" />
+ <public name="toExtendBottom" />
</staging-public-group>
<staging-public-group type="id" first-id="0x01de0000">
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 f86add6..2b25c3e 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -375,6 +375,7 @@
<java-symbol type="string" name="config_usbConfirmActivity" />
<java-symbol type="string" name="config_usbResolverActivity" />
<java-symbol type="string" name="config_sensorUseStartedActivity" />
+ <java-symbol type="string" name="config_hdmiCecSetMenuLanguageActivity" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_maxNumVisibleRecentTasks_lowRam" />
<java-symbol type="integer" name="config_minNumVisibleRecentTasks_grid" />
@@ -1598,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" />
@@ -1696,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" />
@@ -2214,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" />
@@ -2706,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" />
@@ -3268,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" />
@@ -3672,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" />
@@ -3847,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" />
@@ -4128,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" />
@@ -4452,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" />
@@ -4679,6 +4711,8 @@
<java-symbol type="string" name="config_deviceSpecificDeviceStatePolicyProvider" />
+ <java-symbol type="array" name="config_dockExtconStateMapping" />
+
<java-symbol type="string" name="notification_channel_abusive_bg_apps"/>
<java-symbol type="string" name="notification_title_abusive_bg_apps"/>
<java-symbol type="string" name="notification_content_abusive_bg_apps"/>
@@ -4688,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/app/PropertyInvalidatedCacheTests.java b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
index d3e8bb0..5338d04 100644
--- a/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
+++ b/core/tests/coretests/src/android/app/PropertyInvalidatedCacheTests.java
@@ -37,9 +37,9 @@
@SmallTest
public class PropertyInvalidatedCacheTests {
- // This property is never set. The test process does not have permission to set any
- // properties.
- static final String CACHE_PROPERTY = "cache_key.cache_test_a";
+ // Configuration for creating caches
+ private static final int MODULE = PropertyInvalidatedCache.MODULE_TEST;
+ private static final String API = "testApi";
// This class is a proxy for binder calls. It contains a counter that increments
// every time the class is queried.
@@ -64,6 +64,25 @@
}
}
+ // The functions for querying the server.
+ private static class ServerQuery
+ extends PropertyInvalidatedCache.QueryHandler<Integer, Boolean> {
+ private final ServerProxy mServer;
+
+ ServerQuery(ServerProxy server) {
+ mServer = server;
+ }
+
+ @Override
+ public Boolean apply(Integer x) {
+ return mServer.query(x);
+ }
+ @Override
+ public boolean shouldBypassCache(Integer x) {
+ return x % 13 == 0;
+ }
+ }
+
// Clear the test mode after every test, in case this process is used for other
// tests. This also resets the test property map.
@After
@@ -82,19 +101,11 @@
// Create a cache that uses simple arithmetic to computer its values.
PropertyInvalidatedCache<Integer, Boolean> testCache =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- @Override
- public boolean bypass(Integer x) {
- return x % 13 == 0;
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache.setTestMode(true);
- PropertyInvalidatedCache.testPropertyName(CACHE_PROPERTY);
+ testCache.testPropertyName();
tester.verify(0);
assertEquals(tester.value(3), testCache.query(3));
@@ -136,26 +147,14 @@
// Three caches, all using the same system property but one uses a different name.
PropertyInvalidatedCache<Integer, Boolean> cache1 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache<Integer, Boolean> cache2 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
PropertyInvalidatedCache<Integer, Boolean> cache3 =
- new PropertyInvalidatedCache<>(4, CACHE_PROPERTY, "cache3") {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ new PropertyInvalidatedCache<>(4, MODULE, API, "cache3",
+ new ServerQuery(tester));
// Caches are enabled upon creation.
assertEquals(false, cache1.getDisabledState());
@@ -176,45 +175,29 @@
assertEquals(false, cache3.getDisabledState());
// Create a new cache1. Verify that the new instance is disabled.
- cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
assertEquals(true, cache1.getDisabledState());
// Remove the record of caches being locally disabled. This is a clean-up step.
- cache1.clearDisableLocal();
+ cache1.forgetDisableLocal();
assertEquals(true, cache1.getDisabledState());
assertEquals(true, cache2.getDisabledState());
assertEquals(false, cache3.getDisabledState());
// Create a new cache1. Verify that the new instance is not disabled.
- cache1 = new PropertyInvalidatedCache<>(4, CACHE_PROPERTY) {
- @Override
- public Boolean recompute(Integer x) {
- return tester.query(x);
- }
- };
+ cache1 = new PropertyInvalidatedCache<>(4, MODULE, API, "cache1",
+ new ServerQuery(tester));
assertEquals(false, cache1.getDisabledState());
}
- private static final String UNSET_KEY = "Aiw7woh6ie4toh7W";
+ private static class TestQuery
+ extends PropertyInvalidatedCache.QueryHandler<Integer, String> {
- private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
- TestCache() {
- this(CACHE_PROPERTY);
- }
-
- TestCache(String key) {
- super(4, key);
- setTestMode(true);
- testPropertyName(key);
- }
+ private int mRecomputeCount = 0;
@Override
- public String recompute(Integer qv) {
+ public String apply(Integer qv) {
mRecomputeCount += 1;
return "foo" + qv.toString();
}
@@ -222,15 +205,40 @@
int getRecomputeCount() {
return mRecomputeCount;
}
+ }
- private int mRecomputeCount = 0;
+ private static class TestCache extends PropertyInvalidatedCache<Integer, String> {
+ private final TestQuery mQuery;
+
+ TestCache() {
+ this(MODULE, API);
+ }
+
+ TestCache(int module, String api) {
+ this(module, api, new TestQuery());
+ setTestMode(true);
+ testPropertyName();
+ }
+
+ TestCache(int module, String api, TestQuery query) {
+ super(4, module, api, api, query);
+ mQuery = query;
+ setTestMode(true);
+ testPropertyName();
+ }
+
+ public int getRecomputeCount() {
+ return mQuery.getRecomputeCount();
+ }
+
+
}
@Test
public void testCacheRecompute() {
TestCache cache = new TestCache();
cache.invalidateCache();
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
assertEquals("foo5", cache.query(5));
@@ -241,6 +249,11 @@
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(3, cache.getRecomputeCount());
+ // Invalidate the cache with a direct call to the property.
+ PropertyInvalidatedCache.invalidateCache(MODULE, API);
+ assertEquals("foo5", cache.query(5));
+ assertEquals("foo5", cache.query(5));
+ assertEquals(4, cache.getRecomputeCount());
}
@Test
@@ -257,7 +270,8 @@
@Test
public void testCachePropertyUnset() {
- TestCache cache = new TestCache(UNSET_KEY);
+ final String UNSET_API = "otherApi";
+ TestCache cache = new TestCache(MODULE, UNSET_API);
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(2, cache.getRecomputeCount());
@@ -327,17 +341,40 @@
@Test
public void testLocalProcessDisable() {
TestCache cache = new TestCache();
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
cache.invalidateCache();
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
assertEquals("foo5", cache.query(5));
assertEquals(1, cache.getRecomputeCount());
- assertEquals(cache.isDisabledLocal(), false);
+ assertEquals(cache.isDisabled(), false);
cache.disableLocal();
- assertEquals(cache.isDisabledLocal(), true);
+ assertEquals(cache.isDisabled(), true);
assertEquals("foo5", cache.query(5));
assertEquals("foo5", cache.query(5));
assertEquals(3, cache.getRecomputeCount());
}
+
+ @Test
+ public void testPropertyNames() {
+ String n1;
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM, "getPackageInfo");
+ assertEquals(n1, "cache_key.system_server.get_package_info");
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM, "get_package_info");
+ assertEquals(n1, "cache_key.system_server.get_package_info");
+ try {
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_SYSTEM - 1, "get package_info");
+ // n1 is an invalid api name.
+ assertEquals(false, true);
+ } catch (IllegalArgumentException e) {
+ // An exception is expected here.
+ }
+
+ n1 = PropertyInvalidatedCache.createPropertyName(
+ PropertyInvalidatedCache.MODULE_BLUETOOTH, "getState");
+ assertEquals(n1, "cache_key.bluetooth.get_state");
+ }
}
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/AccessibilityNodeInfoTest.java b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
index 2018836..99670d9 100644
--- a/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
+++ b/core/tests/coretests/src/android/view/accessibility/AccessibilityNodeInfoTest.java
@@ -58,7 +58,7 @@
// The number of flags held in boolean properties. Their values should also be double-checked
// in the methods above.
- private static final int NUM_BOOLEAN_PROPERTIES = 23;
+ private static final int NUM_BOOLEAN_PROPERTIES = 24;
@Test
public void testStandardActions_serializationFlagIsValid() {
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/com.android.systemui.xml b/data/etc/com.android.systemui.xml
index d95644a..ae350ec 100644
--- a/data/etc/com.android.systemui.xml
+++ b/data/etc/com.android.systemui.xml
@@ -74,5 +74,6 @@
<permission name="android.permission.BROADCAST_CLOSE_SYSTEM_DIALOGS" />
<permission name="android.permission.FORCE_STOP_PACKAGES" />
<permission name="android.permission.ACCESS_FPS_COUNTER" />
+ <permission name="android.permission.CHANGE_CONFIGURATION" />
</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/src/com/android/wm/shell/transition/DefaultTransitionHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
index 27b6dc5..ddf01a8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/transition/DefaultTransitionHandler.java
@@ -67,7 +67,11 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.graphics.Canvas;
import android.graphics.Color;
+import android.graphics.Insets;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
@@ -78,6 +82,7 @@
import android.os.UserHandle;
import android.util.ArrayMap;
import android.view.Choreographer;
+import android.view.Surface;
import android.view.SurfaceControl;
import android.view.SurfaceSession;
import android.view.WindowManager;
@@ -103,6 +108,8 @@
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Consumer;
/** The default handler that handles anything not already handled. */
public class DefaultTransitionHandler implements Transitions.TransitionHandler {
@@ -331,6 +338,9 @@
finishCallback.onTransitionFinished(null /* wct */, null /* wctCB */);
};
+ final List<Consumer<SurfaceControl.Transaction>> postStartTransactionCallbacks =
+ new ArrayList<>();
+
@ColorInt int backgroundColorForTransition = 0;
final int wallpaperTransit = getWallpaperTransitType(info);
for (int i = info.getChanges().size() - 1; i >= 0; --i) {
@@ -402,13 +412,15 @@
}
}
- float cornerRadius = 0;
+ final float cornerRadius;
if (a.hasRoundedCorners() && isTask) {
// hasRoundedCorners is currently only enabled for tasks
final Context displayContext =
mDisplayController.getDisplayContext(change.getTaskInfo().displayId);
cornerRadius =
ScreenDecorationsUtils.getWindowCornerRadius(displayContext);
+ } else {
+ cornerRadius = 0;
}
if (a.getShowBackground()) {
@@ -424,12 +436,37 @@
}
}
+ boolean delayedEdgeExtension = false;
+ if (!isTask && a.hasExtension()) {
+ if (!Transitions.isOpeningType(change.getMode())) {
+ // Can screenshot now (before startTransaction is applied)
+ edgeExtendWindow(change, a, startTransaction, finishTransaction);
+ } else {
+ // Need to screenshot after startTransaction is applied otherwise activity
+ // may not be visible or ready yet.
+ postStartTransactionCallbacks
+ .add(t -> edgeExtendWindow(change, a, t, finishTransaction));
+ delayedEdgeExtension = true;
+ }
+ }
+
final Rect clipRect = Transitions.isClosingType(change.getMode())
? mRotator.getEndBoundsInStartRotation(change)
: change.getEndAbsBounds();
- startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
- mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
- cornerRadius, clipRect);
+
+ if (delayedEdgeExtension) {
+ // If the edge extension needs to happen after the startTransition has been
+ // applied, then we want to only start the animation after the edge extension
+ // postStartTransaction callback has been run
+ postStartTransactionCallbacks.add(t ->
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor,
+ null /* position */, cornerRadius, clipRect));
+ } else {
+ startSurfaceAnimation(animations, a, change.getLeash(), onAnimFinish,
+ mTransactionPool, mMainExecutor, mAnimExecutor, null /* position */,
+ cornerRadius, clipRect);
+ }
if (info.getAnimationOptions() != null) {
attachThumbnail(animations, onAnimFinish, change, info.getAnimationOptions(),
@@ -443,7 +480,20 @@
startTransaction, finishTransaction);
}
- startTransaction.apply();
+ // postStartTransactionCallbacks require that the start transaction is already
+ // applied to run otherwise they may result in flickers and UI inconsistencies.
+ boolean waitForStartTransactionApply = postStartTransactionCallbacks.size() > 0;
+ startTransaction.apply(waitForStartTransactionApply);
+
+ // Run tasks that require startTransaction to already be applied
+ for (Consumer<SurfaceControl.Transaction> postStartTransactionCallback :
+ postStartTransactionCallbacks) {
+ final SurfaceControl.Transaction t = mTransactionPool.acquire();
+ postStartTransactionCallback.accept(t);
+ t.apply();
+ mTransactionPool.release(t);
+ }
+
mRotator.cleanUp(finishTransaction);
TransitionMetrics.getInstance().reportAnimationStart(transition);
// run finish now in-case there are no animations
@@ -451,6 +501,117 @@
return true;
}
+ private void edgeExtendWindow(TransitionInfo.Change change,
+ Animation a, SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final Transformation transformationAtStart = new Transformation();
+ a.getTransformationAt(0, transformationAtStart);
+ final Transformation transformationAtEnd = new Transformation();
+ a.getTransformationAt(1, transformationAtEnd);
+
+ // We want to create an extension surface that is the maximal size and the animation will
+ // take care of cropping any part that overflows.
+ final Insets maxExtensionInsets = Insets.min(
+ transformationAtStart.getInsets(), transformationAtEnd.getInsets());
+
+ final int targetSurfaceHeight = Math.max(change.getStartAbsBounds().height(),
+ change.getEndAbsBounds().height());
+ final int targetSurfaceWidth = Math.max(change.getStartAbsBounds().width(),
+ change.getEndAbsBounds().width());
+ if (maxExtensionInsets.left < 0) {
+ final Rect edgeBounds = new Rect(0, 0, 1, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.left, targetSurfaceHeight);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Left Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.top < 0) {
+ final Rect edgeBounds = new Rect(0, 0, targetSurfaceWidth, 1);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.top);
+ final int xPos = 0;
+ final int yPos = maxExtensionInsets.top;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Top Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.right < 0) {
+ final Rect edgeBounds = new Rect(targetSurfaceWidth - 1, 0,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ -maxExtensionInsets.right, targetSurfaceHeight);
+ final int xPos = targetSurfaceWidth;
+ final int yPos = 0;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Right Edge Extension", startTransaction, finishTransaction);
+ }
+
+ if (maxExtensionInsets.bottom < 0) {
+ final Rect edgeBounds = new Rect(0, targetSurfaceHeight - 1,
+ targetSurfaceWidth, targetSurfaceHeight);
+ final Rect extensionRect = new Rect(0, 0,
+ targetSurfaceWidth, -maxExtensionInsets.bottom);
+ final int xPos = maxExtensionInsets.left;
+ final int yPos = targetSurfaceHeight;
+ createExtensionSurface(change.getLeash(), edgeBounds, extensionRect, xPos, yPos,
+ "Bottom Edge Extension", startTransaction, finishTransaction);
+ }
+ }
+
+ private SurfaceControl createExtensionSurface(SurfaceControl surfaceToExtend, Rect edgeBounds,
+ Rect extensionRect, int xPos, int yPos, String layerName,
+ SurfaceControl.Transaction startTransaction,
+ SurfaceControl.Transaction finishTransaction) {
+ final SurfaceControl edgeExtensionLayer = new SurfaceControl.Builder()
+ .setName(layerName)
+ .setParent(surfaceToExtend)
+ .setHidden(true)
+ .setCallsite("DefaultTransitionHandler#startAnimation")
+ .setOpaque(true)
+ .setBufferSize(extensionRect.width(), extensionRect.height())
+ .build();
+
+ SurfaceControl.LayerCaptureArgs captureArgs =
+ new SurfaceControl.LayerCaptureArgs.Builder(surfaceToExtend)
+ .setSourceCrop(edgeBounds)
+ .setFrameScale(1)
+ .setPixelFormat(PixelFormat.RGBA_8888)
+ .setChildrenOnly(true)
+ .setAllowProtected(true)
+ .build();
+ final SurfaceControl.ScreenshotHardwareBuffer edgeBuffer =
+ SurfaceControl.captureLayers(captureArgs);
+
+ if (edgeBuffer == null) {
+ ProtoLog.e(ShellProtoLogGroup.WM_SHELL_TRANSITIONS,
+ "Failed to capture edge of window.");
+ return null;
+ }
+
+ android.graphics.BitmapShader shader =
+ new android.graphics.BitmapShader(edgeBuffer.asBitmap(),
+ android.graphics.Shader.TileMode.CLAMP,
+ android.graphics.Shader.TileMode.CLAMP);
+ final Paint paint = new Paint();
+ paint.setShader(shader);
+
+ final Surface surface = new Surface(edgeExtensionLayer);
+ Canvas c = surface.lockHardwareCanvas();
+ c.drawRect(extensionRect, paint);
+ surface.unlockCanvasAndPost(c);
+ surface.release();
+
+ startTransaction.setLayer(edgeExtensionLayer, Integer.MIN_VALUE);
+ startTransaction.setPosition(edgeExtensionLayer, xPos, yPos);
+ startTransaction.setVisibility(edgeExtensionLayer, true);
+ finishTransaction.remove(edgeExtensionLayer);
+
+ return edgeExtensionLayer;
+ }
+
private void addBackgroundToTransition(
@NonNull SurfaceControl rootLeash,
@ColorInt int color,
@@ -778,9 +939,17 @@
}
t.setMatrix(leash, transformation.getMatrix(), matrix);
t.setAlpha(leash, transformation.getAlpha());
+
+ Insets extensionInsets = Insets.min(transformation.getInsets(), Insets.NONE);
+ if (!extensionInsets.equals(Insets.NONE) && clipRect != null && !clipRect.isEmpty()) {
+ // Clip out any overflowing edge extension
+ clipRect.inset(extensionInsets);
+ t.setCrop(leash, clipRect);
+ }
+
if (anim.hasRoundedCorners() && cornerRadius > 0 && clipRect != null) {
// We can only apply rounded corner if a crop is set
- t.setWindowCrop(leash, clipRect);
+ t.setCrop(leash, clipRect);
t.setCornerRadius(leash, cornerRadius);
}
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..e0ce081 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetManager.java
@@ -320,15 +320,15 @@
}
private static final class InternalNetworkManagementListener
- extends IInternalNetworkManagementListener.Stub {
+ extends IEthernetNetworkManagementListener.Stub {
@NonNull
private final Executor mExecutor;
@NonNull
- private final BiConsumer<Network, InternalNetworkManagementException> mListener;
+ private final BiConsumer<Network, EthernetNetworkManagementException> mListener;
InternalNetworkManagementListener(
@NonNull final Executor executor,
- @NonNull final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @NonNull final BiConsumer<Network, EthernetNetworkManagementException> listener) {
Objects.requireNonNull(executor, "Pass a non-null executor");
Objects.requireNonNull(listener, "Pass a non-null listener");
mExecutor = executor;
@@ -338,14 +338,14 @@
@Override
public void onComplete(
@Nullable final Network network,
- @Nullable final InternalNetworkManagementException e) {
+ @Nullable final EthernetNetworkManagementException e) {
mExecutor.execute(() -> mListener.accept(network, e));
}
}
private InternalNetworkManagementListener getInternalNetworkManagementListener(
@Nullable final Executor executor,
- @Nullable final BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable final BiConsumer<Network, EthernetNetworkManagementException> listener) {
if (null != listener) {
Objects.requireNonNull(executor, "Pass a non-null executor, or a null listener");
}
@@ -358,11 +358,12 @@
return proxy;
}
+ @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
private void updateConfiguration(
@NonNull String iface,
- @NonNull InternalNetworkUpdateRequest request,
+ @NonNull EthernetNetworkUpdateRequest request,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -372,10 +373,11 @@
}
}
+ @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
private void connectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
@@ -385,10 +387,11 @@
}
}
+ @RequiresPermission(android.Manifest.permission.MANAGE_ETHERNET_NETWORKS)
private void disconnectNetwork(
@NonNull String iface,
@Nullable @CallbackExecutor Executor executor,
- @Nullable BiConsumer<Network, InternalNetworkManagementException> listener) {
+ @Nullable BiConsumer<Network, EthernetNetworkManagementException> listener) {
final InternalNetworkManagementListener proxy = getInternalNetworkManagementListener(
executor, listener);
try {
diff --git a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
similarity index 93%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
index dcce706..adf9e5a 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkManagementException.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.aidl
@@ -16,4 +16,4 @@
package android.net;
- parcelable InternalNetworkManagementException;
\ No newline at end of file
+ parcelable EthernetNetworkManagementException;
\ No newline at end of file
diff --git a/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
new file mode 100644
index 0000000..a35f28e
--- /dev/null
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkManagementException.java
@@ -0,0 +1,71 @@
+/*
+ * 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;
+
+import java.util.Objects;
+
+/** @hide */
+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 80%
rename from packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
rename to packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
index f42c4b7..4d229d2 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/InternalNetworkUpdateRequest.java
+++ b/packages/ConnectivityT/framework-t/src/android/net/EthernetNetworkUpdateRequest.java
@@ -23,7 +23,7 @@
import java.util.Objects;
/** @hide */
-public final class InternalNetworkUpdateRequest implements Parcelable {
+public final class EthernetNetworkUpdateRequest implements Parcelable {
@NonNull
private final StaticIpConfiguration mIpConfig;
@NonNull
@@ -40,7 +40,7 @@
}
/** @hide */
- public InternalNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
+ public EthernetNetworkUpdateRequest(@NonNull final StaticIpConfiguration ipConfig,
@NonNull final NetworkCapabilities networkCapabilities) {
Objects.requireNonNull(ipConfig);
Objects.requireNonNull(networkCapabilities);
@@ -48,7 +48,7 @@
mNetworkCapabilities = new NetworkCapabilities(networkCapabilities);
}
- private InternalNetworkUpdateRequest(@NonNull final Parcel source) {
+ private EthernetNetworkUpdateRequest(@NonNull final Parcel source) {
Objects.requireNonNull(source);
mIpConfig = StaticIpConfiguration.CREATOR.createFromParcel(source);
mNetworkCapabilities = NetworkCapabilities.CREATOR.createFromParcel(source);
@@ -56,7 +56,7 @@
@Override
public String toString() {
- return "InternalNetworkUpdateRequest{"
+ return "EthernetNetworkUpdateRequest{"
+ "mIpConfig=" + mIpConfig
+ ", mNetworkCapabilities=" + mNetworkCapabilities + '}';
}
@@ -65,7 +65,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- InternalNetworkUpdateRequest that = (InternalNetworkUpdateRequest) o;
+ EthernetNetworkUpdateRequest that = (EthernetNetworkUpdateRequest) o;
return Objects.equals(that.getIpConfig(), mIpConfig)
&& Objects.equals(that.getNetworkCapabilities(), mNetworkCapabilities);
@@ -88,16 +88,16 @@
}
@NonNull
- public static final Parcelable.Creator<InternalNetworkUpdateRequest> CREATOR =
- new Parcelable.Creator<InternalNetworkUpdateRequest>() {
+ public static final Parcelable.Creator<EthernetNetworkUpdateRequest> CREATOR =
+ new Parcelable.Creator<EthernetNetworkUpdateRequest>() {
@Override
- public InternalNetworkUpdateRequest[] newArray(int size) {
- return new InternalNetworkUpdateRequest[size];
+ public EthernetNetworkUpdateRequest[] newArray(int size) {
+ return new EthernetNetworkUpdateRequest[size];
}
@Override
- public InternalNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
- return new InternalNetworkUpdateRequest(source);
+ public EthernetNetworkUpdateRequest createFromParcel(@NonNull Parcel source) {
+ return new EthernetNetworkUpdateRequest(source);
}
};
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
index e688bea..544d02b 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetManager.aidl
@@ -18,8 +18,8 @@
import android.net.IpConfiguration;
import android.net.IEthernetServiceListener;
-import android.net.IInternalNetworkManagementListener;
-import android.net.InternalNetworkUpdateRequest;
+import android.net.IEthernetNetworkManagementListener;
+import android.net.EthernetNetworkUpdateRequest;
import android.net.ITetheredInterfaceCallback;
/**
@@ -38,8 +38,8 @@
void setIncludeTestInterfaces(boolean include);
void requestTetheredInterface(in ITetheredInterfaceCallback callback);
void releaseTetheredInterface(in ITetheredInterfaceCallback callback);
- void updateConfiguration(String iface, in InternalNetworkUpdateRequest request,
- in IInternalNetworkManagementListener listener);
- void connectNetwork(String iface, in IInternalNetworkManagementListener listener);
- void disconnectNetwork(String iface, in IInternalNetworkManagementListener listener);
+ void updateConfiguration(String iface, in EthernetNetworkUpdateRequest request,
+ in IEthernetNetworkManagementListener listener);
+ void connectNetwork(String iface, in IEthernetNetworkManagementListener listener);
+ void disconnectNetwork(String iface, in IEthernetNetworkManagementListener listener);
}
diff --git a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
similarity index 80%
rename from packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
rename to packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
index 69cde3b..93edccf 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/IInternalNetworkManagementListener.aidl
+++ b/packages/ConnectivityT/framework-t/src/android/net/IEthernetNetworkManagementListener.aidl
@@ -16,10 +16,10 @@
package android.net;
-import android.net.InternalNetworkManagementException;
+import android.net.EthernetNetworkManagementException;
import android.net.Network;
/** @hide */
-oneway interface IInternalNetworkManagementListener {
- void onComplete(in Network network, in InternalNetworkManagementException exception);
+oneway interface IEthernetNetworkManagementListener {
+ void onComplete(in Network network, in EthernetNetworkManagementException exception);
}
\ No newline at end of file
diff --git a/packages/ConnectivityT/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 2e6a58f..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" />
@@ -213,6 +214,9 @@
<!-- DevicePolicyManager get user restrictions -->
<uses-permission android:name="android.permission.MANAGE_PROFILE_AND_DEVICE_OWNERS" />
+ <!-- DevicePolicyManager get admin policy -->
+ <uses-permission android:name="android.permission.QUERY_ADMIN_POLICY" />
+
<!-- TV picture-in-picture -->
<uses-permission android:name="android.permission.RECEIVE_MEDIA_RESOURCE_USAGE" />
@@ -303,6 +307,10 @@
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<uses-permission android:name="android.permission.SET_CLIP_SOURCE" />
+ <!-- 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" />
<protected-broadcast android:name="com.android.settings.flashlight.action.FLASHLIGHT_CHANGED" />
@@ -486,6 +494,16 @@
android:excludeFromRecents="true">
</activity>
+ <!-- started from HdmiCecLocalDevicePlayback -->
+ <activity android:name=".hdmi.HdmiCecSetMenuLanguageActivity"
+ android:exported="true"
+ android:launchMode="singleTop"
+ android:permission="android.permission.CHANGE_CONFIGURATION"
+ android:theme="@style/BottomSheet"
+ android:finishOnCloseSystemDialogs="true"
+ android:excludeFromRecents="true">
+ </activity>
+
<!-- started from SensoryPrivacyService -->
<activity android:name=".sensorprivacy.SensorUseStartedActivity"
android:exported="true"
@@ -856,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
new file mode 100644
index 0000000..b6f516f
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_date.xml
@@ -0,0 +1,30 @@
+<?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.
+-->
+<TextClock
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ 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"
+ android:shadowRadius="?attr/shadowRadius"
+ android:format12Hour="EEE, MMM d"
+ android:format24Hour="EEE, MMM d"
+ android:textSize="@dimen/dream_overlay_complication_clock_date_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
new file mode 100644
index 0000000..82c8d5f
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_clock_time.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<TextClock
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ 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="@font/clock"
+ android:textColor="@android:color/white"
+ android:format12Hour="h:mm"
+ android:format24Hour="kk:mm"
+ android:shadowColor="@color/keyguard_shadow_color"
+ android:shadowRadius="?attr/shadowRadius"
+ android:textSize="@dimen/dream_overlay_complication_clock_time_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
new file mode 100644
index 0000000..08f0d67
--- /dev/null
+++ b/packages/SystemUI/res/layout/dream_overlay_complication_weather.xml
@@ -0,0 +1,27 @@
+<?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.
+-->
+<TextView
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ 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"
+ android:textSize="@dimen/dream_overlay_complication_weather_text_size"/>
diff --git a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
index 2d565a1..f4eb32f 100644
--- a/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_complications_layer.xml
@@ -20,34 +20,6 @@
android:id="@+id/dream_overlay_complications_layer"
android:layout_width="match_parent"
android:layout_height="match_parent">
- <TextClock
- android:id="@+id/time_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:fontFamily="sans-serif-thin"
- android:format12Hour="h:mm"
- android:format24Hour="kk:mm"
- android:shadowColor="#B2000000"
- android:shadowRadius="2.0"
- android:singleLine="true"
- android:textSize="72sp"
- android:textColor="@android:color/white"
- app:layout_constraintBottom_toTopOf="@+id/date_view"
- app:layout_constraintStart_toStartOf="parent" />
- <TextClock
- android:id="@+id/date_view"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:shadowColor="#B2000000"
- android:shadowRadius="2.0"
- android:format12Hour="EEE, MMM d"
- android:format24Hour="EEE, MMM d"
- android:singleLine="true"
- android:textSize="18sp"
- android:textColor="@android:color/white"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="@+id/time_view"
- app:layout_constraintStart_toStartOf="@+id/time_view" />
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
diff --git a/packages/SystemUI/res/layout/dream_overlay_container.xml b/packages/SystemUI/res/layout/dream_overlay_container.xml
index 4929f50..3c2183d 100644
--- a/packages/SystemUI/res/layout/dream_overlay_container.xml
+++ b/packages/SystemUI/res/layout/dream_overlay_container.xml
@@ -25,7 +25,7 @@
android:id="@+id/dream_overlay_content"
android:layout_width="match_parent"
android:layout_height="0dp"
- app:layout_constraintTop_toTopOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/dream_overlay_status_bar"
app:layout_constraintBottom_toBottomOf="parent" />
<com.android.systemui.dreams.DreamOverlayStatusBarView
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/status_bar_expanded.xml b/packages/SystemUI/res/layout/status_bar_expanded.xml
index 2290964..39d7f4f 100644
--- a/packages/SystemUI/res/layout/status_bar_expanded.xml
+++ b/packages/SystemUI/res/layout/status_bar_expanded.xml
@@ -122,7 +122,7 @@
android:layout_marginTop="@dimen/notification_panel_margin_top"
android:layout_width="@dimen/notification_panel_width"
android:layout_height="match_parent"
- android:layout_marginBottom="@dimen/close_handle_underlap"
+ android:layout_marginBottom="@dimen/notification_panel_margin_bottom"
android:importantForAccessibility="no"
systemui:layout_constraintStart_toStartOf="parent"
systemui:layout_constraintEnd_toEndOf="parent"
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-land/dimens.xml b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
index 89d046b..c2cec52 100644
--- a/packages/SystemUI/res/values-sw600dp-land/dimens.xml
+++ b/packages/SystemUI/res/values-sw600dp-land/dimens.xml
@@ -26,6 +26,10 @@
keyguard_split_shade_top_margin - status_bar_header_height_keyguard = 8dp -->
<dimen name="keyguard_clock_top_margin">8dp</dimen>
+ <dimen name="split_shade_notifications_scrim_margin_bottom">16dp</dimen>
+
+ <dimen name="notification_panel_margin_bottom">48dp</dimen>
+
<!-- Limit the TaskView to this percentage of the overall screen width (0.0 - 1.0) -->
<item name="controls_task_view_width_percentage" translatable="false" format="float" type="dimen">0.45</item>
<dimen name="controls_task_view_right_margin">8dp</dimen>
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/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
similarity index 62%
copy from packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
copy to packages/SystemUI/res/values-sw720dp-land/dimens.xml
index cb602d79..ae557c4 100644
--- a/packages/ConnectivityT/framework-t/src/android/net/NetworkStateSnapshot.aidl
+++ b/packages/SystemUI/res/values-sw720dp-land/dimens.xml
@@ -1,5 +1,7 @@
-/**
- * Copyright (c) 2021, The Android Open Source Project
+<?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.
@@ -12,8 +14,10 @@
* 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 NetworkStateSnapshot;
+*/
+-->
+<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 800dd0a..fdb5631 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 -->
@@ -311,9 +311,6 @@
<!-- Move the back button drawable for 3 button layout upwards in ime mode and in portrait -->
<dimen name="navbar_back_button_ime_offset">2dp</dimen>
- <!-- Amount of close_handle that will NOT overlap the notification list -->
- <dimen name="close_handle_underlap">32dp</dimen>
-
<!-- Height of the status bar header bar in the car setting. -->
<dimen name="car_status_bar_header_height">128dp</dimen>
@@ -330,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>
@@ -376,8 +373,12 @@
-->
<dimen name="nssl_split_shade_min_content_height">256dp</dimen>
- <!-- The bottom margin of the panel that holds the list of notifications. -->
- <dimen name="notification_panel_margin_bottom">0dp</dimen>
+ <dimen name="notification_panel_margin_bottom">32dp</dimen>
+
+ <!-- The bottom padding of the panel that holds the list of notifications. -->
+ <dimen name="notification_panel_padding_bottom">0dp</dimen>
+
+ <dimen name="split_shade_notifications_scrim_margin_bottom">0dp</dimen>
<dimen name="notification_panel_width">@dimen/match_parent</dimen>
@@ -490,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>
@@ -989,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>
@@ -1345,6 +1348,16 @@
shade. -->
<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
if their end is aligned with the parent end. Represented as the percentage over from the
start of the parent container. -->
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 75ae52c..e5cabb0 100644
--- a/packages/SystemUI/res/values/strings.xml
+++ b/packages/SystemUI/res/values/strings.xml
@@ -123,6 +123,18 @@
<!-- Message of notification shown when trying to enable USB debugging but a secondary user is the current foreground user. -->
<string name="usb_debugging_secondary_user_message">The user currently signed in to this device can\'t turn on USB debugging. To use this feature, switch to the primary user.</string>
+ <!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=80] -->
+ <string name="hdmi_cec_set_menu_language_title">Do you want to change the system language to <xliff:g id="language" example="German">%1$s</xliff:g>?</string>
+
+ <!-- Description for the <Set Menu Language> confirmation dialog [CHAR LIMIT=NONE] -->
+ <string name="hdmi_cec_set_menu_language_description">System language change requested by another device</string>
+
+ <!-- Button label for accepting language change [CHAR LIMIT=25] -->
+ <string name="hdmi_cec_set_menu_language_accept">Change language</string>
+
+ <!-- Button label for declining language change [CHAR LIMIT=25] -->
+ <string name="hdmi_cec_set_menu_language_decline">Keep current language</string>
+
<!-- Title of confirmation dialog for wireless debugging [CHAR LIMIT=NONE] -->
<string name="wifi_debugging_title">Allow wireless debugging on this network?</string>
@@ -2367,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/Dependency.java b/packages/SystemUI/src/com/android/systemui/Dependency.java
index 08ed24c..b32c2b6 100644
--- a/packages/SystemUI/src/com/android/systemui/Dependency.java
+++ b/packages/SystemUI/src/com/android/systemui/Dependency.java
@@ -49,6 +49,7 @@
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.fragments.FragmentService;
+import com.android.systemui.hdmi.HdmiCecSetMenuLanguageHelper;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.media.dialog.MediaOutputDialogFactory;
@@ -250,6 +251,7 @@
@Inject Lazy<LocationController> mLocationController;
@Inject Lazy<RotationLockController> mRotationLockController;
@Inject Lazy<ZenModeController> mZenModeController;
+ @Inject Lazy<HdmiCecSetMenuLanguageHelper> mHdmiCecSetMenuLanguageHelper;
@Inject Lazy<HotspotController> mHotspotController;
@Inject Lazy<CastController> mCastController;
@Inject Lazy<FlashlightController> mFlashlightController;
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/dagger/DefaultActivityBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
index bce8784..1653e0a 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/DefaultActivityBinder.java
@@ -19,6 +19,7 @@
import android.app.Activity;
import com.android.systemui.ForegroundServicesDialog;
+import com.android.systemui.hdmi.HdmiCecSetMenuLanguageActivity;
import com.android.systemui.keyguard.WorkLockActivity;
import com.android.systemui.people.PeopleSpaceActivity;
import com.android.systemui.people.widget.LaunchConversationActivity;
@@ -120,4 +121,11 @@
@IntoMap
@ClassKey(TvUnblockSensorActivity.class)
public abstract Activity bindTvUnblockSensorActivity(TvUnblockSensorActivity activity);
+
+ /** Inject into HdmiCecSetMenuLanguageActivity. */
+ @Binds
+ @IntoMap
+ @ClassKey(HdmiCecSetMenuLanguageActivity.class)
+ public abstract Activity bindHdmiCecSetMenuLanguageActivity(
+ HdmiCecSetMenuLanguageActivity activity);
}
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
index 41287b6..ec2beb1 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIBinder.java
@@ -27,6 +27,9 @@
import com.android.systemui.clipboardoverlay.ClipboardListener;
import com.android.systemui.dreams.DreamOverlayRegistrant;
import com.android.systemui.dreams.SmartSpaceComplication;
+import com.android.systemui.dreams.complication.DreamClockDateComplication;
+import com.android.systemui.dreams.complication.DreamClockTimeComplication;
+import com.android.systemui.dreams.complication.DreamWeatherComplication;
import com.android.systemui.globalactions.GlobalActionsComponent;
import com.android.systemui.keyguard.KeyguardViewMediator;
import com.android.systemui.keyguard.dagger.KeyguardModule;
@@ -234,4 +237,25 @@
@ClassKey(MediaDreamSentinel.class)
public abstract CoreStartable bindMediaDreamSentinel(
MediaDreamSentinel sentinel);
+
+ /** Inject into DreamClockTimeComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamClockTimeComplication.Registrant.class)
+ public abstract CoreStartable bindDreamClockTimeComplicationRegistrant(
+ DreamClockTimeComplication.Registrant registrant);
+
+ /** Inject into DreamClockDateComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamClockDateComplication.Registrant.class)
+ public abstract CoreStartable bindDreamClockDateComplicationRegistrant(
+ DreamClockDateComplication.Registrant registrant);
+
+ /** Inject into DreamWeatherComplication.Registrant */
+ @Binds
+ @IntoMap
+ @ClassKey(DreamWeatherComplication.Registrant.class)
+ public abstract CoreStartable bindDreamWeatherComplicationRegistrant(
+ DreamWeatherComplication.Registrant registrant);
}
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/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
new file mode 100644
index 0000000..6861c74
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockDateComplication.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent.DreamClockDateComplicationModule.DREAM_CLOCK_DATE_COMPLICATION_VIEW;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamClockDateComplicationComponent;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Clock Date Complication that produce Clock Date view holder.
+ */
+public class DreamClockDateComplication implements Complication {
+ DreamClockDateComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamClockDateComplication}.
+ */
+ @Inject
+ public DreamClockDateComplication(
+ DreamClockDateComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_DATE;
+ }
+
+ /**
+ * Create {@link DreamClockDateViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} responsbile for registering {@link DreamClockDateComplication} with
+ * SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamClockDateComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamClockDateComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamClockDateComplication dreamClockDateComplication) {
+ super(context);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamClockDateComplication;
+ }
+
+ @Override
+ public void start() {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Clock Date Complication View.
+ */
+ public static class DreamClockDateViewHolder implements ViewHolder {
+ private final View mView;
+ private final ComplicationLayoutParams mLayoutParams;
+
+ @Inject
+ DreamClockDateViewHolder(@Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW) View view,
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
new file mode 100644
index 0000000..936767a
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamClockTimeComplication.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.DREAM_CLOCK_TIME_COMPLICATION_VIEW;
+
+import android.content.Context;
+import android.view.View;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamClockTimeComplicationComponent;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Clock Time Complication that produce Clock Time view holder.
+ */
+public class DreamClockTimeComplication implements Complication {
+ DreamClockTimeComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamClockTimeComplication}.
+ */
+ @Inject
+ public DreamClockTimeComplication(
+ DreamClockTimeComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_TIME;
+ }
+
+ /**
+ * Create {@link DreamClockTimeViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} responsbile for registering {@link DreamClockTimeComplication} with
+ * SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamClockTimeComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamClockTimeComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamClockTimeComplication dreamClockTimeComplication) {
+ super(context);
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamClockTimeComplication;
+ }
+
+ @Override
+ public void start() {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Clock Time Complication View.
+ */
+ public static class DreamClockTimeViewHolder implements ViewHolder {
+ private final View mView;
+ private final ComplicationLayoutParams mLayoutParams;
+
+ @Inject
+ DreamClockTimeViewHolder(@Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW) View view,
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ }
+
+ @Override
+ public View getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.java
new file mode 100644
index 0000000..f5c5a43
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/DreamWeatherComplication.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.systemui.dreams.complication;
+
+import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS;
+import static com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent.DreamWeatherComplicationModule.DREAM_WEATHER_COMPLICATION_VIEW;
+
+import android.app.smartspace.SmartspaceAction;
+import android.app.smartspace.SmartspaceTarget;
+import android.content.Context;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.text.TextUtils;
+import android.widget.TextView;
+
+import com.android.systemui.CoreStartable;
+import com.android.systemui.R;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.dreams.complication.dagger.DreamWeatherComplicationComponent;
+import com.android.systemui.plugins.BcSmartspaceDataPlugin.SmartspaceTargetListener;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+import com.android.systemui.util.ViewController;
+
+import javax.inject.Inject;
+import javax.inject.Named;
+
+/**
+ * Weather Complication that produce Weather view holder.
+ */
+public class DreamWeatherComplication implements Complication {
+ DreamWeatherComplicationComponent.Factory mComponentFactory;
+
+ /**
+ * Default constructor for {@link DreamWeatherComplication}.
+ */
+ @Inject
+ public DreamWeatherComplication(
+ DreamWeatherComplicationComponent.Factory componentFactory) {
+ mComponentFactory = componentFactory;
+ }
+
+ @Override
+ public int getRequiredTypeAvailability() {
+ return COMPLICATION_TYPE_WEATHER;
+ }
+
+ /**
+ * Create {@link DreamWeatherViewHolder}.
+ */
+ @Override
+ public ViewHolder createView(ComplicationViewModel model) {
+ return mComponentFactory.create().getViewHolder();
+ }
+
+ /**
+ * {@link CoreStartable} for registering {@link DreamWeatherComplication} with SystemUI.
+ */
+ public static class Registrant extends CoreStartable {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private final DreamOverlayStateController mDreamOverlayStateController;
+ private final DreamWeatherComplication mComplication;
+
+ /**
+ * Default constructor to register {@link DreamWeatherComplication}.
+ */
+ @Inject
+ public Registrant(Context context,
+ LockscreenSmartspaceController smartspaceController,
+ DreamOverlayStateController dreamOverlayStateController,
+ DreamWeatherComplication dreamWeatherComplication) {
+ super(context);
+ mSmartSpaceController = smartspaceController;
+ mDreamOverlayStateController = dreamOverlayStateController;
+ mComplication = dreamWeatherComplication;
+ }
+
+ @Override
+ public void start() {
+ if (mSmartSpaceController.isEnabled()) {
+ mDreamOverlayStateController.addComplication(mComplication);
+ }
+ }
+ }
+
+ /**
+ * ViewHolder to contain value/logic associated with a Weather Complication View.
+ */
+ public static class DreamWeatherViewHolder implements ViewHolder {
+ private final TextView mView;
+ private final ComplicationLayoutParams mLayoutParams;
+ private final DreamWeatherViewController mViewController;
+
+ @Inject
+ DreamWeatherViewHolder(
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
+ DreamWeatherViewController controller,
+ @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
+ ComplicationLayoutParams layoutParams) {
+ mView = view;
+ mLayoutParams = layoutParams;
+ mViewController = controller;
+ mViewController.init();
+ }
+
+ @Override
+ public TextView getView() {
+ return mView;
+ }
+
+ @Override
+ public ComplicationLayoutParams getLayoutParams() {
+ return mLayoutParams;
+ }
+ }
+
+ /**
+ * ViewController to contain value/logic associated with a Weather Complication View.
+ */
+ static class DreamWeatherViewController extends ViewController<TextView> {
+ private final LockscreenSmartspaceController mSmartSpaceController;
+ private SmartspaceTargetListener mSmartspaceTargetListener;
+
+ @Inject
+ DreamWeatherViewController(
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW) TextView view,
+ LockscreenSmartspaceController smartspaceController
+ ) {
+ super(view);
+ mSmartSpaceController = smartspaceController;
+ }
+
+ @Override
+ protected void onViewAttached() {
+ mSmartspaceTargetListener = targets -> targets.forEach(
+ t -> {
+ if (t instanceof SmartspaceTarget
+ && ((SmartspaceTarget) t).getFeatureType()
+ == SmartspaceTarget.FEATURE_WEATHER) {
+ final SmartspaceTarget target = (SmartspaceTarget) t;
+ final SmartspaceAction headerAction = target.getHeaderAction();
+ if (headerAction == null || TextUtils.isEmpty(
+ headerAction.getTitle())) {
+ return;
+ }
+
+ String temperature = headerAction.getTitle().toString();
+ mView.setText(temperature);
+ final Icon icon = headerAction.getIcon();
+ if (icon != null) {
+ final int iconSize =
+ getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_size);
+ final Drawable iconDrawable = icon.loadDrawable(getContext());
+ iconDrawable.setBounds(0, 0, iconSize, iconSize);
+ mView.setCompoundDrawables(iconDrawable, null, null, null);
+ mView.setCompoundDrawablePadding(
+ getResources().getDimensionPixelSize(
+ R.dimen.smart_action_button_icon_padding));
+
+ }
+ }
+ });
+ mSmartSpaceController.addListener(mSmartspaceTargetListener);
+ }
+
+ @Override
+ protected void onViewDetached() {
+ mSmartSpaceController.removeListener(mSmartspaceTargetListener);
+ }
+ }
+}
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
new file mode 100644
index 0000000..eaffb1c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockDateComplicationComponent.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamClockDateComplication.DreamClockDateViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamClockDateComplicationComponent} is responsible for generating dependencies
+ * surrounding the
+ * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamClockDateComplicationComponent.DreamClockDateComplicationModule.class,
+})
+@DreamClockDateComplicationComponent.DreamClockDateComplicationScope
+public interface DreamClockDateComplicationComponent {
+ /**
+ * Creates {@link DreamClockDateViewHolder}.
+ */
+ DreamClockDateViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamClockDateComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamClockDateComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamClockDateComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamClockDateComplicationComponent}.
+ */
+ @Module
+ interface DreamClockDateComplicationModule {
+ String DREAM_CLOCK_DATE_COMPLICATION_VIEW = "clock_date_complication_view";
+ String DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS =
+ "clock_date_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 2;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamClockDateComplicationScope
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_VIEW)
+ static View provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull(
+ layoutInflater.inflate(R.layout.dream_overlay_complication_clock_date,
+ null, false),
+ "R.layout.dream_overlay_complication_clock_date did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamClockDateComplicationScope
+ @Named(DREAM_CLOCK_DATE_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
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
new file mode 100644
index 0000000..d539f5c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamClockTimeComplicationComponent.java
@@ -0,0 +1,116 @@
+/*
+ * 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.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+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;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamClockTimeComplication.DreamClockTimeViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamClockTimeComplicationComponent} is responsible for generating dependencies
+ * surrounding the
+ * Clock Time {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamClockTimeComplicationComponent.DreamClockTimeComplicationModule.class,
+})
+@DreamClockTimeComplicationComponent.DreamClockTimeComplicationScope
+public interface DreamClockTimeComplicationComponent {
+ /**
+ * Creates {@link DreamClockTimeViewHolder}.
+ */
+ DreamClockTimeViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamClockTimeComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamClockTimeComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamClockTimeComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamClockTimeComplicationComponent}.
+ */
+ @Module
+ interface DreamClockTimeComplicationModule {
+ String DREAM_CLOCK_TIME_COMPLICATION_VIEW = "clock_time_complication_view";
+ String DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS =
+ "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.
+ */
+ @Provides
+ @DreamClockTimeComplicationScope
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_VIEW)
+ static View provideComplicationView(LayoutInflater layoutInflater) {
+ 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;
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamClockTimeComplicationScope
+ @Named(DREAM_CLOCK_TIME_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_UP,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
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
new file mode 100644
index 0000000..a282594
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/DreamWeatherComplicationComponent.java
@@ -0,0 +1,111 @@
+/*
+ * 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.dreams.complication.dagger;
+
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import com.android.internal.util.Preconditions;
+import com.android.systemui.R;
+import com.android.systemui.dreams.complication.ComplicationLayoutParams;
+import com.android.systemui.dreams.complication.DreamWeatherComplication.DreamWeatherViewHolder;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+
+import javax.inject.Named;
+import javax.inject.Scope;
+
+import dagger.Module;
+import dagger.Provides;
+import dagger.Subcomponent;
+
+/**
+ * {@link DreamWeatherComplicationComponent} is responsible for generating dependencies surrounding
+ * the
+ * Clock Date {@link com.android.systemui.dreams.complication.Complication}, such as the layout
+ * details.
+ */
+@Subcomponent(modules = {
+ DreamWeatherComplicationComponent.DreamWeatherComplicationModule.class,
+})
+@DreamWeatherComplicationComponent.DreamWeatherComplicationScope
+public interface DreamWeatherComplicationComponent {
+ /**
+ * Creates {@link DreamWeatherViewHolder}.
+ */
+ DreamWeatherViewHolder getViewHolder();
+
+ @Documented
+ @Retention(RUNTIME)
+ @Scope
+ @interface DreamWeatherComplicationScope {
+ }
+
+ /**
+ * Generates {@link DreamWeatherComplicationComponent}.
+ */
+ @Subcomponent.Factory
+ interface Factory {
+ DreamWeatherComplicationComponent create();
+ }
+
+ /**
+ * Scoped values for {@link DreamWeatherComplicationComponent}.
+ */
+ @Module
+ interface DreamWeatherComplicationModule {
+ String DREAM_WEATHER_COMPLICATION_VIEW = "weather_complication_view";
+ String DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS =
+ "weather_complication_layout_params";
+ // Order weight of insert into parent container
+ int INSERT_ORDER_WEIGHT = 1;
+
+ /**
+ * Provides the complication view.
+ */
+ @Provides
+ @DreamWeatherComplicationScope
+ @Named(DREAM_WEATHER_COMPLICATION_VIEW)
+ static TextView provideComplicationView(LayoutInflater layoutInflater) {
+ return Preconditions.checkNotNull((TextView)
+ layoutInflater.inflate(R.layout.dream_overlay_complication_weather,
+ null, false),
+ "R.layout.dream_overlay_complication_weather did not properly inflated");
+ }
+
+ /**
+ * Provides the layout parameters for the complication view.
+ */
+ @Provides
+ @DreamWeatherComplicationScope
+ @Named(DREAM_WEATHER_COMPLICATION_LAYOUT_PARAMS)
+ static ComplicationLayoutParams provideLayoutParams() {
+ return new ComplicationLayoutParams(0,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ComplicationLayoutParams.POSITION_BOTTOM
+ | ComplicationLayoutParams.POSITION_START,
+ ComplicationLayoutParams.DIRECTION_END,
+ INSERT_ORDER_WEIGHT,
+ true);
+ }
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.java
new file mode 100644
index 0000000..8e4fb37
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/dreams/complication/dagger/RegisteredComplicationsModule.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.systemui.dreams.complication.dagger;
+
+import com.android.systemui.dagger.SystemUIBinder;
+
+import dagger.Module;
+
+/**
+ * Module for all components with corresponding dream layer complications registered in
+ * {@link SystemUIBinder}.
+ */
+@Module(subcomponents = {
+ DreamClockTimeComplicationComponent.class,
+ DreamClockDateComplicationComponent.class,
+ DreamWeatherComplicationComponent.class,
+})
+public interface RegisteredComplicationsModule {
+}
diff --git a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
index 3d2f924..d8af9e5 100644
--- a/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dreams/dagger/DreamModule.java
@@ -16,6 +16,7 @@
package com.android.systemui.dreams.dagger;
+import com.android.systemui.dreams.complication.dagger.RegisteredComplicationsModule;
import com.android.systemui.dreams.touch.dagger.DreamTouchModule;
import dagger.Module;
@@ -25,6 +26,7 @@
*/
@Module(includes = {
DreamTouchModule.class,
+ RegisteredComplicationsModule.class,
},
subcomponents = {
DreamOverlayComponent.class,
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 97edf3b..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,11 +107,16 @@
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 ResourceBooleanFlag STATUS_BAR_USER_SWITCHER =
+ new ResourceBooleanFlag(602, R.bool.flag_user_switcher_chip);
+
/***************************************/
// 700 - dialer/calls
public static final BooleanFlag ONGOING_CALL_STATUS_BAR_CHIP =
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/hdmi/HdmiCecSetMenuLanguageActivity.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
new file mode 100644
index 0000000..b304c3c
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageActivity.java
@@ -0,0 +1,98 @@
+/*
+ * 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.hdmi;
+
+import android.hardware.hdmi.HdmiControlManager;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.systemui.R;
+import com.android.systemui.tv.TvBottomSheetActivity;
+
+import javax.inject.Inject;
+
+/**
+ * Confirmation dialog shown when Set Menu Language CEC message was received.
+ */
+public class HdmiCecSetMenuLanguageActivity extends TvBottomSheetActivity
+ implements View.OnClickListener {
+ private static final String TAG = HdmiCecSetMenuLanguageActivity.class.getSimpleName();
+
+ private final HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
+
+ @Inject
+ public HdmiCecSetMenuLanguageActivity(
+ HdmiCecSetMenuLanguageHelper hdmiCecSetMenuLanguageHelper) {
+ mHdmiCecSetMenuLanguageHelper = hdmiCecSetMenuLanguageHelper;
+ }
+
+ @Override
+ public final void onCreate(Bundle b) {
+ super.onCreate(b);
+ getWindow().addPrivateFlags(
+ WindowManager.LayoutParams.SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ String languageTag = getIntent().getStringExtra(HdmiControlManager.EXTRA_LOCALE);
+ mHdmiCecSetMenuLanguageHelper.setLocale(languageTag);
+ if (mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()) {
+ finish();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ CharSequence title = getString(R.string.hdmi_cec_set_menu_language_title,
+ mHdmiCecSetMenuLanguageHelper.getLocale().getDisplayLanguage());
+ CharSequence text = getString(R.string.hdmi_cec_set_menu_language_description);
+ initUI(title, text);
+ }
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.bottom_sheet_positive_button) {
+ mHdmiCecSetMenuLanguageHelper.acceptLocale();
+ } else {
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ }
+ finish();
+ }
+
+ void initUI(CharSequence title, CharSequence text) {
+ TextView titleTextView = findViewById(R.id.bottom_sheet_title);
+ TextView contentTextView = findViewById(R.id.bottom_sheet_body);
+ ImageView icon = findViewById(R.id.bottom_sheet_icon);
+ ImageView secondIcon = findViewById(R.id.bottom_sheet_second_icon);
+ Button okButton = findViewById(R.id.bottom_sheet_positive_button);
+ Button cancelButton = findViewById(R.id.bottom_sheet_negative_button);
+
+ titleTextView.setText(title);
+ contentTextView.setText(text);
+ icon.setImageResource(com.android.internal.R.drawable.ic_settings_language);
+ secondIcon.setVisibility(View.GONE);
+
+ okButton.setText(R.string.hdmi_cec_set_menu_language_accept);
+ okButton.setOnClickListener(this);
+
+ cancelButton.setText(R.string.hdmi_cec_set_menu_language_decline);
+ cancelButton.setOnClickListener(this);
+ cancelButton.requestFocus();
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
new file mode 100644
index 0000000..1f58112
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelper.java
@@ -0,0 +1,97 @@
+/*
+ * 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.hdmi;
+
+import android.provider.Settings;
+
+import com.android.internal.app.LocalePicker;
+import com.android.systemui.dagger.SysUISingleton;
+import com.android.systemui.dagger.qualifiers.Background;
+import com.android.systemui.util.settings.SecureSettings;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+import javax.inject.Inject;
+
+/**
+ * Helper class to separate model and view for system language change initiated by HDMI CEC.
+ */
+@SysUISingleton
+public class HdmiCecSetMenuLanguageHelper {
+ private static final String TAG = HdmiCecSetMenuLanguageHelper.class.getSimpleName();
+ private static final String SEPARATOR = ",";
+
+ private final Executor mBackgroundExecutor;
+ private final SecureSettings mSecureSettings;
+
+ private Locale mLocale;
+ private HashSet<String> mDenylist;
+
+ @Inject
+ public HdmiCecSetMenuLanguageHelper(@Background Executor executor,
+ SecureSettings secureSettings) {
+ mBackgroundExecutor = executor;
+ mSecureSettings = secureSettings;
+ String denylist = mSecureSettings.getString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST);
+ mDenylist = new HashSet<>(denylist == null
+ ? Collections.EMPTY_SET
+ : Arrays.asList(denylist.split(SEPARATOR)));
+ }
+
+ /**
+ * Set internal locale based on given language tag.
+ */
+ public void setLocale(String languageTag) {
+ mLocale = Locale.forLanguageTag(languageTag);
+ }
+
+ /**
+ * Returns the locale from {@code <Set Menu Language>} CEC message.
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Returns whether the locale from {@code <Set Menu Language>} CEC message was already
+ * denylisted.
+ */
+ public boolean isLocaleDenylisted() {
+ return mDenylist.contains(mLocale.toLanguageTag());
+ }
+
+ /**
+ * Accepts the new locale and updates system language.
+ */
+ public void acceptLocale() {
+ mBackgroundExecutor.execute(() -> LocalePicker.updateLocale(mLocale));
+ }
+
+ /**
+ * Declines the locale and puts it on the denylist.
+ */
+ public void declineLocale() {
+ mDenylist.add(mLocale.toLanguageTag());
+ mSecureSettings.putString(Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST,
+ String.join(SEPARATOR, mDenylist));
+ }
+}
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/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index 1bef32a..4b550f2 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;
@@ -1410,7 +1410,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_ALLOW_GESTURE_IGNORING_BAR_VISIBILITY,
allowSystemGestureIgnoringBarVisibility())
.commitUpdate(mDisplayId);
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..1218fd3 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
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 9f6a81d..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;
@@ -199,11 +198,14 @@
private int mMaxTopPadding;
private int mTopPadding;
private boolean mAnimateNextTopPaddingChange;
- private int mBottomMargin;
+ private int mBottomPadding;
private int mBottomInset = 0;
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);
}
@@ -981,7 +982,7 @@
mMinTopOverScrollToEscape = res.getDimensionPixelSize(
R.dimen.min_top_overscroll_to_qs);
mStatusBarHeight = SystemBarUtils.getStatusBarHeight(mContext);
- mBottomMargin = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
+ mBottomPadding = res.getDimensionPixelSize(R.dimen.notification_panel_padding_bottom);
mMinimumPaddings = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
mQsTilePadding = res.getDimensionPixelOffset(R.dimen.qs_tile_margin_horizontal);
mSkinnyNotifsInLandscape = res.getBoolean(R.bool.config_skinnyNotifsInLandscape);
@@ -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();
@@ -2280,7 +2296,7 @@
// The topPadding can be bigger than the regular padding when qs is expanded, in that
// state the maxPanelHeight and the contentHeight should be bigger
- mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomMargin;
+ mContentHeight = height + Math.max(mIntrinsicPadding, mTopPadding) + mBottomPadding;
updateScrollability();
clampScrollPosition();
updateStackPosition();
@@ -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/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index 8f0579c..e24cd3e 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -46,7 +46,7 @@
*/
public class StackScrollAlgorithm {
- public static final float START_FRACTION = 0.3f;
+ public static final float START_FRACTION = 0.5f;
private static final String LOG_TAG = "StackScrollAlgorithm";
private final ViewGroup mHostView;
@@ -61,7 +61,7 @@
@VisibleForTesting float mHeadsUpInset;
private int mPinnedZTranslationExtra;
private float mNotificationScrimPadding;
- private int mCloseHandleUnderlapHeight;
+ private int mMarginBottom;
public StackScrollAlgorithm(
Context context,
@@ -87,7 +87,7 @@
R.dimen.heads_up_pinned_elevation);
mGapHeight = res.getDimensionPixelSize(R.dimen.notification_section_divider_height);
mNotificationScrimPadding = res.getDimensionPixelSize(R.dimen.notification_side_paddings);
- mCloseHandleUnderlapHeight = res.getDimensionPixelSize(R.dimen.close_handle_underlap);
+ mMarginBottom = res.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom);
}
/**
@@ -463,7 +463,7 @@
}
} else {
if (view instanceof EmptyShadeView) {
- float fullHeight = ambientState.getLayoutMaxHeight() + mCloseHandleUnderlapHeight
+ float fullHeight = ambientState.getLayoutMaxHeight() + mMarginBottom
- ambientState.getStackY();
viewState.yTranslation = (fullHeight - getMaxAllowedChildHeight(view)) / 2f;
} else if (view != ambientState.getTrackedHeadsUpRow()) {
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 00b54e9..b8e9875 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarView.java
@@ -16,7 +16,6 @@
package com.android.systemui.statusbar.phone;
-import static com.android.systemui.DejankUtils.whitelistIpcs;
import static com.android.systemui.ScreenDecorations.DisplayCutoutView.boundsFromDirection;
import static com.android.systemui.util.Utils.getStatusBarHeaderHeightKeyguard;
@@ -27,7 +26,6 @@
import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.os.UserManager;
import android.util.AttributeSet;
import android.util.Pair;
import android.util.TypedValue;
@@ -42,12 +40,13 @@
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;
import com.android.systemui.battery.BatteryMeterView;
import com.android.systemui.plugins.DarkIconDispatcher.DarkReceiver;
-import com.android.systemui.statusbar.window.StatusBarWindowView;
import java.io.FileDescriptor;
import java.io.PrintWriter;
@@ -70,9 +69,10 @@
private ImageView mMultiUserAvatar;
private BatteryMeterView mBatteryView;
private StatusIconContainer mStatusIconContainer;
+ private ViewGroup mUserSwitcherContainer;
private boolean mKeyguardUserSwitcherEnabled;
- private final UserManager mUserManager;
+ private boolean mKeyguardUserAvatarEnabled;
private boolean mIsPrivacyDotEnabled;
private int mSystemIconsSwitcherHiddenExpandedMargin;
@@ -99,10 +99,10 @@
*/
private int mTopClipping;
private final Rect mClipRect = new Rect(0, 0, 0, 0);
+ private boolean mIsUserSwitcherEnabled;
public KeyguardStatusBarView(Context context, AttributeSet attrs) {
super(context, attrs);
- mUserManager = UserManager.get(getContext());
}
@Override
@@ -115,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);
@@ -163,6 +168,10 @@
updateKeyguardStatusBarHeight();
}
+ public void setUserSwitcherEnabled(boolean enabled) {
+ mIsUserSwitcherEnabled = enabled;
+ }
+
private void updateKeyguardStatusBarHeight() {
MarginLayoutParams lp = (MarginLayoutParams) getLayoutParams();
lp.height = getStatusBarHeaderHeightKeyguard(mContext);
@@ -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) {
@@ -200,11 +220,7 @@
// If we have no keyguard switcher, the screen width is under 600dp. In this case,
// we only show the multi-user switch if it's enabled through UserManager as well as
// by the user.
- // TODO(b/138661450) Move IPC calls to background
- boolean isMultiUserEnabled = whitelistIpcs(() -> mUserManager.isUserSwitcherEnabled(
- mContext.getResources().getBoolean(
- R.bool.qs_show_user_switcher_for_single_user)));
- if (isMultiUserEnabled) {
+ if (mIsUserSwitcherEnabled) {
mMultiUserAvatar.setVisibility(View.VISIBLE);
} else {
mMultiUserAvatar.setVisibility(View.GONE);
@@ -350,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;
@@ -420,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) {
@@ -433,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 8187163..1df1aff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardStatusBarViewController.java
@@ -23,8 +23,10 @@
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
+import android.content.res.Configuration;
import android.content.res.Resources;
import android.hardware.biometrics.BiometricSourceType;
+import android.os.UserManager;
import android.util.MathUtils;
import android.view.View;
@@ -45,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;
@@ -92,6 +97,10 @@
private final BiometricUnlockController mBiometricUnlockController;
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() {
@@ -105,6 +114,11 @@
mView.onOverlayChanged();
KeyguardStatusBarViewController.this.onThemeChanged();
}
+
+ @Override
+ public void onConfigChanged(Configuration newConfig) {
+ updateUserSwitcher();
+ }
};
private final SystemStatusAnimationCallback mAnimationCallback =
@@ -159,6 +173,13 @@
}
@Override
+ public void onKeyguardVisibilityChanged(boolean showing) {
+ if (showing) {
+ updateUserSwitcher();
+ }
+ }
+
+ @Override
public void onBiometricRunningStateChanged(
boolean running,
BiometricSourceType biometricSourceType) {
@@ -230,7 +251,11 @@
KeyguardUpdateMonitor keyguardUpdateMonitor,
BiometricUnlockController biometricUnlockController,
SysuiStatusBarStateController statusBarStateController,
- StatusBarContentInsetsProvider statusBarContentInsetsProvider
+ StatusBarContentInsetsProvider statusBarContentInsetsProvider,
+ UserManager userManager,
+ StatusBarUserSwitcherFeatureController featureController,
+ StatusBarUserSwitcherController userSwitcherController,
+ StatusBarUserInfoTracker statusBarUserInfoTracker
) {
super(view);
mCarrierTextController = carrierTextController;
@@ -248,6 +273,10 @@
mBiometricUnlockController = biometricUnlockController;
mStatusBarStateController = statusBarStateController;
mInsetsProvider = statusBarContentInsetsProvider;
+ mUserManager = userManager;
+ mFeatureController = featureController;
+ mUserSwitcherController = userSwitcherController;
+ mStatusBarUserInfoTracker = statusBarUserInfoTracker;
mFirstBypassAttempt = mKeyguardBypassController.getBypassEnabled();
mKeyguardStateController.addCallback(
@@ -269,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
@@ -276,6 +309,7 @@
super.onInit();
mCarrierTextController.init();
mBatteryMeterViewController.init();
+ mUserSwitcherController.init();
}
@Override
@@ -293,7 +327,7 @@
}
mView.setOnApplyWindowInsetsListener(
(view, windowInsets) -> mView.updateWindowInsets(windowInsets, mInsetsProvider));
-
+ updateUserSwitcher();
onThemeChanged();
}
@@ -317,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. */
@@ -437,6 +474,14 @@
}
/**
+ * Updates visibility of the user switcher button based on {@link android.os.UserManager} state.
+ */
+ private void updateUserSwitcher() {
+ mView.setUserSwitcherEnabled(mUserManager.isUserSwitcherEnabled(getResources().getBoolean(
+ R.bool.qs_show_user_switcher_for_single_user)));
+ }
+
+ /**
* Update {@link KeyguardStatusBarView}'s visibility based on whether keyguard is showing and
* whether heads up is visible.
*/
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 1870588..278b4ec 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java
@@ -20,6 +20,7 @@
import static android.view.View.GONE;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static androidx.constraintlayout.widget.ConstraintSet.BOTTOM;
import static androidx.constraintlayout.widget.ConstraintSet.END;
import static androidx.constraintlayout.widget.ConstraintSet.PARENT_ID;
import static androidx.constraintlayout.widget.ConstraintSet.START;
@@ -414,6 +415,7 @@
private int mDisplayTopInset = 0; // in pixels
private int mDisplayRightInset = 0; // in pixels
private int mSplitShadeStatusBarHeight;
+ private int mSplitShadeNotificationsScrimMarginBottom;
private final KeyguardClockPositionAlgorithm
mClockPositionAlgorithm =
@@ -804,7 +806,6 @@
QsFrameTranslateController qsFrameTranslateController,
KeyguardUnlockAnimationController keyguardUnlockAnimationController) {
super(view,
- featureFlags,
falsingManager,
dozeLog,
keyguardStateController,
@@ -1168,6 +1169,9 @@
public void updateResources() {
mQuickQsOffsetHeight = SystemBarUtils.getQuickQsOffsetHeight(mView.getContext());
mSplitShadeStatusBarHeight = Utils.getSplitShadeStatusBarHeight(mView.getContext());
+ mSplitShadeNotificationsScrimMarginBottom =
+ mResources.getDimensionPixelSize(
+ R.dimen.split_shade_notifications_scrim_margin_bottom);
int qsWidth = mResources.getDimensionPixelSize(R.dimen.qs_panel_width);
int panelWidth = mResources.getDimensionPixelSize(R.dimen.notification_panel_width);
mShouldUseSplitNotificationShade =
@@ -1177,6 +1181,8 @@
mQs.setInSplitShade(mShouldUseSplitNotificationShade);
}
+ int notificationsBottomMargin = mResources.getDimensionPixelSize(
+ R.dimen.notification_panel_margin_bottom);
int topMargin = mShouldUseSplitNotificationShade ? mSplitShadeStatusBarHeight :
mResources.getDimensionPixelSize(R.dimen.notification_panel_margin_top);
mSplitShadeHeaderController.setSplitShadeMode(mShouldUseSplitNotificationShade);
@@ -1205,9 +1211,12 @@
constraintSet.getConstraint(R.id.notification_stack_scroller).layout.mWidth = panelWidth;
constraintSet.getConstraint(R.id.qs_frame).layout.mWidth = qsWidth;
constraintSet.setMargin(R.id.notification_stack_scroller, TOP, topMargin);
+ constraintSet.setMargin(R.id.notification_stack_scroller, BOTTOM,
+ notificationsBottomMargin);
constraintSet.setMargin(R.id.qs_frame, TOP, topMargin);
constraintSet.applyTo(mNotificationContainerParent);
mAmbientState.setStackTopMargin(topMargin);
+ mNotificationsQSContainerController.updateMargins();
mNotificationsQSContainerController.setSplitShadeEnabled(mShouldUseSplitNotificationShade);
updateKeyguardStatusViewAlignment(/* animate= */false);
@@ -1875,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) {
@@ -2565,7 +2581,8 @@
right = getView().getRight() + mDisplayRightInset;
} else {
top = Math.min(qsPanelBottomY, mSplitShadeStatusBarHeight);
- bottom = top + mNotificationStackScrollLayoutController.getHeight();
+ bottom = top + mNotificationStackScrollLayoutController.getHeight()
+ - mSplitShadeNotificationsScrimMarginBottom;
left = mNotificationStackScrollLayoutController.getLeft();
right = mNotificationStackScrollLayoutController.getRight();
}
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 34bb6d3..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
@@ -63,7 +66,7 @@
}
public override fun onViewAttached() {
- notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ updateMargins()
overviewProxyService.addCallback(taskbarVisibilityListener)
mView.setInsetsChangedListener(windowInsetsListener)
mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) }
@@ -75,6 +78,10 @@
mView.removeQSFragmentAttachedListener()
}
+ fun updateMargins() {
+ notificationsBottomMargin = mView.defaultNotificationsMarginBottom
+ }
+
override fun setCustomizerAnimating(animating: Boolean) {
if (isQSCustomizerAnimating != animating) {
isQSCustomizerAnimating = animating
@@ -104,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 9210a8b..95e3b70 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationsQuickSettingsContainer.java
@@ -45,7 +45,6 @@
private View mStackScroller;
private View mKeyguardStatusBar;
- private int mStackScrollerMargin;
private ArrayList<View> mDrawingOrderedChildren = new ArrayList<>();
private ArrayList<View> mLayoutDrawingOrder = new ArrayList<>();
private final Comparator<View> mIndexComparator = Comparator.comparingInt(this::indexOfChild);
@@ -53,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);
@@ -63,7 +63,6 @@
super.onFinishInflate();
mQsFrame = findViewById(R.id.qs_frame);
mStackScroller = findViewById(R.id.notification_stack_scroller);
- mStackScrollerMargin = ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
mKeyguardStatusBar = findViewById(R.id.keyguard_header);
}
@@ -72,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
@@ -91,13 +91,23 @@
mQSScrollView.getPaddingLeft(),
mQSScrollView.getPaddingTop(),
mQSScrollView.getPaddingRight(),
+ paddingBottom);
+ }
+ }
+
+ public void setQSContainerPaddingBottom(int paddingBottom) {
+ if (mQSContainer != null) {
+ mQSContainer.setPadding(
+ mQSContainer.getPaddingLeft(),
+ mQSContainer.getPaddingTop(),
+ mQSContainer.getPaddingRight(),
paddingBottom
);
}
}
public int getDefaultNotificationsMarginBottom() {
- return mStackScrollerMargin;
+ return ((LayoutParams) mStackScroller.getLayoutParams()).bottomMargin;
}
public void setInsetsChangedListener(Consumer<WindowInsets> onInsetsChangedListener) {
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/ScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
index 091831f..ea61a8b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScreenOffAnimationController.kt
@@ -113,14 +113,16 @@
* the animation ends
*/
fun shouldDelayKeyguardShow(): Boolean =
- animations.any { it.shouldPlayAnimation() }
+ animations.any { it.shouldDelayKeyguardShow() }
/**
* Return true while we want to ignore requests to show keyguard, we need to handle pending
* keyguard lock requests manually
+ *
+ * @see [com.android.systemui.keyguard.KeyguardViewMediator.maybeHandlePendingLock]
*/
fun isKeyguardShowDelayed(): Boolean =
- animations.any { it.isAnimationPlaying() }
+ animations.any { it.isKeyguardShowDelayed() }
/**
* Return true to ignore requests to hide keyguard
@@ -211,6 +213,8 @@
fun shouldAnimateInKeyguard(): Boolean = false
fun animateInKeyguard(keyguardView: View, after: Runnable) = after.run()
+ fun shouldDelayKeyguardShow(): Boolean = false
+ fun isKeyguardShowDelayed(): Boolean = false
fun isKeyguardHideDelayed(): Boolean = false
fun shouldHideScrimOnWakeUp(): Boolean = false
fun overrideNotificationsDozeAmount(): Boolean = false
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/UnlockedScreenOffAnimationController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
index cc65ca02..0abe8e4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/UnlockedScreenOffAnimationController.kt
@@ -136,6 +136,12 @@
globalSettings.getFloat(Settings.Global.ANIMATOR_DURATION_SCALE, 1f)
}
+ override fun shouldDelayKeyguardShow(): Boolean =
+ shouldPlayAnimation()
+
+ override fun isKeyguardShowDelayed(): Boolean =
+ isAnimationPlaying()
+
/**
* Animates in the provided keyguard view, ending in the same position that it will be in on
* AOD.
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/RemoteInputView.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
index 48949f92..3205e09 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/RemoteInputView.java
@@ -118,6 +118,7 @@
private boolean mColorized;
private int mTint;
private boolean mResetting;
+ private boolean mWasSpinning;
// TODO(b/193539698): move these to a Controller
private RemoteInputController mController;
@@ -439,6 +440,10 @@
mEditText.requestFocus();
}
}
+ if (mWasSpinning) {
+ mController.addSpinning(mEntry.getKey(), mToken);
+ mWasSpinning = false;
+ }
}
@Override
@@ -447,6 +452,7 @@
mEditText.removeTextChangedListener(mTextWatcher);
mEditText.setOnEditorActionListener(null);
mEditText.mRemoteInputView = null;
+ mWasSpinning = mController.isSpinning(mEntry.getKey(), mToken);
if (mEntry.getRow().isChangingPosition() || isTemporarilyDetached()) {
return;
}
@@ -533,6 +539,8 @@
if (isActive() && mWrapper != null) {
mWrapper.setRemoteInputVisible(true);
}
+
+ mWasSpinning = false;
}
private void reset() {
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/unfold/FoldAodAnimationController.kt b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
index c481fc9..2e627a8 100644
--- a/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
+++ b/packages/SystemUI/src/com/android/systemui/unfold/FoldAodAnimationController.kt
@@ -20,7 +20,6 @@
import android.os.PowerManager
import android.provider.Settings
import com.android.systemui.dagger.qualifiers.Main
-import com.android.systemui.keyguard.KeyguardViewMediator
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.statusbar.LightRevealScrim
import com.android.systemui.statusbar.phone.ScreenOffAnimation
@@ -28,7 +27,6 @@
import com.android.systemui.statusbar.policy.CallbackController
import com.android.systemui.unfold.FoldAodAnimationController.FoldAodAnimationStatus
import com.android.systemui.util.settings.GlobalSettings
-import dagger.Lazy
import javax.inject.Inject
/**
@@ -40,7 +38,6 @@
@Inject
constructor(
@Main private val handler: Handler,
- private val keyguardViewMediatorLazy: Lazy<KeyguardViewMediator>,
private val wakefulnessLifecycle: WakefulnessLifecycle,
private val globalSettings: GlobalSettings
) : CallbackController<FoldAodAnimationStatus>, ScreenOffAnimation, WakefulnessLifecycle.Observer {
@@ -57,7 +54,6 @@
statusBar.notificationPanelViewController.startFoldToAodAnimation {
// End action
isAnimationPlaying = false
- keyguardViewMediatorLazy.get().maybeHandlePendingLock()
}
}
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/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/complication/DreamClockDateComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java
new file mode 100644
index 0000000..b02c506
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockDateComplicationTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamClockDateComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamClockDateComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamClockDateComplication} is registered.
+ */
+ @Test
+ public void testComplicationAdded() {
+ final DreamClockDateComplication.Registrant registrant =
+ new DreamClockDateComplication.Registrant(
+ mContext,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
new file mode 100644
index 0000000..088b4d5
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamClockTimeComplicationTest.java
@@ -0,0 +1,66 @@
+/*
+ * 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.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.verify;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamClockTimeComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamClockTimeComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamClockTimeComplication} is registered.
+ */
+ @Test
+ public void testComplicationAdded() {
+ final DreamClockTimeComplication.Registrant registrant =
+ new DreamClockTimeComplication.Registrant(
+ mContext,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.java
new file mode 100644
index 0000000..151742a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/complication/DreamWeatherComplicationTest.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 com.android.systemui.dreams.complication;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.testing.AndroidTestingRunner;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.dreams.DreamOverlayStateController;
+import com.android.systemui.statusbar.lockscreen.LockscreenSmartspaceController;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class DreamWeatherComplicationTest extends SysuiTestCase {
+ @SuppressWarnings("HidingField")
+ @Mock
+ private Context mContext;
+
+ @Mock
+ private LockscreenSmartspaceController mSmartspaceController;
+
+ @Mock
+ private DreamOverlayStateController mDreamOverlayStateController;
+
+ @Mock
+ private DreamWeatherComplication mComplication;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ /**
+ * Ensures {@link DreamWeatherComplication} is only registered when it is available.
+ */
+ @Test
+ public void testComplicationAvailability() {
+ when(mSmartspaceController.isEnabled()).thenReturn(false);
+ final DreamWeatherComplication.Registrant registrant =
+ new DreamWeatherComplication.Registrant(
+ mContext,
+ mSmartspaceController,
+ mDreamOverlayStateController,
+ mComplication);
+ registrant.start();
+ verify(mDreamOverlayStateController, never()).addComplication(any());
+
+ when(mSmartspaceController.isEnabled()).thenReturn(true);
+ registrant.start();
+ verify(mDreamOverlayStateController).addComplication(eq(mComplication));
+ }
+}
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/hdmi/HdmiCecSetMenuLanguageHelperTest.java b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
new file mode 100644
index 0000000..1171bd2
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/hdmi/HdmiCecSetMenuLanguageHelperTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.hdmi;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.provider.Settings;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.testing.AndroidTestingRunner;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.util.settings.SecureSettings;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.Locale;
+import java.util.concurrent.Executor;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+public class HdmiCecSetMenuLanguageHelperTest extends SysuiTestCase {
+
+ private HdmiCecSetMenuLanguageHelper mHdmiCecSetMenuLanguageHelper;
+
+ @Mock
+ private Executor mExecutor;
+
+ @Mock
+ private SecureSettings mSecureSettings;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mSecureSettings.getString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST)).thenReturn(null);
+ mHdmiCecSetMenuLanguageHelper =
+ new HdmiCecSetMenuLanguageHelper(mExecutor, mSecureSettings);
+ }
+
+ @Test
+ public void testSetGetLocale() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("en");
+ assertThat(mHdmiCecSetMenuLanguageHelper.getLocale()).isEqualTo(Locale.ENGLISH);
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_EmptyByDefault() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("en");
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_AcceptLanguage() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.acceptLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(false);
+ verify(mExecutor).execute(any());
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_DeclineLanguage() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de");
+ }
+
+ @Test
+ public void testIsLocaleDenylisted_DeclineTwoLanguages() {
+ mHdmiCecSetMenuLanguageHelper.setLocale("de");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de");
+ mHdmiCecSetMenuLanguageHelper.setLocale("pl");
+ mHdmiCecSetMenuLanguageHelper.declineLocale();
+ assertThat(mHdmiCecSetMenuLanguageHelper.isLocaleDenylisted()).isEqualTo(true);
+ verify(mSecureSettings).putString(
+ Settings.Secure.HDMI_CEC_SET_MENU_LANGUAGE_DENYLIST, "de,pl");
+ }
+}
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/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 9ca898b..612bad8 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;
@@ -285,20 +286,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 +323,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 +333,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 +344,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
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/notification/stack/StackScrollAlgorithmTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
index ea68143..1da9bbc 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithmTest.kt
@@ -69,10 +69,9 @@
stackScrollAlgorithm.resetViewStates(ambientState, /* speedBumpIndex= */ 0)
- val closeHandleUnderlapHeight =
- context.resources.getDimensionPixelSize(R.dimen.close_handle_underlap)
- val fullHeight =
- ambientState.layoutMaxHeight + closeHandleUnderlapHeight - ambientState.stackY
+ val marginBottom =
+ context.resources.getDimensionPixelSize(R.dimen.notification_panel_margin_bottom)
+ val fullHeight = ambientState.layoutMaxHeight + marginBottom - ambientState.stackY
val centeredY = ambientState.stackY + fullHeight / 2f - emptyShadeView.height / 2f
assertThat(emptyShadeView.viewState?.yTranslation).isEqualTo(centeredY)
}
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 36a4c1e..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
@@ -23,9 +23,13 @@
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.os.UserManager;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.LayoutInflater;
@@ -43,8 +47,12 @@
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;
import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.statusbar.policy.UserInfoController;
@@ -52,6 +60,7 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -87,6 +96,18 @@
private SysuiStatusBarStateController mStatusBarStateController;
@Mock
private StatusBarContentInsetsProvider mStatusBarContentInsetsProvider;
+ @Mock
+ private UserManager mUserManager;
+ @Captor
+ 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;
@@ -101,11 +122,15 @@
allowTestableLooperAsMainThread();
TestableLooper.get(this).runWithLooper(() -> {
mKeyguardStatusBarView =
- (KeyguardStatusBarView) LayoutInflater.from(mContext)
- .inflate(R.layout.keyguard_status_bar, null);
+ spy((KeyguardStatusBarView) LayoutInflater.from(mContext)
+ .inflate(R.layout.keyguard_status_bar, null));
});
- mController = new KeyguardStatusBarViewController(
+ mController = createController();
+ }
+
+ private KeyguardStatusBarViewController createController() {
+ return new KeyguardStatusBarViewController(
mKeyguardStatusBarView,
mCarrierTextController,
mConfigurationController,
@@ -121,7 +146,11 @@
mKeyguardUpdateMonitor,
mBiometricUnlockController,
mStatusBarStateController,
- mStatusBarContentInsetsProvider
+ mStatusBarContentInsetsProvider,
+ mUserManager,
+ mStatusBarUserSwitcherFeatureController,
+ mStatusBarUserSwitcherController,
+ mStatusBarUserInfoTracker
);
}
@@ -133,6 +162,31 @@
verify(mAnimationScheduler).addCallback(any());
verify(mUserInfoController).addCallback(any());
verify(mStatusBarIconController).addIconGroup(any());
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ }
+
+ @Test
+ public void onConfigurationChanged_updatesUserSwitcherVisibility() {
+ mController.onViewAttached();
+ verify(mConfigurationController).addCallback(mConfigurationListenerCaptor.capture());
+ clearInvocations(mUserManager);
+ clearInvocations(mKeyguardStatusBarView);
+
+ mConfigurationListenerCaptor.getValue().onConfigChanged(null);
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
+ }
+
+ @Test
+ public void onKeyguardVisibilityChanged_updatesUserSwitcherVisibility() {
+ mController.onViewAttached();
+ verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardCallbackCaptor.capture());
+ clearInvocations(mUserManager);
+ clearInvocations(mKeyguardStatusBarView);
+
+ mKeyguardCallbackCaptor.getValue().onKeyguardVisibilityChanged(true);
+ verify(mUserManager).isUserSwitcherEnabled(anyBoolean());
+ verify(mKeyguardStatusBarView).setUserSwitcherEnabled(anyBoolean());
}
@Test
@@ -318,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 337e64592..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,8 @@
@Test
fun testDetailShowingInSplitShade() {
notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(false)
+
given(taskbarVisible = false,
navigationMode = GESTURES_NAVIGATION,
insets = windowInsets().withStableBottom())
@@ -215,6 +365,36 @@
then(expectedContainerPadding = 0)
}
+ @Test
+ fun testDetailShowingInSplitShade_newFooter() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ useNewFooter(true)
+
+ 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 testNotificationsMarginBottomIsUpdated() {
+ notificationsQSContainerController.splitShadeEnabled = true
+ verify(notificationsQSContainer).setNotificationsMarginBottom(NOTIFICATIONS_MARGIN)
+
+ whenever(notificationsQSContainer.defaultNotificationsMarginBottom).thenReturn(100)
+ notificationsQSContainerController.updateMargins()
+ notificationsQSContainerController.splitShadeEnabled = false
+
+ verify(notificationsQSContainer).setNotificationsMarginBottom(100)
+ }
+
private fun given(
taskbarVisible: Boolean,
navigationMode: Int,
@@ -234,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)
}
@@ -251,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..4e7e3c1 100644
--- a/services/Android.bp
+++ b/services/Android.bp
@@ -16,6 +16,10 @@
// "-Xep:AndroidFrameworkBinderIdentity:ERROR",
"-Xep:AndroidFrameworkCompatChange:ERROR",
// "-Xep:AndroidFrameworkUid:ERROR",
+ "-Xep:SelfEquals:ERROR",
+ "-Xep:NullTernary:ERROR",
+ "-Xep:TryFailThrowable:ERROR",
+ "-Xep:HashtableContains:ERROR",
// NOTE: only enable to generate local patchfiles
// "-XepPatchChecks:refaster:frameworks/base/errorprone/refaster/EfficientXml.java.refaster",
// "-XepPatchLocation:/tmp/refaster/",
@@ -102,6 +106,7 @@
":services.usage-sources",
":services.usb-sources",
":services.voiceinteraction-sources",
+ ":services.wallpapereffectsgeneration-sources",
":services.wifi-sources",
],
visibility: ["//visibility:private"],
@@ -158,6 +163,7 @@
"services.usage",
"services.usb",
"services.voiceinteraction",
+ "services.wallpapereffectsgeneration",
"services.wifi",
"service-blobstore",
"service-jobscheduler",
@@ -181,10 +187,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/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 0eb6b8d..a771e7b 100644
--- a/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
+++ b/services/companion/java/com/android/server/companion/presence/BleCompanionDeviceScanner.java
@@ -21,8 +21,6 @@
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_PREVIOUS_STATE;
import static android.bluetooth.BluetoothAdapter.EXTRA_STATE;
-import static android.bluetooth.BluetoothAdapter.STATE_BLE_ON;
-import static android.bluetooth.BluetoothAdapter.STATE_ON;
import static android.bluetooth.BluetoothAdapter.nameForState;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_ALREADY_STARTED;
import static android.bluetooth.le.ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED;
@@ -35,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;
@@ -72,7 +71,6 @@
@SuppressLint("LongLogTag")
class BleCompanionDeviceScanner implements AssociationStore.OnChangeListener {
- private static final boolean DEBUG = false;
private static final String TAG = "CompanionDevice_PresenceMonitor_BLE";
/**
@@ -156,7 +154,7 @@
private void checkBleState() {
enforceInitialized();
- final boolean bleAvailable = isBleAvailable();
+ final boolean bleAvailable = mBtAdapter.isLeEnabled();
if (DEBUG) {
Log.i(TAG, "checkBleState() bleAvailable=" + bleAvailable);
}
@@ -183,16 +181,6 @@
}
}
- /**
- * A duplicate of {@code BluetoothAdapter.getLeAccess()} method which has the package-private
- * access level, so it's not accessible from here.
- */
- private boolean isBleAvailable() {
- final int state = mBtAdapter.getLeState();
- if (DEBUG) Log.d(TAG, "getLeAccess() state=" + nameForBtState(state));
- return state == STATE_ON || state == STATE_BLE_ON;
- }
-
@MainThread
private void startScan() {
enforceInitialized();
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/android/app/usage/UsageStatsManagerInternal.java b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
index 435d294..a35aa7c 100644
--- a/services/core/java/android/app/usage/UsageStatsManagerInternal.java
+++ b/services/core/java/android/app/usage/UsageStatsManagerInternal.java
@@ -21,6 +21,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.UsageStatsManager.StandbyBuckets;
import android.content.ComponentName;
import android.content.LocusId;
@@ -375,10 +376,11 @@
* to this broadcast.
* @param timestampMs time (in millis) when the broadcast was dispatched, in
* {@link SystemClock#elapsedRealtime()} timebase.
+ * @param targetUidProcState process state of the uid that the broadcast is targeted to.
*/
public abstract void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
@NonNull UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs);
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState);
/**
* Reports a notification posted event to the UsageStatsManager.
diff --git a/services/core/java/com/android/server/BinaryTransparencyService.java b/services/core/java/com/android/server/BinaryTransparencyService.java
index db510cb..7714dbc 100644
--- a/services/core/java/com/android/server/BinaryTransparencyService.java
+++ b/services/core/java/com/android/server/BinaryTransparencyService.java
@@ -33,6 +33,7 @@
import android.util.Slog;
import com.android.internal.os.IBinaryTransparencyService;
+import com.android.internal.util.FrameworkStatsLog;
import java.io.File;
import java.io.FileDescriptor;
@@ -373,6 +374,7 @@
private void getVBMetaDigestInformation() {
mVbmetaDigest = SystemProperties.get(SYSPROP_NAME_VBETA_DIGEST, VBMETA_DIGEST_UNAVAILABLE);
Slog.d(TAG, String.format("VBMeta Digest: %s", mVbmetaDigest));
+ FrameworkStatsLog.write(FrameworkStatsLog.VBMETA_DIGEST_REPORTED, mVbmetaDigest);
}
@NonNull
@@ -437,6 +439,13 @@
} else {
mBinaryHashes.put(packageName, sha256digest);
}
+
+ if (packageInfo.isApex) {
+ FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED,
+ packageInfo.packageName,
+ packageInfo.getLongVersionCode(),
+ mBinaryHashes.get(packageInfo.packageName));
+ }
}
} catch (PackageManager.NameNotFoundException e) {
Slog.e(TAG, "Could not find package with name " + packageName);
@@ -466,6 +475,8 @@
} else {
mBinaryHashes.put(packageInfo.packageName, sha256digest);
}
+ FrameworkStatsLog.write(FrameworkStatsLog.APEX_INFO_GATHERED, packageInfo.packageName,
+ packageInfo.getLongVersionCode(), mBinaryHashes.get(packageInfo.packageName));
Slog.d(TAG, String.format("Last update time for %s: %d", packageInfo.packageName,
packageInfo.lastUpdateTime));
mBinaryLastUpdateTimes.put(packageInfo.packageName, packageInfo.lastUpdateTime);
diff --git a/services/core/java/com/android/server/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/DockObserver.java b/services/core/java/com/android/server/DockObserver.java
index e5a7b4e..8a6b54f 100644
--- a/services/core/java/com/android/server/DockObserver.java
+++ b/services/core/java/com/android/server/DockObserver.java
@@ -19,7 +19,6 @@
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
-import android.content.pm.PackageManager;
import android.media.AudioManager;
import android.media.Ringtone;
import android.media.RingtoneManager;
@@ -32,15 +31,21 @@
import android.os.UEventObserver;
import android.os.UserHandle;
import android.provider.Settings;
-import android.util.Log;
+import android.util.Pair;
import android.util.Slog;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.util.DumpUtils;
+import com.android.server.ExtconUEventObserver.ExtconInfo;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
/**
* DockObserver monitors for a docking station.
@@ -48,9 +53,6 @@
final class DockObserver extends SystemService {
private static final String TAG = "DockObserver";
- private static final String DOCK_UEVENT_MATCH = "DEVPATH=/devices/virtual/switch/dock";
- private static final String DOCK_STATE_PATH = "/sys/class/switch/dock/state";
-
private static final int MSG_DOCK_STATE_CHANGED = 0;
private final PowerManager mPowerManager;
@@ -69,6 +71,92 @@
private final boolean mAllowTheaterModeWakeFromDock;
+ private final List<ExtconStateConfig> mExtconStateConfigs;
+
+ static final class ExtconStateProvider {
+ private final Map<String, String> mState;
+
+ ExtconStateProvider(Map<String, String> state) {
+ mState = state;
+ }
+
+ String getValue(String key) {
+ return mState.get(key);
+ }
+
+
+ static ExtconStateProvider fromString(String stateString) {
+ Map<String, String> states = new HashMap<>();
+ String[] lines = stateString.split("\n");
+ for (String line : lines) {
+ String[] fields = line.split("=");
+ if (fields.length == 2) {
+ states.put(fields[0], fields[1]);
+ } else {
+ Slog.e(TAG, "Invalid line: " + line);
+ }
+ }
+ return new ExtconStateProvider(states);
+ }
+
+ static ExtconStateProvider fromFile(String stateFilePath) {
+ char[] buffer = new char[1024];
+ try (FileReader file = new FileReader(stateFilePath)) {
+ int len = file.read(buffer, 0, 1024);
+ String stateString = (new String(buffer, 0, len)).trim();
+ return ExtconStateProvider.fromString(stateString);
+ } catch (FileNotFoundException e) {
+ Slog.w(TAG, "No state file found at: " + stateFilePath);
+ return new ExtconStateProvider(new HashMap<>());
+ } catch (Exception e) {
+ Slog.e(TAG, "" , e);
+ return new ExtconStateProvider(new HashMap<>());
+ }
+ }
+ }
+
+ /**
+ * Represents a mapping from extcon state to EXTRA_DOCK_STATE value. Each
+ * instance corresponds to an entry in config_dockExtconStateMapping.
+ */
+ private static final class ExtconStateConfig {
+
+ // The EXTRA_DOCK_STATE that will be used if the extcon key-value pairs match
+ public final int extraStateValue;
+
+ // A list of key-value pairs that must be present in the extcon state for a match
+ // to be considered. An empty list is considered a matching wildcard.
+ public final List<Pair<String, String>> keyValuePairs = new ArrayList<>();
+
+ ExtconStateConfig(int extraStateValue) {
+ this.extraStateValue = extraStateValue;
+ }
+ }
+
+ private static List<ExtconStateConfig> loadExtconStateConfigs(Context context) {
+ String[] rows = context.getResources().getStringArray(
+ com.android.internal.R.array.config_dockExtconStateMapping);
+ try {
+ ArrayList<ExtconStateConfig> configs = new ArrayList<>();
+ for (String row : rows) {
+ String[] rowFields = row.split(",");
+ ExtconStateConfig config = new ExtconStateConfig(Integer.parseInt(rowFields[0]));
+ for (int i = 1; i < rowFields.length; i++) {
+ String[] keyValueFields = rowFields[i].split("=");
+ if (keyValueFields.length != 2) {
+ throw new IllegalArgumentException("Invalid key-value: " + rowFields[i]);
+ }
+ config.keyValuePairs.add(Pair.create(keyValueFields[0], keyValueFields[1]));
+ }
+ configs.add(config);
+ }
+ return configs;
+ } catch (IllegalArgumentException | ArrayIndexOutOfBoundsException e) {
+ Slog.e(TAG, "Could not parse extcon state config", e);
+ return new ArrayList<>();
+ }
+ }
+
public DockObserver(Context context) {
super(context);
@@ -77,9 +165,25 @@
mAllowTheaterModeWakeFromDock = context.getResources().getBoolean(
com.android.internal.R.bool.config_allowTheaterModeWakeFromDock);
- init(); // set initial status
+ mExtconStateConfigs = loadExtconStateConfigs(context);
- mObserver.startObserving(DOCK_UEVENT_MATCH);
+ List<ExtconInfo> infos = ExtconInfo.getExtconInfoForTypes(new String[] {
+ ExtconInfo.EXTCON_DOCK
+ });
+
+ if (!infos.isEmpty()) {
+ ExtconInfo info = infos.get(0);
+ Slog.i(TAG, "Found extcon info devPath: " + info.getDevicePath()
+ + ", statePath: " + info.getStatePath());
+
+ // set initial status
+ setDockStateFromProviderLocked(ExtconStateProvider.fromFile(info.getStatePath()));
+ mPreviousDockState = mActualDockState;
+
+ mExtconUEventObserver.startObserving(info);
+ } else {
+ Slog.i(TAG, "No extcon dock device found in this kernel.");
+ }
}
@Override
@@ -101,26 +205,6 @@
}
}
- private void init() {
- synchronized (mLock) {
- try {
- char[] buffer = new char[1024];
- FileReader file = new FileReader(DOCK_STATE_PATH);
- try {
- int len = file.read(buffer, 0, 1024);
- setActualDockStateLocked(Integer.parseInt((new String(buffer, 0, len)).trim()));
- mPreviousDockState = mActualDockState;
- } finally {
- file.close();
- }
- } catch (FileNotFoundException e) {
- Slog.w(TAG, "This kernel does not have dock station support");
- } catch (Exception e) {
- Slog.e(TAG, "" , e);
- }
- }
- }
-
private void setActualDockStateLocked(int newState) {
mActualDockState = newState;
if (!mUpdatesStopped) {
@@ -234,19 +318,50 @@
}
};
- private final UEventObserver mObserver = new UEventObserver() {
- @Override
- public void onUEvent(UEventObserver.UEvent event) {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Slog.v(TAG, "Dock UEVENT: " + event.toString());
+ private int getDockedStateExtraValue(ExtconStateProvider state) {
+ for (ExtconStateConfig config : mExtconStateConfigs) {
+ boolean match = true;
+ for (Pair<String, String> keyValue : config.keyValuePairs) {
+ String stateValue = state.getValue(keyValue.first);
+ match = match && keyValue.second.equals(stateValue);
+ if (!match) {
+ break;
+ }
}
- try {
- synchronized (mLock) {
- setActualDockStateLocked(Integer.parseInt(event.get("SWITCH_STATE")));
+ if (match) {
+ return config.extraStateValue;
+ }
+ }
+
+ return Intent.EXTRA_DOCK_STATE_DESK;
+ }
+
+ @VisibleForTesting
+ void setDockStateFromProviderForTesting(ExtconStateProvider provider) {
+ synchronized (mLock) {
+ setDockStateFromProviderLocked(provider);
+ }
+ }
+
+ private void setDockStateFromProviderLocked(ExtconStateProvider provider) {
+ int state = Intent.EXTRA_DOCK_STATE_UNDOCKED;
+ if ("1".equals(provider.getValue("DOCK"))) {
+ state = getDockedStateExtraValue(provider);
+ }
+ setActualDockStateLocked(state);
+ }
+
+ private final ExtconUEventObserver mExtconUEventObserver = new ExtconUEventObserver() {
+ @Override
+ public void onUEvent(ExtconInfo extconInfo, UEventObserver.UEvent event) {
+ synchronized (mLock) {
+ String stateString = event.get("STATE");
+ if (stateString != null) {
+ setDockStateFromProviderLocked(ExtconStateProvider.fromString(stateString));
+ } else {
+ Slog.e(TAG, "Extcon event missing STATE: " + event);
}
- } catch (NumberFormatException e) {
- Slog.e(TAG, "Could not parse switch state from event " + event);
}
}
};
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 0bc3fcc..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,
@@ -576,19 +576,19 @@
return () -> {
final TimingsTraceAndSlog t = new TimingsTraceAndSlog(oldTrace);
final String serviceName = service.getClass().getName();
+ final int curUserId = curUser.getUserIdentifier();
+ t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName);
try {
- final int curUserId = curUser.getUserIdentifier();
- t.traceBegin("ssm.on" + USER_STARTING + "User-" + curUserId + "_" + serviceName);
long time = SystemClock.elapsedRealtime();
service.onUserStarting(curUser);
warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
"on" + USER_STARTING + "User-" + curUserId);
- t.traceEnd();
} 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 {
+ t.traceEnd();
}
};
}
@@ -601,14 +601,26 @@
final int curUserId = curUser.getUserIdentifier();
t.traceBegin("ssm.on" + USER_COMPLETED_EVENT + "User-" + curUserId
+ "_" + eventType + "_" + serviceName);
- long time = SystemClock.elapsedRealtime();
- service.onUserCompletedEvent(curUser, eventType);
- warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
- "on" + USER_COMPLETED_EVENT + "User-" + curUserId);
- t.traceEnd();
+ try {
+ long time = SystemClock.elapsedRealtime();
+ service.onUserCompletedEvent(curUser, eventType);
+ warnIfTooLong(SystemClock.elapsedRealtime() - time, service,
+ "on" + USER_COMPLETED_EVENT + "User-" + curUserId);
+ } catch (Exception e) {
+ logFailure(USER_COMPLETED_EVENT, curUser, serviceName, e);
+ throw e;
+ } finally {
+ t.traceEnd();
+ }
};
}
+ /** 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;
@@ -676,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) {
@@ -700,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/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 0c383eb..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;
@@ -334,7 +335,7 @@
mService.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
// Tell the application to launch this receiver.
- maybeReportBroadcastDispatchedEventLocked(r);
+ maybeReportBroadcastDispatchedEventLocked(r, r.curReceiver.applicationInfo.uid);
r.intent.setComponent(r.curComponent);
boolean started = false;
@@ -870,7 +871,7 @@
+ " due to receiver " + filter.receiverList.app
+ " (uid " + filter.receiverList.uid + ")"
+ " not specifying RECEIVER_EXPORTED");
- // skip = true;
+ skip = true;
}
if (skip) {
@@ -927,7 +928,7 @@
r.receiverTime = SystemClock.uptimeMillis();
maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
maybeScheduleTempAllowlistLocked(filter.owningUid, r, r.options);
- maybeReportBroadcastDispatchedEventLocked(r);
+ maybeReportBroadcastDispatchedEventLocked(r, filter.owningUid);
performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
new Intent(r.intent), r.resultCode, r.resultData,
r.resultExtras, r.ordered, r.initialSticky, r.userId);
@@ -1856,22 +1857,35 @@
return null;
}
- private void maybeReportBroadcastDispatchedEventLocked(BroadcastRecord r) {
+ 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;
- }
- // TODO (206518114): Only report this event when the broadcast is dispatched while the app
- // is in the background state.
getUsageStatsManagerInternal().reportBroadcastDispatched(
r.callingUid, targetPackage, UserHandle.of(r.userId),
- r.options.getIdForResponseEvent(), SystemClock.elapsedRealtime());
+ 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
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..aa0d3da 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(
@@ -246,7 +251,7 @@
* @param packageName package to check
*/
boolean isHibernatingGlobally(String packageName) {
- if (!checkHibernationEnabled("isHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return false;
}
getContext().enforceCallingOrSelfPermission(
@@ -272,7 +277,7 @@
*/
void setHibernatingForUser(String packageName, int userId, boolean isHibernating) {
String methodName = "setHibernatingForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -297,7 +302,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 +332,7 @@
* @param isHibernating new hibernation state
*/
void setHibernatingGlobally(String packageName, boolean isHibernating) {
- if (!checkHibernationEnabled("setHibernatingGlobally")) {
+ if (!sIsServiceEnabled) {
return;
}
getContext().enforceCallingOrSelfPermission(
@@ -359,7 +365,7 @@
@NonNull List<String> getHibernatingPackagesForUser(int userId) {
ArrayList<String> hibernatingPackages = new ArrayList<>();
String methodName = "getHibernatingPackagesForUser";
- if (!checkHibernationEnabled(methodName)) {
+ if (!sIsServiceEnabled) {
return hibernatingPackages;
}
getContext().enforceCallingOrSelfPermission(
@@ -390,6 +396,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 +421,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 +434,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 +699,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 +744,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 +939,8 @@
UserManager getUserManager();
+ StorageStatsManager getStorageStatsManager();
+
Executor getBackgroundExecutor();
UsageStatsManagerInternal getUsageStatsManagerInternal();
@@ -972,6 +990,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/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/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/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 b23395f..0edcea5 100644
--- a/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
+++ b/services/core/java/com/android/server/hdmi/HdmiCecLocalDevicePlayback.java
@@ -17,10 +17,15 @@
package com.android.server.hdmi;
import android.annotation.CallSuper;
+import android.content.ActivityNotFoundException;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
import android.hardware.hdmi.HdmiControlManager;
import android.hardware.hdmi.HdmiDeviceInfo;
import android.hardware.hdmi.IHdmiControlCallback;
import android.hardware.tv.cec.V1_0.SendMessageResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
@@ -45,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;
@@ -383,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;
}
@@ -408,7 +412,7 @@
// locale from being chosen. 'eng' in the CEC command, for instance,
// will always be mapped to en-AU among other variants like en-US, en-GB,
// an en-IN, which may not be the expected one.
- LocalePicker.updateLocale(localeInfo.getLocale());
+ startSetMenuLanguageActivity(localeInfo.getLocale());
return Constants.HANDLED;
}
}
@@ -420,6 +424,24 @@
}
}
+ private void startSetMenuLanguageActivity(Locale locale) {
+ final long identity = Binder.clearCallingIdentity();
+ try {
+ Context context = mService.getContext();
+ Intent intent = new Intent();
+ intent.putExtra(HdmiControlManager.EXTRA_LOCALE, locale.toLanguageTag());
+ intent.setComponent(
+ ComponentName.unflattenFromString(context.getResources().getString(
+ com.android.internal.R.string.config_hdmiCecSetMenuLanguageActivity)));
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ context.startActivityAsUser(intent, context.getUser());
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "unable to start HdmiCecSetMenuLanguageActivity");
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+
@Override
@Constants.HandleMessageResult
protected int handleSetSystemAudioMode(HdmiCecMessage message) {
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/contexthub/IContextHubWrapper.java b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
index 21ea1f6..a1ee46b 100644
--- a/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
+++ b/services/core/java/com/android/server/location/contexthub/IContextHubWrapper.java
@@ -465,7 +465,7 @@
try {
mHub.onHostEndpointConnected(info);
} catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "RemoteException in onHostEndpointConnected");
+ Log.e(TAG, "Exception in onHostEndpointConnected" + e.getMessage());
}
}
@@ -474,7 +474,7 @@
try {
mHub.onHostEndpointDisconnected((char) hostEndpointId);
} catch (RemoteException | ServiceSpecificException e) {
- Log.e(TAG, "RemoteException in onHostEndpointDisconnected");
+ Log.e(TAG, "Exception in onHostEndpointDisconnected" + e.getMessage());
}
}
@@ -488,6 +488,8 @@
return ContextHubTransaction.RESULT_SUCCESS;
} catch (RemoteException | ServiceSpecificException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -499,8 +501,10 @@
try {
mHub.loadNanoapp(contextHubId, aidlNanoAppBinary, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -510,8 +514,10 @@
try {
mHub.unloadNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -521,8 +527,10 @@
try {
mHub.enableNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -532,8 +540,10 @@
try {
mHub.disableNanoapp(contextHubId, nanoappId, transactionId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -542,8 +552,10 @@
try {
mHub.queryNanoapps(contextHubId);
return ContextHubTransaction.RESULT_SUCCESS;
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | UnsupportedOperationException e) {
return ContextHubTransaction.RESULT_FAILED_UNKNOWN;
+ } catch (IllegalArgumentException e) {
+ return ContextHubTransaction.RESULT_FAILED_BAD_PARAMS;
}
}
@@ -551,7 +563,7 @@
mAidlCallbackMap.put(contextHubId, new ContextHubAidlCallback(contextHubId, callback));
try {
mHub.registerCallback(contextHubId, mAidlCallbackMap.get(contextHubId));
- } catch (RemoteException | ServiceSpecificException e) {
+ } catch (RemoteException | ServiceSpecificException | IllegalArgumentException e) {
Log.e(TAG, "Exception while registering callback: " + e.getMessage());
}
}
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/InitAppsHelper.java b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
similarity index 64%
rename from services/core/java/com/android/server/pm/InitAppsHelper.java
rename to services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
index 2fda02d..06405ae 100644
--- a/services/core/java/com/android/server/pm/InitAppsHelper.java
+++ b/services/core/java/com/android/server/pm/InitAndSystemPackageHelper.java
@@ -32,7 +32,6 @@
import static com.android.server.pm.PackageManagerService.TAG;
import static com.android.server.pm.pkg.parsing.ParsingPackageUtils.PARSE_FRAMEWORK_RES_SPLITS;
-import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.pm.parsing.ApkLiteParseUtils;
import android.os.Environment;
@@ -62,25 +61,14 @@
* further cleanup and eventually all the installation/scanning related logic will go to another
* class.
*/
-final class InitAppsHelper {
+final class InitAndSystemPackageHelper {
private final PackageManagerService mPm;
+
private final List<ScanPartition> mDirsToScanAsSystem;
private final int mScanFlags;
private final int mSystemParseFlags;
private final int mSystemScanFlags;
private final InstallPackageHelper mInstallPackageHelper;
- private final ApexManager mApexManager;
- private final PackageParser2 mPackageParser;
- private final ExecutorService mExecutorService;
- /* Tracks how long system scan took */
- private long mSystemScanTime;
- /* Track of the number of cached system apps */
- private int mCachedSystemApps;
- /* Track of the number of system apps */
- private int mSystemPackagesCount;
- private final boolean mIsDeviceUpgrading;
- private final boolean mIsOnlyCoreApps;
- private final List<ScanPartition> mSystemPartitions;
/**
* Tracks new system packages [received in an OTA] that we expect to
@@ -88,34 +76,21 @@
* are package location.
*/
private final ArrayMap<String, File> mExpectingBetter = new ArrayMap<>();
- /* Tracks of any system packages that no longer exist that needs to be pruned. */
- private final List<String> mPossiblyDeletedUpdatedSystemApps = new ArrayList<>();
- // Tracks of stub packages that must either be replaced with full versions in the /data
- // partition or be disabled.
- private final List<String> mStubSystemApps = new ArrayList<>();
// TODO(b/198166813): remove PMS dependency
- InitAppsHelper(PackageManagerService pm, ApexManager apexManager,
- InstallPackageHelper installPackageHelper, PackageParser2 packageParser,
- List<ScanPartition> systemPartitions) {
+ InitAndSystemPackageHelper(PackageManagerService pm) {
mPm = pm;
- mApexManager = apexManager;
- mInstallPackageHelper = installPackageHelper;
- mPackageParser = packageParser;
- mSystemPartitions = systemPartitions;
+ mInstallPackageHelper = new InstallPackageHelper(pm);
mDirsToScanAsSystem = getSystemScanPartitions();
- mIsDeviceUpgrading = mPm.isDeviceUpgrading();
- mIsOnlyCoreApps = mPm.isOnlyCoreApps();
// Set flag to monitor and not change apk file paths when scanning install directories.
int scanFlags = SCAN_BOOTING | SCAN_INITIAL;
- if (mIsDeviceUpgrading || mPm.isFirstBoot()) {
+ if (mPm.isDeviceUpgrading() || mPm.isFirstBoot()) {
mScanFlags = scanFlags | SCAN_FIRST_BOOT_OR_UPGRADE;
} else {
mScanFlags = scanFlags;
}
mSystemParseFlags = mPm.getDefParseFlags() | ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
mSystemScanFlags = scanFlags | SCAN_AS_SYSTEM;
- mExecutorService = ParallelPackageParser.makeExecutorService();
}
private List<File> getFrameworkResApkSplitFiles() {
@@ -143,7 +118,7 @@
private List<ScanPartition> getSystemScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- scanPartitions.addAll(mSystemPartitions);
+ scanPartitions.addAll(mPm.mInjector.getSystemPartitions());
scanPartitions.addAll(getApexScanPartitions());
Slog.d(TAG, "Directories scanned as system partitions: " + scanPartitions);
return scanPartitions;
@@ -151,7 +126,8 @@
private List<ScanPartition> getApexScanPartitions() {
final List<ScanPartition> scanPartitions = new ArrayList<>();
- final List<ApexManager.ActiveApexInfo> activeApexInfos = mApexManager.getActiveApexInfos();
+ final List<ApexManager.ActiveApexInfo> activeApexInfos =
+ mPm.mApexManager.getActiveApexInfos();
for (int i = 0; i < activeApexInfos.size(); i++) {
final ScanPartition scanPartition = resolveApexToScanPartition(activeApexInfos.get(i));
if (scanPartition != null) {
@@ -168,133 +144,117 @@
if (apexInfo.preInstalledApexPath.getAbsolutePath().equals(
sp.getFolder().getAbsolutePath())
|| apexInfo.preInstalledApexPath.getAbsolutePath().startsWith(
- sp.getFolder().getAbsolutePath() + File.separator)) {
+ sp.getFolder().getAbsolutePath() + File.separator)) {
return new ScanPartition(apexInfo.apexDirectory, sp, SCAN_AS_APK_IN_APEX);
}
}
return null;
}
- /**
- * Install apps from system dirs.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public OverlayConfig initSystemApps(WatchedArrayMap<String, PackageSetting> packageSettings,
- int[] userIds, long startTime) {
+ public OverlayConfig initPackages(
+ WatchedArrayMap<String, PackageSetting> packageSettings, int[] userIds,
+ long startTime) {
+ PackageParser2 packageParser = mPm.mInjector.getScanningCachingPackageParser();
+
+ ExecutorService executorService = ParallelPackageParser.makeExecutorService();
// Prepare apex package info before scanning APKs, this information is needed when
// scanning apk in apex.
- mApexManager.scanApexPackagesTraced(mPackageParser, mExecutorService);
+ mPm.mApexManager.scanApexPackagesTraced(packageParser, executorService);
- scanSystemDirs(mPackageParser, mExecutorService);
+ scanSystemDirs(packageParser, executorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
- for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
- for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
+ for (ApexManager.ActiveApexInfo apexInfo : mPm.mApexManager.getActiveApexInfos()) {
+ for (String packageName : mPm.mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}
- final OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
+ OverlayConfig overlayConfig = OverlayConfig.initializeSystemInstance(
consumer -> mPm.forEachPackage(
pkg -> consumer.accept(pkg, pkg.isSystem(),
- apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
+ apkInApexPreInstalledPaths.get(pkg.getPackageName()))));
+ // Prune any system packages that no longer exist.
+ final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<>();
+ // Stub packages must either be replaced with full versions in the /data
+ // partition or be disabled.
+ final List<String> stubSystemApps = new ArrayList<>();
- if (!mIsOnlyCoreApps) {
+ if (!mPm.isOnlyCoreApps()) {
// do this first before mucking with mPackages for the "expecting better" case
- updateStubSystemAppsList(mStubSystemApps);
+ updateStubSystemAppsList(stubSystemApps);
mInstallPackageHelper.prepareSystemPackageCleanUp(packageSettings,
- mPossiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
+ possiblyDeletedUpdatedSystemApps, mExpectingBetter, userIds);
}
- logSystemAppsScanningTime(startTime);
- return overlayConfig;
- }
-
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void logSystemAppsScanningTime(long startTime) {
- mCachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
+ final int cachedSystemApps = PackageCacher.sCachedPackageReadCount.get();
// Remove any shared userIDs that have no associated packages
mPm.mSettings.pruneSharedUsersLPw();
- mSystemScanTime = SystemClock.uptimeMillis() - startTime;
- mSystemPackagesCount = mPm.mPackages.size();
- Slog.i(TAG, "Finished scanning system apps. Time: " + mSystemScanTime
- + " ms, packageCount: " + mSystemPackagesCount
+ final long systemScanTime = SystemClock.uptimeMillis() - startTime;
+ final int systemPackagesCount = mPm.mPackages.size();
+ Slog.i(TAG, "Finished scanning system apps. Time: " + systemScanTime
+ + " ms, packageCount: " + systemPackagesCount
+ " , timePerPackage: "
- + (mSystemPackagesCount == 0 ? 0 : mSystemScanTime / mSystemPackagesCount)
- + " , cached: " + mCachedSystemApps);
- if (mIsDeviceUpgrading && mSystemPackagesCount > 0) {
+ + (systemPackagesCount == 0 ? 0 : systemScanTime / systemPackagesCount)
+ + " , cached: " + cachedSystemApps);
+ if (mPm.isDeviceUpgrading() && systemPackagesCount > 0) {
//CHECKSTYLE:OFF IndentationCheck
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_SYSTEM_APP_AVG_SCAN_TIME,
- mSystemScanTime / mSystemPackagesCount);
+ systemScanTime / systemPackagesCount);
//CHECKSTYLE:ON IndentationCheck
}
- }
- /**
- * Install apps/updates from data dir and fix system apps that are affected.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- public void initNonSystemApps(@NonNull int[] userIds, long startTime) {
- if (!mIsOnlyCoreApps) {
+ if (!mPm.isOnlyCoreApps()) {
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
SystemClock.uptimeMillis());
scanDirTracedLI(mPm.getAppInstallDir(), /* frameworkSplits= */ null, 0,
- mScanFlags | SCAN_REQUIRE_KNOWN,
- mPackageParser, mExecutorService);
+ mScanFlags | SCAN_REQUIRE_KNOWN, 0,
+ packageParser, executorService);
+
}
- List<Runnable> unfinishedTasks = mExecutorService.shutdownNow();
+ List<Runnable> unfinishedTasks = executorService.shutdownNow();
if (!unfinishedTasks.isEmpty()) {
throw new IllegalStateException("Not all tasks finished before calling close: "
+ unfinishedTasks);
}
- if (!mIsOnlyCoreApps) {
- fixSystemPackages(userIds);
- logNonSystemAppScanningTime(startTime);
+
+ if (!mPm.isOnlyCoreApps()) {
+ mInstallPackageHelper.cleanupDisabledPackageSettings(possiblyDeletedUpdatedSystemApps,
+ userIds, mScanFlags);
+ mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
+ stubSystemApps, mSystemScanFlags, mSystemParseFlags);
+
+ // Uncompress and install any stubbed system applications.
+ // This must be done last to ensure all stubs are replaced or disabled.
+ mInstallPackageHelper.installSystemStubPackages(stubSystemApps, mScanFlags);
+
+ final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
+ - cachedSystemApps;
+
+ final long dataScanTime = SystemClock.uptimeMillis() - systemScanTime - startTime;
+ final int dataPackagesCount = mPm.mPackages.size() - systemPackagesCount;
+ Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
+ + " ms, packageCount: " + dataPackagesCount
+ + " , timePerPackage: "
+ + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
+ + " , cached: " + cachedNonSystemApps);
+ if (mPm.isDeviceUpgrading() && dataPackagesCount > 0) {
+ //CHECKSTYLE:OFF IndentationCheck
+ FrameworkStatsLog.write(
+ FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
+ BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
+ dataScanTime / dataPackagesCount);
+ //CHECKSTYLE:OFF IndentationCheck
+ }
}
mExpectingBetter.clear();
+
mPm.mSettings.pruneRenamedPackagesLPw();
- mPackageParser.close();
- }
-
- /**
- * Clean up system packages now that some system package updates have been installed from
- * the data dir. Also install system stub packages as the last step.
- */
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void fixSystemPackages(@NonNull int[] userIds) {
- mInstallPackageHelper.cleanupDisabledPackageSettings(mPossiblyDeletedUpdatedSystemApps,
- userIds, mScanFlags);
- mInstallPackageHelper.checkExistingBetterPackages(mExpectingBetter,
- mStubSystemApps, mSystemScanFlags, mSystemParseFlags);
-
- // Uncompress and install any stubbed system applications.
- // This must be done last to ensure all stubs are replaced or disabled.
- mInstallPackageHelper.installSystemStubPackages(mStubSystemApps, mScanFlags);
- }
-
- @GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
- private void logNonSystemAppScanningTime(long startTime) {
- final int cachedNonSystemApps = PackageCacher.sCachedPackageReadCount.get()
- - mCachedSystemApps;
-
- final long dataScanTime = SystemClock.uptimeMillis() - mSystemScanTime - startTime;
- final int dataPackagesCount = mPm.mPackages.size() - mSystemPackagesCount;
- Slog.i(TAG, "Finished scanning non-system apps. Time: " + dataScanTime
- + " ms, packageCount: " + dataPackagesCount
- + " , timePerPackage: "
- + (dataPackagesCount == 0 ? 0 : dataScanTime / dataPackagesCount)
- + " , cached: " + cachedNonSystemApps);
- if (mIsDeviceUpgrading && dataPackagesCount > 0) {
- //CHECKSTYLE:OFF IndentationCheck
- FrameworkStatsLog.write(
- FrameworkStatsLog.BOOT_TIME_EVENT_DURATION_REPORTED,
- BOOT_TIME_EVENT_DURATION__EVENT__OTA_PACKAGE_MANAGER_DATA_APP_AVG_SCAN_TIME,
- dataScanTime / dataPackagesCount);
- //CHECKSTYLE:OFF IndentationCheck
- }
+ packageParser.close();
+ return overlayConfig;
}
/**
@@ -314,14 +274,14 @@
continue;
}
scanDirTracedLI(partition.getOverlayFolder(), /* frameworkSplits= */ null,
- mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
+ mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
List<File> frameworkSplits = getFrameworkResApkSplitFiles();
scanDirTracedLI(frameworkDir, frameworkSplits,
mSystemParseFlags | PARSE_FRAMEWORK_RES_SPLITS,
- mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
+ mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED, 0,
packageParser, executorService);
if (!mPm.mPackages.containsKey("android")) {
throw new IllegalStateException(
@@ -333,11 +293,11 @@
if (partition.getPrivAppFolder() != null) {
scanDirTracedLI(partition.getPrivAppFolder(), /* frameworkSplits= */ null,
mSystemParseFlags,
- mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag,
+ mSystemScanFlags | SCAN_AS_PRIVILEGED | partition.scanFlag, 0,
packageParser, executorService);
}
scanDirTracedLI(partition.getAppFolder(), /* frameworkSplits= */ null,
- mSystemParseFlags, mSystemScanFlags | partition.scanFlag,
+ mSystemParseFlags, mSystemScanFlags | partition.scanFlag, 0,
packageParser, executorService);
}
}
@@ -356,11 +316,11 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private void scanDirTracedLI(File scanDir, List<File> frameworkSplits,
final int parseFlags, int scanFlags,
- PackageParser2 packageParser, ExecutorService executorService) {
+ long currentTime, PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanDir [" + scanDir.getAbsolutePath() + "]");
try {
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
- scanFlags, packageParser, executorService);
+ scanFlags, currentTime, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java
index 83c8472..336da2a 100644
--- a/services/core/java/com/android/server/pm/InstallPackageHelper.java
+++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java
@@ -3030,7 +3030,7 @@
final RemovePackageHelper removePackageHelper = new RemovePackageHelper(mPm);
removePackageHelper.removePackageLI(stubPkg, true /*chatty*/);
try {
- return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, null);
+ return scanSystemPackageTracedLI(scanFile, parseFlags, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.w(TAG, "Failed to install compressed system package:" + stubPkg.getPackageName(),
e);
@@ -3163,7 +3163,7 @@
| ParsingPackageUtils.PARSE_IS_SYSTEM_DIR;
@PackageManagerService.ScanFlags int scanFlags = mPm.getSystemPackageScanFlags(codePath);
final AndroidPackage pkg = scanSystemPackageTracedLI(
- codePath, parseFlags, scanFlags, null);
+ codePath, parseFlags, scanFlags, 0 /*currentTime*/, null);
PackageSetting pkgSetting = mPm.mSettings.getPackageLPr(pkg.getPackageName());
@@ -3338,7 +3338,7 @@
mRemovePackageHelper.removePackageLI(pkg, true);
try {
final File codePath = new File(pkg.getPath());
- scanSystemPackageTracedLI(codePath, 0, scanFlags, null);
+ scanSystemPackageTracedLI(codePath, 0, scanFlags, 0, null);
} catch (PackageManagerException e) {
Slog.e(TAG, "Failed to parse updated, ex-system package: "
+ e.getMessage());
@@ -3359,7 +3359,7 @@
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public void installPackagesFromDir(File scanDir, List<File> frameworkSplits, int parseFlags,
- int scanFlags, PackageParser2 packageParser,
+ int scanFlags, long currentTime, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();
if (ArrayUtils.isEmpty(files)) {
@@ -3402,7 +3402,7 @@
parseResult.parsedPackage);
}
try {
- addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
+ addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags, currentTime,
null);
} catch (PackageManagerException e) {
errorCode = e.error;
@@ -3465,7 +3465,7 @@
try {
final AndroidPackage newPkg = scanSystemPackageTracedLI(
- scanFile, reparseFlags, rescanFlags, null);
+ scanFile, reparseFlags, rescanFlags, 0, null);
// We rescanned a stub, add it to the list of stubbed system packages
if (newPkg.isStub()) {
stubSystemApps.add(packageName);
@@ -3479,14 +3479,14 @@
/**
* Traces a package scan.
- * @see #scanSystemPackageLI(File, int, int, UserHandle)
+ * @see #scanSystemPackageLI(File, int, int, long, UserHandle)
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
public AndroidPackage scanSystemPackageTracedLI(File scanFile, final int parseFlags,
- int scanFlags, UserHandle user) throws PackageManagerException {
+ int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "scanPackage [" + scanFile.toString() + "]");
try {
- return scanSystemPackageLI(scanFile, parseFlags, scanFlags, user);
+ return scanSystemPackageLI(scanFile, parseFlags, scanFlags, currentTime, user);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
@@ -3498,7 +3498,7 @@
*/
@GuardedBy({"mPm.mInstallLock", "mPm.mLock"})
private AndroidPackage scanSystemPackageLI(File scanFile, int parseFlags, int scanFlags,
- UserHandle user) throws PackageManagerException {
+ long currentTime, UserHandle user) throws PackageManagerException {
if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "parsePackage");
@@ -3514,7 +3514,7 @@
PackageManagerService.renameStaticSharedLibraryPackage(parsedPackage);
}
- return addForInitLI(parsedPackage, parseFlags, scanFlags, user);
+ return addForInitLI(parsedPackage, parseFlags, scanFlags, currentTime, user);
}
/**
@@ -3533,11 +3533,11 @@
@GuardedBy({"mPm.mLock", "mPm.mInstallLock"})
private AndroidPackage addForInitLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
final Pair<ScanResult, Boolean> scanResultPair = scanSystemPackageLI(
- parsedPackage, parseFlags, scanFlags, user);
+ parsedPackage, parseFlags, scanFlags, currentTime, user);
final ScanResult scanResult = scanResultPair.first;
boolean shouldHideSystemApp = scanResultPair.second;
if (scanResult.mSuccess) {
@@ -3715,7 +3715,7 @@
private Pair<ScanResult, Boolean> scanSystemPackageLI(ParsedPackage parsedPackage,
@ParsingPackageUtils.ParseFlags int parseFlags,
- @PackageManagerService.ScanFlags int scanFlags,
+ @PackageManagerService.ScanFlags int scanFlags, long currentTime,
@Nullable UserHandle user) throws PackageManagerException {
final boolean scanSystemPartition =
(parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0;
@@ -3902,7 +3902,7 @@
}
final ScanResult scanResult = scanPackageNewLI(parsedPackage, parseFlags,
- scanFlags | SCAN_UPDATE_SIGNATURE, 0 /* currentTime */, user, null);
+ scanFlags | SCAN_UPDATE_SIGNATURE, currentTime, user, null);
return new Pair<>(scanResult, shouldHideSystemApp);
}
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 9d95ead..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();
@@ -944,7 +946,7 @@
private final BroadcastHelper mBroadcastHelper;
private final RemovePackageHelper mRemovePackageHelper;
private final DeletePackageHelper mDeletePackageHelper;
- private final InitAppsHelper mInitAppsHelper;
+ private final InitAndSystemPackageHelper mInitAndSystemPackageHelper;
private final AppDataHelper mAppDataHelper;
private final InstallPackageHelper mInstallPackageHelper;
private final PreferredActivityHelper mPreferredActivityHelper;
@@ -1671,6 +1673,7 @@
mSharedSystemSharedLibraryPackageName = testParams.sharedSystemSharedLibraryPackageName;
mOverlayConfigSignaturePackage = testParams.overlayConfigSignaturePackage;
mResolveComponentName = testParams.resolveComponentName;
+ mRequiredSupplementalProcessPackage = testParams.requiredSupplementalProcessPackage;
mLiveComputer = createLiveComputer();
mSnapshotComputer = null;
@@ -1689,12 +1692,13 @@
mAppDataHelper = testParams.appDataHelper;
mInstallPackageHelper = testParams.installPackageHelper;
mRemovePackageHelper = testParams.removePackageHelper;
- mInitAppsHelper = testParams.initAndSystemPackageHelper;
+ mInitAndSystemPackageHelper = testParams.initAndSystemPackageHelper;
mDeletePackageHelper = testParams.deletePackageHelper;
mPreferredActivityHelper = testParams.preferredActivityHelper;
mResolveIntentHelper = testParams.resolveIntentHelper;
mDexOptHelper = testParams.dexOptHelper;
mSuspendPackageHelper = testParams.suspendPackageHelper;
+
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
registerObservers(false);
@@ -1836,8 +1840,7 @@
mAppDataHelper = new AppDataHelper(this);
mInstallPackageHelper = new InstallPackageHelper(this, mAppDataHelper);
mRemovePackageHelper = new RemovePackageHelper(this, mAppDataHelper);
- mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
- mInjector.getScanningCachingPackageParser(), mInjector.getSystemPartitions());
+ mInitAndSystemPackageHelper = new InitAndSystemPackageHelper(this);
mDeletePackageHelper = new DeletePackageHelper(this, mRemovePackageHelper,
mAppDataHelper);
mSharedLibraries.setDeletePackageHelper(mDeletePackageHelper);
@@ -1967,8 +1970,8 @@
mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);
final int[] userIds = mUserManager.getUserIds();
- mOverlayConfig = mInitAppsHelper.initSystemApps(packageSettings, userIds, startTime);
- mInitAppsHelper.initNonSystemApps(userIds, startTime);
+ mOverlayConfig = mInitAndSystemPackageHelper.initPackages(packageSettings,
+ userIds, startTime);
// Resolve the storage manager.
mStorageManagerPackage = getStorageManagerPackageName();
@@ -2139,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()) {
@@ -3154,6 +3160,11 @@
throw new IllegalStateException("PermissionController is not found");
}
+ @Override
+ public String getSupplementalProcessPackageName() {
+ return mRequiredSupplementalProcessPackage;
+ }
+
String getPackageInstallerPackageName() {
return mRequiredInstallerPackage;
}
@@ -5519,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(
@@ -8677,7 +8706,7 @@
}
boolean isExpectingBetter(String packageName) {
- return mInitAppsHelper.isExpectingBetter(packageName);
+ return mInitAndSystemPackageHelper.isExpectingBetter(packageName);
}
int getDefParseFlags() {
@@ -8780,12 +8809,13 @@
}
boolean isOverlayMutable(String packageName) {
- return mOverlayConfig.isMutable(packageName);
+ return (mOverlayConfig != null ? mOverlayConfig
+ : OverlayConfig.getSystemInstance()).isMutable(packageName);
}
@ScanFlags int getSystemPackageScanFlags(File codePath) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAppsHelper.getDirsToScanAsSystem();
+ mInitAndSystemPackageHelper.getDirsToScanAsSystem();
@PackageManagerService.ScanFlags int scanFlags = SCAN_AS_SYSTEM;
for (int i = dirsToScanAsSystem.size() - 1; i >= 0; i--) {
ScanPartition partition = dirsToScanAsSystem.get(i);
@@ -8803,7 +8833,7 @@
Pair<Integer, Integer> getSystemPackageRescanFlagsAndReparseFlags(File scanFile,
int systemScanFlags, int systemParseFlags) {
List<ScanPartition> dirsToScanAsSystem =
- mInitAppsHelper.getDirsToScanAsSystem();
+ mInitAndSystemPackageHelper.getDirsToScanAsSystem();
@ParsingPackageUtils.ParseFlags int reparseFlags = 0;
@PackageManagerService.ScanFlags int rescanFlags = 0;
for (int i1 = dirsToScanAsSystem.size() - 1; i1 >= 0; i1--) {
diff --git a/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java b/services/core/java/com/android/server/pm/PackageManagerServiceTestParams.java
index 00ca4ae..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;
@@ -108,7 +109,7 @@
public AppDataHelper appDataHelper;
public InstallPackageHelper installPackageHelper;
public RemovePackageHelper removePackageHelper;
- public InitAppsHelper initAndSystemPackageHelper;
+ public InitAndSystemPackageHelper initAndSystemPackageHelper;
public DeletePackageHelper deletePackageHelper;
public PreferredActivityHelper preferredActivityHelper;
public ResolveIntentHelper resolveIntentHelper;
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/StorageEventHelper.java b/services/core/java/com/android/server/pm/StorageEventHelper.java
index 1372098..bb7e55a 100644
--- a/services/core/java/com/android/server/pm/StorageEventHelper.java
+++ b/services/core/java/com/android/server/pm/StorageEventHelper.java
@@ -32,6 +32,7 @@
import android.content.pm.PackagePartitions;
import android.content.pm.UserInfo;
import android.content.pm.VersionedPackage;
+import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import android.os.Environment;
import android.os.FileUtils;
import android.os.UserHandle;
@@ -47,7 +48,6 @@
import com.android.internal.policy.AttributeCache;
import com.android.server.pm.parsing.pkg.AndroidPackage;
import com.android.server.pm.pkg.PackageStateInternal;
-import com.android.server.pm.pkg.parsing.ParsingPackageUtils;
import java.io.File;
import java.util.ArrayList;
@@ -150,7 +150,7 @@
final AndroidPackage pkg;
try {
pkg = installPackageHelper.scanSystemPackageTracedLI(
- ps.getPath(), parseFlags, SCAN_INITIAL, null);
+ ps.getPath(), parseFlags, SCAN_INITIAL, 0, null);
loaded.add(pkg);
} catch (PackageManagerException e) {
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 52f7d10..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;
@@ -122,8 +124,7 @@
private static final int MSG_DISPATCH_UNLOCK_LOCKOUT = 13;
private static final int MSG_REFRESH_DEVICE_LOCKED_FOR_USER = 14;
private static final int MSG_SCHEDULE_TRUST_TIMEOUT = 15;
- private static final int MSG_USER_REQUESTED_UNLOCK = 16;
- private static final int MSG_ENABLE_TRUST_AGENT = 17;
+ public static final int MSG_USER_REQUESTED_UNLOCK = 16;
private static final String REFRESH_DEVICE_LOCKED_EXCEPT_USER = "except";
@@ -146,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:
@@ -229,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);
@@ -397,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)
@@ -442,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);
@@ -473,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);
}
@@ -632,24 +728,6 @@
}
}
-
- /**
- * Uses {@link LockPatternUtils} to enable the setting for trust agent in the specified
- * component name. This should only be used for testing.
- */
- private void enableTrustAgentForUserForTest(@NonNull ComponentName componentName, int userId) {
- Log.i(TAG,
- "Enabling trust agent " + componentName.flattenToString() + " for user " + userId);
- List<ComponentName> agents =
- new ArrayList<>(mLockPatternUtils.getEnabledTrustAgents(userId));
- if (!agents.contains(componentName)) {
- agents.add(componentName);
- }
- // Even if the agent was already there, we still call setEnabledTrustAgents to trigger a
- // refresh of installed agents.
- mLockPatternUtils.setEnabledTrustAgents(agents, userId);
- }
-
boolean isDeviceLockedInner(int userId) {
synchronized (mDeviceLockedForUser) {
return mDeviceLockedForUser.get(userId, true);
@@ -948,7 +1026,6 @@
continue;
}
allowedAgents.add(resolveInfo);
- if (DEBUG) Slog.d(TAG, "Adding agent " + getComponentName(resolveInfo));
}
return allowedAgents;
}
@@ -970,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<>();
@@ -1178,13 +1270,6 @@
}
@Override
- public void enableTrustAgentForUserForTest(ComponentName componentName, int userId)
- throws RemoteException {
- enforceReportPermission();
- mHandler.obtainMessage(MSG_ENABLE_TRUST_AGENT, userId, 0, componentName).sendToTarget();
- }
-
- @Override
public void reportKeyguardShowingChanged() throws RemoteException {
enforceReportPermission();
// coalesce refresh messages.
@@ -1460,9 +1545,6 @@
// This is also called when the security mode of a user changes.
refreshDeviceLockedForUser(UserHandle.USER_ALL);
break;
- case MSG_ENABLE_TRUST_AGENT:
- enableTrustAgentForUserForTest((ComponentName) msg.obj, msg.arg1);
- break;
case MSG_KEYGUARD_SHOWING_CHANGED:
refreshDeviceLockedForUser(mCurrentUser);
break;
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 302ab239..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;
@@ -223,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;
@@ -6848,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;
}
}
@@ -10954,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;
}
@@ -10981,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) {
@@ -11017,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");
@@ -11073,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) {
@@ -11096,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);
}
@@ -11115,8 +11126,6 @@
@GuardedBy("getLockObject()")
private void setLogoutUserIdLocked(@UserIdInt int userId) {
- if (!mInjector.userManagerIsHeadlessSystemUserMode()) return; // ignore
-
if (userId == UserHandle.USER_CURRENT) {
userId = getCurrentForegroundUserId();
}
@@ -11199,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);
@@ -11213,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;
}
@@ -11603,6 +11618,7 @@
final CallerIdentity caller = getCallerIdentity(who);
Preconditions.checkCallAuthorization(isDefaultDeviceOwner(caller)
+ || isFinancedDeviceOwner(caller)
|| isProfileOwner(caller)
|| (parent && isProfileOwnerOfOrganizationOwnedDevice(caller)));
@@ -13978,7 +13994,7 @@
synchronized (getLockObject()) {
if (isFinancedDeviceOwner(caller)) {
- enforceCanSetPermissionGrantOnFinancedDevice(packageName, permission);
+ enforcePermissionGrantStateOnFinancedDevice(packageName, permission);
}
long ident = mInjector.binderClearCallingIdentity();
try {
@@ -14039,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");
}
}
@@ -14055,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())
@@ -18374,12 +18394,16 @@
private void setDeviceOwnerTypeLocked(ComponentName admin,
@DeviceOwnerType int deviceOwnerType) {
String packageName = admin.getPackageName();
+ boolean isAdminTestOnly;
verifyDeviceOwnerTypePreconditionsLocked(admin);
- Preconditions.checkState(!mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
- "The device owner type has already been set for " + packageName);
- mOwners.setDeviceOwnerType(packageName, deviceOwnerType);
+ isAdminTestOnly = isAdminTestOnlyLocked(admin, mOwners.getDeviceOwnerUserId());
+ Preconditions.checkState(isAdminTestOnly
+ || !mOwners.isDeviceOwnerTypeSetForDeviceOwner(packageName),
+ "Test only admins can only set the device owner type more than once");
+
+ mOwners.setDeviceOwnerType(packageName, deviceOwnerType, isAdminTestOnly);
}
@Override
@@ -18555,9 +18579,10 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller),
+ || canQueryAdminPolicy(caller),
"SSID allowlist can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or a system app.");
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
UserHandle.USER_SYSTEM);
@@ -18593,9 +18618,10 @@
final CallerIdentity caller = getCallerIdentity();
Preconditions.checkCallAuthorization(
isDefaultDeviceOwner(caller) || isProfileOwnerOfOrganizationOwnedDevice(caller)
- || isSystemUid(caller),
+ || canQueryAdminPolicy(caller),
"SSID denylist can only be retrieved by a device owner or "
- + "a profile owner on an organization-owned device or a system app.");
+ + "a profile owner on an organization-owned device or "
+ + "an app with the QUERY_ADMIN_POLICY permission.");
synchronized (getLockObject()) {
final ActiveAdmin admin = getDeviceOwnerOrProfileOwnerOfOrganizationOwnedDeviceLocked(
UserHandle.USER_SYSTEM);
@@ -18694,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/Owners.java b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
index 3584728..fe8f223 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/Owners.java
@@ -637,13 +637,15 @@
}
}
- void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType) {
+ void setDeviceOwnerType(String packageName, @DeviceOwnerType int deviceOwnerType,
+ boolean isAdminTestOnly) {
synchronized (mLock) {
if (!hasDeviceOwner()) {
Slog.e(TAG, "Attempting to set a device owner type when there is no device owner");
return;
- } else if (isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
- Slog.e(TAG, "Device owner type for " + packageName + " has already been set");
+ } else if (!isAdminTestOnly && isDeviceOwnerTypeSetForDeviceOwner(packageName)) {
+ Slog.e(TAG, "Setting the device owner type more than once is only allowed"
+ + " for test only admins");
return;
}
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/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 eed2d42..d2358a0 100644
--- a/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
+++ b/services/tests/mockingservicestests/src/com/android/server/app/GameManagerServiceTests.java
@@ -23,20 +23,32 @@
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+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;
import static org.mockito.Mockito.when;
import android.Manifest;
import android.app.GameManager;
import android.app.GameModeInfo;
+import android.app.GameState;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.pm.ApplicationInfo;
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;
import android.os.test.TestLooper;
import android.platform.test.annotations.Presubmit;
import android.provider.DeviceConfig;
@@ -46,6 +58,7 @@
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
+import com.android.server.LocalServices;
import com.android.server.SystemService;
import org.junit.After;
@@ -76,6 +89,8 @@
private TestLooper mTestLooper;
@Mock
private PackageManager mMockPackageManager;
+ @Mock
+ private PowerManagerInternal mMockPowerManager;
// Stolen from ConnectivityServiceTest.MockContext
class MockContext extends ContextWrapper {
@@ -149,19 +164,27 @@
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()))
.thenReturn(applicationInfo);
+ LocalServices.addService(PowerManagerInternal.class, mMockPowerManager);
}
@After
public void tearDown() throws Exception {
+ LocalServices.removeServiceForTest(PowerManagerInternal.class);
GameManagerService gameManagerService = new GameManagerService(mMockContext);
gameManagerService.disableCompatScale(mPackageName);
if (mMockingSession != null) {
@@ -310,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.
*/
@@ -499,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) {
@@ -511,7 +574,7 @@
}
GameManagerService.GamePackageConfiguration config =
gameManagerService.getConfig(mPackageName);
- assertEquals(config.getGameModeConfiguration(gameMode).getFps(), fps);
+ assertEquals(fps, config.getGameModeConfiguration(gameMode).getFps());
}
/**
@@ -893,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();
@@ -1052,4 +1145,41 @@
assertEquals(GameManager.GAME_MODE_UNSUPPORTED, gameModeInfo.getActiveGameMode());
assertEquals(0, gameModeInfo.getAvailableGameModes().length);
}
+
+ @Test
+ public void testGameStateLoadingRequiresPerformanceMode() {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ GameState gameState = new GameState(true, GameState.MODE_NONE);
+ gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+ mTestLooper.dispatchAll();
+ verify(mMockPowerManager, never()).setPowerMode(anyInt(), anyBoolean());
+ }
+
+ private void setGameState(boolean isLoading) {
+ mockDeviceConfigNone();
+ mockModifyGameModeGranted();
+ GameManagerService gameManagerService =
+ new GameManagerService(mMockContext, mTestLooper.getLooper());
+ startUser(gameManagerService, USER_ID_1);
+ gameManagerService.setGameMode(
+ mPackageName, GameManager.GAME_MODE_PERFORMANCE, USER_ID_1);
+ GameState gameState = new GameState(isLoading, GameState.MODE_NONE);
+ gameManagerService.setGameState(mPackageName, gameState, USER_ID_1);
+ mTestLooper.dispatchAll();
+ verify(mMockPowerManager, times(1)).setPowerMode(Mode.GAME_LOADING, isLoading);
+ }
+
+ @Test
+ public void testSetGameStateLoading() {
+ setGameState(true);
+ }
+
+ @Test
+ public void testSetGameStateNotLoading() {
+ setGameState(false);
+ }
}
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/DockObserverTest.java b/services/tests/servicestests/src/com/android/server/DockObserverTest.java
new file mode 100644
index 0000000..c325778
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/DockObserverTest.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 com.android.server;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.content.Intent;
+import android.os.Looper;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableContext;
+import android.testing.TestableLooper;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.internal.R;
+import com.android.internal.util.test.BroadcastInterceptingContext;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.util.concurrent.ExecutionException;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class DockObserverTest {
+
+ @Rule
+ public TestableContext mContext =
+ new TestableContext(ApplicationProvider.getApplicationContext(), null);
+
+ private final BroadcastInterceptingContext mInterceptingContext =
+ new BroadcastInterceptingContext(mContext);
+
+ BroadcastInterceptingContext.FutureIntent updateExtconDockState(DockObserver observer,
+ String extconDockState) {
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ mInterceptingContext.nextBroadcastIntent(Intent.ACTION_DOCK_EVENT);
+ observer.setDockStateFromProviderForTesting(
+ DockObserver.ExtconStateProvider.fromString(extconDockState));
+ TestableLooper.get(this).processAllMessages();
+ return futureIntent;
+ }
+
+ DockObserver observerWithMappingConfig(String[] configEntries) {
+ mContext.getOrCreateTestableResources().addOverride(
+ R.array.config_dockExtconStateMapping,
+ configEntries);
+ return new DockObserver(mInterceptingContext);
+ }
+
+ void assertDockEventIntentWithExtraThenUndock(DockObserver observer, String extconDockState,
+ int expectedExtra) throws ExecutionException, InterruptedException {
+ assertThat(updateExtconDockState(observer, extconDockState)
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(expectedExtra);
+ assertThat(updateExtconDockState(observer, "DOCK=0")
+ .get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_UNDOCKED);
+ }
+
+ @Before
+ public void setUp() {
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ }
+ }
+
+ @Test
+ public void testDockIntentBroadcast_onlyAfterBootReady()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = new DockObserver(mInterceptingContext);
+ BroadcastInterceptingContext.FutureIntent futureIntent =
+ updateExtconDockState(observer, "DOCK=1");
+ updateExtconDockState(observer, "DOCK=1").assertNotReceived();
+ // Last boot phase reached
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ TestableLooper.get(this).processAllMessages();
+ assertThat(futureIntent.get().getIntExtra(Intent.EXTRA_DOCK_STATE, -1))
+ .isEqualTo(Intent.EXTRA_DOCK_STATE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResource()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(
+ new String[] {"2,KEY1=1,KEY2=2", "3,KEY3=3"});
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+
+ // Mapping should not match
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_DESK);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2",
+ Intent.EXTRA_DOCK_STATE_DESK);
+
+ // 1st mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY2=2\nKEY1=1",
+ Intent.EXTRA_DOCK_STATE_CAR);
+
+ // 2nd mapping now matches
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY3=3",
+ Intent.EXTRA_DOCK_STATE_LE_DESK);
+ }
+
+ @Test
+ public void testDockIntentBroadcast_customConfigResourceWithWildcard()
+ throws ExecutionException, InterruptedException {
+ DockObserver observer = observerWithMappingConfig(new String[] {
+ "2,KEY2=2",
+ "3,KEY3=3",
+ "4"
+ });
+ observer.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY);
+ assertDockEventIntentWithExtraThenUndock(observer, "DOCK=1\nKEY5=5",
+ Intent.EXTRA_DOCK_STATE_HE_DESK);
+ }
+}
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..51607e5 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);
@@ -381,18 +395,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 +546,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 64ce6b2..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;
@@ -7737,30 +7739,20 @@
dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
- int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
-
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
initializeDpms();
-
- returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
}
@Test
- public void testSetDeviceOwnerType_asDeviceOwner_throwsExceptionWhenSetDeviceOwnerTypeAgain()
+ public void testSetDeviceOwnerType_asDeviceOwner_setDeviceOwnerTypeTwice_success()
throws Exception {
setDeviceOwner();
+ dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT);
dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_FINANCED);
- int returnedDeviceOwnerType = dpm.getDeviceOwnerType(admin1);
- assertThat(dpms.mOwners.hasDeviceOwner()).isTrue();
- assertThat(returnedDeviceOwnerType).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
-
- assertThrows(IllegalStateException.class,
- () -> dpm.setDeviceOwnerType(admin1, DEVICE_OWNER_TYPE_DEFAULT));
+ assertThat(dpm.getDeviceOwnerType(admin1)).isEqualTo(DEVICE_OWNER_TYPE_FINANCED);
}
@Test
@@ -7789,6 +7781,7 @@
verify(getServices().userManagerInternal, never())
.setDevicePolicyUserRestrictions(anyInt(), any(), any(), anyBoolean());
+ DpmTestUtils.assertRestrictions(new Bundle(), dpm.getUserRestrictions(admin1));
}
}
}
@@ -7820,6 +7813,9 @@
eq(true));
reset(getServices().userManagerInternal);
+ DpmTestUtils.assertRestrictions(DpmTestUtils.newRestrictions(restriction),
+ dpm.getUserRestrictions(admin1));
+
dpm.clearUserRestriction(admin1, restriction);
reset(getServices().userManagerInternal);
}
@@ -8066,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));
@@ -8344,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));
}
@@ -8353,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(
@@ -8369,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(
@@ -8381,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));
}
@@ -8397,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(
@@ -8413,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(
@@ -8425,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/devicepolicy/OwnersTest.java b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
index 02a8ae8..9a5254d 100644
--- a/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
+++ b/services/tests/servicestests/src/com/android/server/devicepolicy/OwnersTest.java
@@ -93,7 +93,7 @@
assertThat(owners.getProfileOwnerUserRestrictionsNeedsMigration(21)).isFalse();
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_FINANCED);
+ DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
// There is no device owner, so the default owner type should be returned.
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_DEFAULT);
@@ -367,7 +367,7 @@
owners.setDeviceOwnerUserRestrictionsMigrated();
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_FINANCED);
+ DEVICE_OWNER_TYPE_FINANCED, /* isAdminTestOnly= */ false);
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
@@ -399,7 +399,7 @@
owners.setProfileOwnerUserRestrictionsMigrated(11);
owners.setDeviceOwnerType(owners.getDeviceOwnerPackageName(),
- DEVICE_OWNER_TYPE_DEFAULT);
+ DEVICE_OWNER_TYPE_DEFAULT, /* isAdminTestOnly= */ false);
// The previous device owner type should persist.
assertThat(owners.getDeviceOwnerType(owners.getDeviceOwnerPackageName())).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
@@ -585,7 +585,8 @@
assertThat(owners.getProfileOwnerFile(11).exists()).isTrue();
String previousDeviceOwnerPackageName = owners.getDeviceOwnerPackageName();
- owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED);
+ owners.setDeviceOwnerType(previousDeviceOwnerPackageName, DEVICE_OWNER_TYPE_FINANCED,
+ /* isAdminTestOnly= */ false);
assertThat(owners.getDeviceOwnerType(previousDeviceOwnerPackageName)).isEqualTo(
DEVICE_OWNER_TYPE_FINANCED);
owners.setDeviceOwnerProtectedPackages(
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/BroadcastResponseStatsTracker.java b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
index e655013..27e8d69 100644
--- a/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
+++ b/services/usage/java/com/android/server/usage/BroadcastResponseStatsTracker.java
@@ -16,6 +16,8 @@
package com.android.server.usage;
+import static android.app.ActivityManager.procStateToString;
+
import static com.android.server.usage.UsageStatsService.DEBUG_RESPONSE_STATS;
import android.annotation.ElapsedRealtimeLong;
@@ -23,6 +25,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
+import android.app.ActivityManager.ProcessState;
import android.app.usage.BroadcastResponseStats;
import android.os.UserHandle;
import android.text.TextUtils;
@@ -78,12 +81,17 @@
// TODO (206518114): Move all callbacks handling to a handler thread.
void reportBroadcastDispatchEvent(int sourceUid, @NonNull String targetPackage,
UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs) {
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
if (DEBUG_RESPONSE_STATS) {
- Slog.d(TAG, TextUtils.formatSimple(
- "reportBroadcastDispatchEvent; srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s",
+ Slog.d(TAG, TextUtils.formatSimple("reportBroadcastDispatchEvent; "
+ + "srcUid=%d, tgtPkg=%s, tgtUsr=%d, id=%d, ts=%s, state=%s",
sourceUid, targetPackage, targetUser, idForResponseEvent,
- TimeUtils.formatDuration(timestampMs)));
+ TimeUtils.formatDuration(timestampMs), procStateToString(targetUidProcState)));
+ }
+ if (targetUidProcState <= mAppStandby.getBroadcastResponseFgThresholdState()) {
+ // No need to track the broadcast response state while the target app is
+ // in the foreground.
+ return;
}
synchronized (mLock) {
final LongSparseArray<BroadcastEvent> broadcastEvents =
diff --git a/services/usage/java/com/android/server/usage/UsageStatsService.java b/services/usage/java/com/android/server/usage/UsageStatsService.java
index 6906f20..4a761a7 100644
--- a/services/usage/java/com/android/server/usage/UsageStatsService.java
+++ b/services/usage/java/com/android/server/usage/UsageStatsService.java
@@ -42,6 +42,7 @@
import android.annotation.SuppressLint;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
+import android.app.ActivityManager.ProcessState;
import android.app.AppOpsManager;
import android.app.IUidObserver;
import android.app.PendingIntent;
@@ -1813,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);
@@ -3042,9 +3044,9 @@
@Override
public void reportBroadcastDispatched(int sourceUid, @NonNull String targetPackage,
@NonNull UserHandle targetUser, long idForResponseEvent,
- @ElapsedRealtimeLong long timestampMs) {
+ @ElapsedRealtimeLong long timestampMs, @ProcessState int targetUidProcState) {
mResponseStatsTracker.reportBroadcastDispatchEvent(sourceUid, targetPackage,
- targetUser, idForResponseEvent, timestampMs);
+ targetUser, idForResponseEvent, timestampMs, targetUidProcState);
}
@Override
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