Merge "Tweak generated ctor to allow building from Launcher" into sc-dev
diff --git a/apct-tests/perftests/core/src/android/os/OWNERS b/apct-tests/perftests/core/src/android/os/OWNERS
new file mode 100644
index 0000000..a1719c9
--- /dev/null
+++ b/apct-tests/perftests/core/src/android/os/OWNERS
@@ -0,0 +1 @@
+per-file PackageParsingPerfTest.kt = file:/services/core/java/com/android/server/pm/OWNERS
\ No newline at end of file
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
index ed55f00..3bbc945 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java
@@ -64,7 +64,7 @@
public void onStart() {
publishBinderService(Context.APP_SEARCH_SERVICE, new Stub());
mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class);
- mImplInstanceManager = new ImplInstanceManager(getContext());
+ mImplInstanceManager = ImplInstanceManager.getInstance(getContext());
}
private class Stub extends IAppSearchManager.Stub {
@@ -102,7 +102,8 @@
}
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
}
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.setSchema(
packageName,
databaseName,
@@ -133,7 +134,8 @@
final long callingIdentity = Binder.clearCallingIdentity();
try {
verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
List<AppSearchSchema> schemas = impl.getSchema(packageName, databaseName);
List<Bundle> schemaBundles = new ArrayList<>(schemas.size());
for (int i = 0; i < schemas.size(); i++) {
@@ -166,7 +168,8 @@
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
for (int i = 0; i < documentBundles.size(); i++) {
GenericDocument document = new GenericDocument(documentBundles.get(i));
try {
@@ -207,12 +210,18 @@
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Bundle> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
for (int i = 0; i < uris.size(); i++) {
String uri = uris.get(i);
try {
- GenericDocument document = impl.getDocument(packageName, databaseName,
- namespace, uri, typePropertyPaths);
+ GenericDocument document =
+ impl.getDocument(
+ packageName,
+ databaseName,
+ namespace,
+ uri,
+ typePropertyPaths);
resultBuilder.setSuccess(uri, document.getBundle());
} catch (Throwable t) {
resultBuilder.setResult(uri, throwableToFailedResult(t));
@@ -245,7 +254,8 @@
final long callingIdentity = Binder.clearCallingIdentity();
try {
verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
SearchResultPage searchResultPage =
impl.query(
packageName,
@@ -278,12 +288,14 @@
final long callingIdentity = Binder.clearCallingIdentity();
try {
verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
- SearchResultPage searchResultPage = impl.globalQuery(
- queryExpression,
- new SearchSpec(searchSpecBundle),
- packageName,
- callingUid);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ SearchResultPage searchResultPage =
+ impl.globalQuery(
+ queryExpression,
+ new SearchSpec(searchSpecBundle),
+ packageName,
+ callingUid);
invokeCallbackOnResult(
callback,
AppSearchResult.newSuccessfulResult(searchResultPage.getBundle()));
@@ -306,7 +318,8 @@
// TODO(b/162450968) check nextPageToken is being advanced by the same uid as originally
// opened it
try {
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
SearchResultPage searchResultPage = impl.getNextPage(nextPageToken);
invokeCallbackOnResult(
callback,
@@ -324,7 +337,8 @@
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.invalidateNextPageToken(nextPageToken);
} catch (Throwable t) {
Log.e(TAG, "Unable to invalidate the query page token", t);
@@ -350,15 +364,11 @@
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
- impl.reportUsage(
- packageName,
- databaseName,
- namespace,
- uri,
- usageTimeMillis);
- invokeCallbackOnResult(callback,
- AppSearchResult.newSuccessfulResult(/*result=*/ null));
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
+ impl.reportUsage(packageName, databaseName, namespace, uri, usageTimeMillis);
+ invokeCallbackOnResult(
+ callback, AppSearchResult.newSuccessfulResult(/*result=*/ null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
} finally {
@@ -385,7 +395,8 @@
verifyCallingPackage(callingUid, packageName);
AppSearchBatchResult.Builder<String, Void> resultBuilder =
new AppSearchBatchResult.Builder<>();
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
for (int i = 0; i < uris.size(); i++) {
String uri = uris.get(i);
try {
@@ -421,7 +432,8 @@
final long callingIdentity = Binder.clearCallingIdentity();
try {
verifyCallingPackage(callingUid, packageName);
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.removeByQuery(
packageName,
databaseName,
@@ -441,7 +453,8 @@
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- AppSearchImpl impl = mImplInstanceManager.getInstance(callingUserId);
+ AppSearchImpl impl =
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
impl.persistToDisk();
} catch (Throwable t) {
Log.e(TAG, "Unable to persist the data to disk", t);
@@ -457,7 +470,7 @@
int callingUserId = handleIncomingUser(userId, callingUid);
final long callingIdentity = Binder.clearCallingIdentity();
try {
- mImplInstanceManager.getInstance(callingUserId);
+ mImplInstanceManager.getAppSearchImpl(getContext(), callingUserId);
invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null));
} catch (Throwable t) {
invokeCallbackOnError(callback, t);
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
index fe3c2e1..97b1a8c 100644
--- a/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
+++ b/apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java
@@ -41,14 +41,33 @@
public final class ImplInstanceManager {
private static final String APP_SEARCH_DIR = "appSearch";
- private static final SparseArray<AppSearchImpl> sInstances = new SparseArray<>();
+ private static ImplInstanceManager sImplInstanceManager;
- private final Context mContext;
+ private final SparseArray<AppSearchImpl> mInstances = new SparseArray<>();
private final String mGlobalQuerierPackage;
- public ImplInstanceManager(@NonNull Context context) {
- mContext = context;
- mGlobalQuerierPackage = getGlobalAppSearchDataQuerierPackageName(mContext);
+ private ImplInstanceManager(@NonNull String globalQuerierPackage) {
+ mGlobalQuerierPackage = globalQuerierPackage;
+ }
+
+ /**
+ * Gets an instance of ImplInstanceManager to be used.
+ *
+ * <p>If no instance has been initialized yet, a new one will be created. Otherwise, the
+ * existing instance will be returned.
+ */
+ @NonNull
+ public static ImplInstanceManager getInstance(@NonNull Context context) {
+ if (sImplInstanceManager == null) {
+ synchronized (ImplInstanceManager.class) {
+ if (sImplInstanceManager == null) {
+ sImplInstanceManager =
+ new ImplInstanceManager(
+ getGlobalAppSearchDataQuerierPackageName(context));
+ }
+ }
+ }
+ return sImplInstanceManager;
}
/**
@@ -57,30 +76,30 @@
* <p>If no AppSearchImpl instance exists for this user, Icing will be initialized and one will
* be created.
*
+ * @param context The context
* @param userId The multi-user userId of the device user calling AppSearch
* @return An initialized {@link AppSearchImpl} for this user
*/
@NonNull
- public AppSearchImpl getInstance(@UserIdInt int userId)
+ public AppSearchImpl getAppSearchImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
- AppSearchImpl instance = sInstances.get(userId);
+ AppSearchImpl instance = mInstances.get(userId);
if (instance == null) {
synchronized (ImplInstanceManager.class) {
- instance = sInstances.get(userId);
+ instance = mInstances.get(userId);
if (instance == null) {
- instance = createImpl(userId);
- sInstances.put(userId, instance);
+ instance = createImpl(context, userId);
+ mInstances.put(userId, instance);
}
}
}
return instance;
}
- private AppSearchImpl createImpl(@UserIdInt int userId)
+ private AppSearchImpl createImpl(@NonNull Context context, @UserIdInt int userId)
throws AppSearchException {
- File appSearchDir = getAppSearchDir(mContext, userId);
- return AppSearchImpl.create(
- appSearchDir, mContext, userId, mGlobalQuerierPackage);
+ File appSearchDir = getAppSearchDir(context, userId);
+ return AppSearchImpl.create(appSearchDir, context, userId, mGlobalQuerierPackage);
}
private static File getAppSearchDir(@NonNull Context context, @UserIdInt int userId) {
@@ -96,7 +115,8 @@
*
* @param context Context of the system service.
*/
- private static String getGlobalAppSearchDataQuerierPackageName(Context context) {
+ @NonNull
+ private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) {
String globalAppSearchDataQuerierPackage =
context.getString(R.string.config_globalAppSearchDataQuerierPackage);
try {
diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
index d44169d1..d4ee9bb 100644
--- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java
@@ -151,8 +151,7 @@
static final boolean DEBUG_BG_LIMIT = localLOGV || false;
static final boolean DEBUG_STANDBY = localLOGV || false;
static final boolean RECORD_ALARMS_IN_HISTORY = true;
- // TODO (b/178484639): Turn off once allow-while-idle revamp is completed.
- static final boolean RECORD_DEVICE_IDLE_ALARMS = true;
+ static final boolean RECORD_DEVICE_IDLE_ALARMS = false;
static final String TIMEZONE_PROPERTY = "persist.sys.timezone";
static final int TICK_HISTORY_DEPTH = 10;
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
index dba1d4b..e05f0b0 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobConcurrencyManager.java
@@ -16,6 +16,8 @@
package com.android.server.job;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.job.JobInfo;
import android.content.BroadcastReceiver;
@@ -25,8 +27,12 @@
import android.os.Handler;
import android.os.PowerManager;
import android.os.RemoteException;
+import android.provider.DeviceConfig;
+import android.util.ArraySet;
import android.util.IndentingPrintWriter;
+import android.util.Pair;
import android.util.Slog;
+import android.util.SparseIntArray;
import android.util.TimeUtils;
import android.util.proto.ProtoOutputStream;
@@ -35,25 +41,43 @@
import com.android.internal.app.procstats.ProcessStats;
import com.android.internal.util.StatLogger;
import com.android.server.JobSchedulerBackgroundThread;
-import com.android.server.job.JobSchedulerService.Constants;
-import com.android.server.job.JobSchedulerService.MaxJobCountsPerMemoryTrimLevel;
import com.android.server.job.controllers.JobStatus;
import com.android.server.job.controllers.StateController;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.Iterator;
import java.util.List;
/**
- * This class decides, given the various configuration and the system status, how many more jobs
- * can start.
+ * This class decides, given the various configuration and the system status, which jobs can start
+ * and which {@link JobServiceContext} to run each job on.
*/
class JobConcurrencyManager {
private static final String TAG = JobSchedulerService.TAG;
private static final boolean DEBUG = JobSchedulerService.DEBUG;
+ static final String CONFIG_KEY_PREFIX_CONCURRENCY = "concurrency_";
+ private static final String KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "screen_off_adjustment_delay_ms";
+ private static final long DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS = 30_000;
+
+ static final int WORK_TYPE_NONE = 0;
+ static final int WORK_TYPE_TOP = 1 << 0;
+ static final int WORK_TYPE_BG = 1 << 1;
+ private static final int NUM_WORK_TYPES = 2;
+
+ @IntDef(prefix = {"WORK_TYPE_"}, flag = true, value = {
+ WORK_TYPE_NONE,
+ WORK_TYPE_TOP,
+ WORK_TYPE_BG
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface WorkType {
+ }
+
private final Object mLock;
private final JobSchedulerService mService;
- private final JobSchedulerService.Constants mConstants;
private final Context mContext;
private final Handler mHandler;
@@ -67,6 +91,53 @@
private static final int MAX_JOB_CONTEXTS_COUNT = JobSchedulerService.MAX_JOB_CONTEXTS_COUNT;
+ private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_ON =
+ new WorkConfigLimitsPerMemoryTrimLevel(
+ new WorkTypeConfig("screen_on_normal", 8,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 2), Pair.create(WORK_TYPE_BG, 2)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 6))),
+ new WorkTypeConfig("screen_on_moderate", 8,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 4))),
+ new WorkTypeConfig("screen_on_low", 5,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 1))),
+ new WorkTypeConfig("screen_on_critical", 5,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 1)))
+ );
+ private static final WorkConfigLimitsPerMemoryTrimLevel CONFIG_LIMITS_SCREEN_OFF =
+ new WorkConfigLimitsPerMemoryTrimLevel(
+ new WorkTypeConfig("screen_off_normal", 10,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 2)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 6))),
+ new WorkTypeConfig("screen_off_moderate", 10,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 6), Pair.create(WORK_TYPE_BG, 2)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 4))),
+ new WorkTypeConfig("screen_off_low", 5,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 1))),
+ new WorkTypeConfig("screen_off_critical", 5,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, 4), Pair.create(WORK_TYPE_BG, 1)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, 1)))
+ );
+
/**
* This array essentially stores the state of mActiveServices array.
* The ith index stores the job present on the ith JobServiceContext.
@@ -79,10 +150,14 @@
int[] mRecycledPreferredUidForContext = new int[MAX_JOB_CONTEXTS_COUNT];
- /** Max job counts according to the current system state. */
- private JobSchedulerService.MaxJobCounts mMaxJobCounts;
+ int[] mRecycledWorkTypeForContext = new int[MAX_JOB_CONTEXTS_COUNT];
- private final JobCountTracker mJobCountTracker = new JobCountTracker();
+ private final ArraySet<JobStatus> mRunningJobs = new ArraySet<>();
+
+ private final WorkCountTracker mWorkCountTracker = new WorkCountTracker();
+
+ /** Wait for this long after screen off before adjusting the job concurrency. */
+ private long mScreenOffAdjustmentDelayMs = DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS;
/** Current memory trim level. */
private int mLastMemoryTrimLevel;
@@ -106,7 +181,6 @@
JobConcurrencyManager(JobSchedulerService service) {
mService = service;
mLock = mService.mLock;
- mConstants = service.mConstants;
mContext = service.getContext();
mHandler = JobSchedulerBackgroundThread.getHandler();
@@ -165,8 +239,7 @@
// Note: we can't directly do postDelayed(this::rampUpForScreenOn), because
// we need the exact same instance for removeCallbacks().
- mHandler.postDelayed(mRampUpForScreenOff,
- mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
+ mHandler.postDelayed(mRampUpForScreenOff, mScreenOffAdjustmentDelayMs);
}
}
}
@@ -174,7 +247,7 @@
private final Runnable mRampUpForScreenOff = this::rampUpForScreenOff;
/**
- * Called in {@link Constants#SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS} after
+ * Called in {@link #mScreenOffAdjustmentDelayMs} after
* the screen turns off, in order to increase concurrency.
*/
private void rampUpForScreenOff() {
@@ -188,9 +261,7 @@
return;
}
final long now = JobSchedulerService.sElapsedRealtimeClock.millis();
- if ((mLastScreenOffRealtime
- + mConstants.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS)
- > now) {
+ if ((mLastScreenOffRealtime + mScreenOffAdjustmentDelayMs) > now) {
return;
}
@@ -204,12 +275,6 @@
}
}
- private boolean isFgJob(JobStatus job) {
- // (It's super confusing PRIORITY_BOUND_FOREGROUND_SERVICE isn't FG here)
- return job.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
- || job.shouldTreatAsExpeditedJob();
- }
-
@GuardedBy("mLock")
private void refreshSystemStateLocked() {
final long nowUptime = JobSchedulerService.sUptimeMillisClock.millis();
@@ -232,28 +297,29 @@
}
@GuardedBy("mLock")
- private void updateMaxCountsLocked() {
+ private void updateCounterConfigLocked() {
refreshSystemStateLocked();
- final MaxJobCountsPerMemoryTrimLevel jobCounts = mEffectiveInteractiveState
- ? mConstants.MAX_JOB_COUNTS_SCREEN_ON
- : mConstants.MAX_JOB_COUNTS_SCREEN_OFF;
+ final WorkConfigLimitsPerMemoryTrimLevel workConfigs = mEffectiveInteractiveState
+ ? CONFIG_LIMITS_SCREEN_ON : CONFIG_LIMITS_SCREEN_OFF;
-
+ WorkTypeConfig workTypeConfig;
switch (mLastMemoryTrimLevel) {
case ProcessStats.ADJ_MEM_FACTOR_MODERATE:
- mMaxJobCounts = jobCounts.moderate;
+ workTypeConfig = workConfigs.moderate;
break;
case ProcessStats.ADJ_MEM_FACTOR_LOW:
- mMaxJobCounts = jobCounts.low;
+ workTypeConfig = workConfigs.low;
break;
case ProcessStats.ADJ_MEM_FACTOR_CRITICAL:
- mMaxJobCounts = jobCounts.critical;
+ workTypeConfig = workConfigs.critical;
break;
default:
- mMaxJobCounts = jobCounts.normal;
+ workTypeConfig = workConfigs.normal;
break;
}
+
+ mWorkCountTracker.setConfig(workTypeConfig);
}
/**
@@ -277,31 +343,26 @@
Slog.d(TAG, printPendingQueueLocked());
}
- final JobPackageTracker tracker = mService.mJobPackageTracker;
final List<JobStatus> pendingJobs = mService.mPendingJobs;
final List<JobServiceContext> activeServices = mService.mActiveServices;
- final List<StateController> controllers = mService.mControllers;
-
- updateMaxCountsLocked();
// To avoid GC churn, we recycle the arrays.
JobStatus[] contextIdToJobMap = mRecycledAssignContextIdToJobMap;
boolean[] slotChanged = mRecycledSlotChanged;
int[] preferredUidForContext = mRecycledPreferredUidForContext;
+ int[] workTypeForContext = mRecycledWorkTypeForContext;
+ updateCounterConfigLocked();
+ // Reset everything since we'll re-evaluate the current state.
+ mWorkCountTracker.resetCounts();
- // Initialize the work variables and also count running jobs.
- mJobCountTracker.reset(
- mMaxJobCounts.getMaxTotal(),
- mMaxJobCounts.getMaxBg(),
- mMaxJobCounts.getMinBg());
-
- for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+ for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
final JobServiceContext js = mService.mActiveServices.get(i);
final JobStatus status = js.getRunningJobLocked();
if ((contextIdToJobMap[i] = status) != null) {
- mJobCountTracker.incrementRunningJobCount(isFgJob(status));
+ mWorkCountTracker.incrementRunningJobCount(js.getRunningJobWorkType());
+ workTypeForContext[i] = js.getRunningJobWorkType();
}
slotChanged[i] = false;
@@ -312,41 +373,25 @@
}
// Next, update the job priorities, and also count the pending FG / BG jobs.
- for (int i = 0; i < pendingJobs.size(); i++) {
- final JobStatus pending = pendingJobs.get(i);
+ updateNonRunningPriorities(pendingJobs, true);
- // If job is already running, go to next job.
- int jobRunningContext = findJobContextIdFromMap(pending, contextIdToJobMap);
- if (jobRunningContext != -1) {
- continue;
- }
-
- final int priority = mService.evaluateJobPriorityLocked(pending);
- pending.lastEvaluatedPriority = priority;
-
- mJobCountTracker.incrementPendingJobCount(isFgJob(pending));
- }
-
- mJobCountTracker.onCountDone();
+ mWorkCountTracker.onCountDone();
for (int i = 0; i < pendingJobs.size(); i++) {
final JobStatus nextPending = pendingJobs.get(i);
- // Unfortunately we need to repeat this relatively expensive check.
- int jobRunningContext = findJobContextIdFromMap(nextPending, contextIdToJobMap);
- if (jobRunningContext != -1) {
+ if (mRunningJobs.contains(nextPending)) {
continue;
}
// TODO(171305774): make sure HPJs aren't pre-empted and add dedicated contexts for them
- final boolean isPendingFg = isFgJob(nextPending);
-
// Find an available slot for nextPending. The context should be available OR
// it should have lowest priority among all running jobs
// (sharing the same Uid as nextPending)
int minPriorityForPreemption = Integer.MAX_VALUE;
int selectedContextId = -1;
+ int workType = mWorkCountTracker.canJobStart(getJobWorkTypes(nextPending));
boolean startingJob = false;
for (int j=0; j<MAX_JOB_CONTEXTS_COUNT; j++) {
JobStatus job = contextIdToJobMap[j];
@@ -355,7 +400,7 @@
final boolean preferredUidOkay = (preferredUid == nextPending.getUid())
|| (preferredUid == JobServiceContext.NO_PREFERRED_UID);
- if (preferredUidOkay && mJobCountTracker.canJobStart(isPendingFg)) {
+ if (preferredUidOkay && workType != WORK_TYPE_NONE) {
// This slot is free, and we haven't yet hit the limit on
// concurrent jobs... we can just throw the job in to here.
selectedContextId = j;
@@ -391,19 +436,19 @@
}
if (startingJob) {
// Increase the counters when we're going to start a job.
- mJobCountTracker.onStartingNewJob(isPendingFg);
+ workTypeForContext[selectedContextId] = workType;
+ mWorkCountTracker.stageJob(workType);
}
}
if (DEBUG) {
Slog.d(TAG, printContextIdToJobMap(contextIdToJobMap, "running jobs final"));
}
- mJobCountTracker.logStatus();
+ if (DEBUG) {
+ Slog.d(TAG, "assignJobsToContexts: " + mWorkCountTracker.toString());
+ }
- tracker.noteConcurrency(mJobCountTracker.getTotalRunningJobCountToNote(),
- mJobCountTracker.getFgRunningJobCountToNote());
-
- for (int i=0; i<MAX_JOB_CONTEXTS_COUNT; i++) {
+ for (int i = 0; i < MAX_JOB_CONTEXTS_COUNT; i++) {
boolean preservePreferredUid = false;
if (slotChanged[i]) {
JobStatus js = activeServices.get(i).getRunningJobLocked();
@@ -421,30 +466,58 @@
Slog.d(TAG, "About to run job on context "
+ i + ", job: " + pendingJob);
}
- for (int ic=0; ic<controllers.size(); ic++) {
- controllers.get(ic).prepareForExecutionLocked(pendingJob);
- }
- if (!activeServices.get(i).executeRunnableJob(pendingJob)) {
- Slog.d(TAG, "Error executing " + pendingJob);
- }
- if (pendingJobs.remove(pendingJob)) {
- tracker.noteNonpending(pendingJob);
- }
+ startJobLocked(activeServices.get(i), pendingJob, workTypeForContext[i]);
}
}
if (!preservePreferredUid) {
activeServices.get(i).clearPreferredUid();
}
}
+ mWorkCountTracker.resetStagingCount();
+ noteConcurrency();
}
- private static int findJobContextIdFromMap(JobStatus jobStatus, JobStatus[] map) {
- for (int i=0; i<map.length; i++) {
- if (map[i] != null && map[i].matches(jobStatus.getUid(), jobStatus.getJobId())) {
- return i;
+ private void noteConcurrency() {
+ mService.mJobPackageTracker.noteConcurrency(mRunningJobs.size(),
+ // TODO: log per type instead of only TOP
+ mWorkCountTracker.getRunningJobCount(WORK_TYPE_TOP));
+ }
+
+ private void updateNonRunningPriorities(@NonNull final List<JobStatus> pendingJobs,
+ boolean updateCounter) {
+ for (int i = 0; i < pendingJobs.size(); i++) {
+ final JobStatus pending = pendingJobs.get(i);
+
+ // If job is already running, go to next job.
+ if (mRunningJobs.contains(pending)) {
+ continue;
+ }
+
+ pending.lastEvaluatedPriority = mService.evaluateJobPriorityLocked(pending);
+
+ if (updateCounter) {
+ mWorkCountTracker.incrementPendingJobCount(getJobWorkTypes(pending));
}
}
- return -1;
+ }
+
+ private void startJobLocked(@NonNull JobServiceContext worker, @NonNull JobStatus jobStatus,
+ @WorkType final int workType) {
+ final List<StateController> controllers = mService.mControllers;
+ for (int ic = 0; ic < controllers.size(); ic++) {
+ controllers.get(ic).prepareForExecutionLocked(jobStatus);
+ }
+ if (!worker.executeRunnableJob(jobStatus, workType)) {
+ Slog.e(TAG, "Error executing " + jobStatus);
+ mWorkCountTracker.onStagedJobFailed(workType);
+ } else {
+ mRunningJobs.add(jobStatus);
+ mWorkCountTracker.onJobStarted(workType);
+ }
+ final List<JobStatus> pendingJobs = mService.mPendingJobs;
+ if (pendingJobs.remove(jobStatus)) {
+ mService.mJobPackageTracker.noteNonpending(jobStatus);
+ }
}
@GuardedBy("mLock")
@@ -473,12 +546,42 @@
return s.toString();
}
+ void updateConfigLocked() {
+ DeviceConfig.Properties properties =
+ DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER);
+
+ mScreenOffAdjustmentDelayMs = properties.getLong(
+ KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, DEFAULT_SCREEN_OFF_ADJUSTMENT_DELAY_MS);
+
+ CONFIG_LIMITS_SCREEN_ON.normal.update(properties);
+ CONFIG_LIMITS_SCREEN_ON.moderate.update(properties);
+ CONFIG_LIMITS_SCREEN_ON.low.update(properties);
+ CONFIG_LIMITS_SCREEN_ON.critical.update(properties);
+
+ CONFIG_LIMITS_SCREEN_OFF.normal.update(properties);
+ CONFIG_LIMITS_SCREEN_OFF.moderate.update(properties);
+ CONFIG_LIMITS_SCREEN_OFF.low.update(properties);
+ CONFIG_LIMITS_SCREEN_OFF.critical.update(properties);
+ }
public void dumpLocked(IndentingPrintWriter pw, long now, long nowRealtime) {
pw.println("Concurrency:");
pw.increaseIndent();
try {
+ pw.print("Configuration:");
+ pw.increaseIndent();
+ pw.print(KEY_SCREEN_OFF_ADJUSTMENT_DELAY_MS, mScreenOffAdjustmentDelayMs).println();
+ CONFIG_LIMITS_SCREEN_ON.normal.dump(pw);
+ CONFIG_LIMITS_SCREEN_ON.moderate.dump(pw);
+ CONFIG_LIMITS_SCREEN_ON.low.dump(pw);
+ CONFIG_LIMITS_SCREEN_ON.critical.dump(pw);
+ CONFIG_LIMITS_SCREEN_OFF.normal.dump(pw);
+ CONFIG_LIMITS_SCREEN_OFF.moderate.dump(pw);
+ CONFIG_LIMITS_SCREEN_OFF.low.dump(pw);
+ CONFIG_LIMITS_SCREEN_OFF.critical.dump(pw);
+ pw.decreaseIndent();
+
pw.print("Screen state: current ");
pw.print(mCurrentInteractiveState ? "ON" : "OFF");
pw.print(" effective ");
@@ -497,7 +600,7 @@
pw.println("Current max jobs:");
pw.println(" ");
- pw.println(mJobCountTracker);
+ pw.println(mWorkCountTracker);
pw.println();
@@ -523,8 +626,6 @@
proto.write(JobConcurrencyManagerProto.TIME_SINCE_LAST_SCREEN_OFF_MS,
nowRealtime - mLastScreenOffRealtime);
- mJobCountTracker.dumpProto(proto, JobConcurrencyManagerProto.JOB_COUNT_TRACKER);
-
proto.write(JobConcurrencyManagerProto.MEMORY_TRIM_LEVEL, mLastMemoryTrimLevel);
mStatLogger.dumpProto(proto, JobConcurrencyManagerProto.STATS);
@@ -532,199 +633,312 @@
proto.end(token);
}
+ int getJobWorkTypes(@NonNull JobStatus js) {
+ int classification = 0;
+ // TODO(171305774): create dedicated work type for EJ and FGS
+ if (js.lastEvaluatedPriority >= JobInfo.PRIORITY_TOP_APP
+ || js.shouldTreatAsExpeditedJob()) {
+ classification |= WORK_TYPE_TOP;
+ } else {
+ classification |= WORK_TYPE_BG;
+ }
+ return classification;
+ }
+
+ @VisibleForTesting
+ static class WorkTypeConfig {
+ private static final String KEY_PREFIX_MAX_TOTAL =
+ CONFIG_KEY_PREFIX_CONCURRENCY + "max_total_";
+ private static final String KEY_PREFIX_MAX_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "max_top_";
+ private static final String KEY_PREFIX_MAX_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "max_bg_";
+ private static final String KEY_PREFIX_MIN_TOP = CONFIG_KEY_PREFIX_CONCURRENCY + "min_top_";
+ private static final String KEY_PREFIX_MIN_BG = CONFIG_KEY_PREFIX_CONCURRENCY + "min_bg_";
+ private final String mConfigIdentifier;
+
+ private int mMaxTotal;
+ private final SparseIntArray mMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final int mDefaultMaxTotal;
+ private final SparseIntArray mDefaultMinReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mDefaultMaxAllowedSlots = new SparseIntArray(NUM_WORK_TYPES);
+
+ WorkTypeConfig(@NonNull String configIdentifier, int defaultMaxTotal,
+ List<Pair<Integer, Integer>> defaultMin, List<Pair<Integer, Integer>> defaultMax) {
+ mConfigIdentifier = configIdentifier;
+ mDefaultMaxTotal = mMaxTotal = defaultMaxTotal;
+ for (int i = defaultMin.size() - 1; i >= 0; --i) {
+ mDefaultMinReservedSlots.put(defaultMin.get(i).first, defaultMin.get(i).second);
+ }
+ for (int i = defaultMax.size() - 1; i >= 0; --i) {
+ mDefaultMaxAllowedSlots.put(defaultMax.get(i).first, defaultMax.get(i).second);
+ }
+ update(new DeviceConfig.Properties.Builder(
+ DeviceConfig.NAMESPACE_JOB_SCHEDULER).build());
+ }
+
+ void update(@NonNull DeviceConfig.Properties properties) {
+ // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
+ mMaxTotal = Math.max(1, Math.min(MAX_JOB_CONTEXTS_COUNT,
+ properties.getInt(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mDefaultMaxTotal)));
+
+ mMaxAllowedSlots.clear();
+ // Ensure they're in the range [1, total].
+ final int maxTop = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_TOP + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_TOP, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_TOP, maxTop);
+ final int maxBg = Math.max(1, Math.min(mMaxTotal,
+ properties.getInt(KEY_PREFIX_MAX_BG + mConfigIdentifier,
+ mDefaultMaxAllowedSlots.get(WORK_TYPE_BG, mMaxTotal))));
+ mMaxAllowedSlots.put(WORK_TYPE_BG, maxBg);
+
+ int remaining = mMaxTotal;
+ mMinReservedSlots.clear();
+ // Ensure top is in the range [1, min(maxTop, total)]
+ final int minTop = Math.max(1, Math.min(Math.min(maxTop, mMaxTotal),
+ properties.getInt(KEY_PREFIX_MIN_TOP + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_TOP))));
+ mMinReservedSlots.put(WORK_TYPE_TOP, minTop);
+ remaining -= minTop;
+ // Ensure bg is in the range [0, min(maxBg, remaining)]
+ final int minBg = Math.max(0, Math.min(Math.min(maxBg, remaining),
+ properties.getInt(KEY_PREFIX_MIN_BG + mConfigIdentifier,
+ mDefaultMinReservedSlots.get(WORK_TYPE_BG))));
+ mMinReservedSlots.put(WORK_TYPE_BG, minBg);
+ }
+
+ int getMaxTotal() {
+ return mMaxTotal;
+ }
+
+ int getMax(@WorkType int workType) {
+ return mMaxAllowedSlots.get(workType, mMaxTotal);
+ }
+
+ int getMinReserved(@WorkType int workType) {
+ return mMinReservedSlots.get(workType);
+ }
+
+ void dump(IndentingPrintWriter pw) {
+ pw.print(KEY_PREFIX_MAX_TOTAL + mConfigIdentifier, mMaxTotal).println();
+ pw.print(KEY_PREFIX_MIN_TOP + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_TOP))
+ .println();
+ pw.print(KEY_PREFIX_MAX_TOP + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_TOP))
+ .println();
+ pw.print(KEY_PREFIX_MIN_BG + mConfigIdentifier, mMinReservedSlots.get(WORK_TYPE_BG))
+ .println();
+ pw.print(KEY_PREFIX_MAX_BG + mConfigIdentifier, mMaxAllowedSlots.get(WORK_TYPE_BG))
+ .println();
+ }
+ }
+
+ /** {@link WorkTypeConfig} for each memory trim level. */
+ static class WorkConfigLimitsPerMemoryTrimLevel {
+ public final WorkTypeConfig normal;
+ public final WorkTypeConfig moderate;
+ public final WorkTypeConfig low;
+ public final WorkTypeConfig critical;
+
+ WorkConfigLimitsPerMemoryTrimLevel(WorkTypeConfig normal, WorkTypeConfig moderate,
+ WorkTypeConfig low, WorkTypeConfig critical) {
+ this.normal = normal;
+ this.moderate = moderate;
+ this.low = low;
+ this.critical = critical;
+ }
+ }
+
/**
- * This class decides, taking into account {@link #mMaxJobCounts} and how mny jos are running /
- * pending, how many more job can start.
+ * This class decides, taking into account the current {@link WorkTypeConfig} and how many jobs
+ * are running/pending, how many more job can start.
*
* Extracted for testing and logging.
*/
@VisibleForTesting
- static class JobCountTracker {
- private int mConfigNumMaxTotalJobs;
- private int mConfigNumMaxBgJobs;
- private int mConfigNumMinBgJobs;
+ static class WorkCountTracker {
+ private int mConfigMaxTotal;
+ private final SparseIntArray mConfigNumReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mConfigAbsoluteMaxSlots = new SparseIntArray(NUM_WORK_TYPES);
- private int mNumRunningFgJobs;
- private int mNumRunningBgJobs;
+ /**
+ * Numbers may be lower in this than in {@link #mConfigNumReservedSlots} if there aren't
+ * enough ready jobs of a type to take up all of the desired reserved slots.
+ */
+ private final SparseIntArray mNumActuallyReservedSlots = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mNumPendingJobs = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mNumRunningJobs = new SparseIntArray(NUM_WORK_TYPES);
+ private final SparseIntArray mNumStartingJobs = new SparseIntArray(NUM_WORK_TYPES);
+ private int mNumUnspecialized = 0;
+ private int mNumUnspecializedRemaining = 0;
- private int mNumPendingFgJobs;
- private int mNumPendingBgJobs;
+ void setConfig(@NonNull WorkTypeConfig workTypeConfig) {
+ mConfigMaxTotal = workTypeConfig.getMaxTotal();
+ mConfigNumReservedSlots.put(WORK_TYPE_TOP,
+ workTypeConfig.getMinReserved(WORK_TYPE_TOP));
+ mConfigNumReservedSlots.put(WORK_TYPE_BG, workTypeConfig.getMinReserved(WORK_TYPE_BG));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_TOP, workTypeConfig.getMax(WORK_TYPE_TOP));
+ mConfigAbsoluteMaxSlots.put(WORK_TYPE_BG, workTypeConfig.getMax(WORK_TYPE_BG));
- private int mNumStartingFgJobs;
- private int mNumStartingBgJobs;
-
- private int mNumReservedForBg;
- private int mNumActualMaxFgJobs;
- private int mNumActualMaxBgJobs;
-
- void reset(int numTotalMaxJobs, int numMaxBgJobs, int numMinBgJobs) {
- mConfigNumMaxTotalJobs = numTotalMaxJobs;
- mConfigNumMaxBgJobs = numMaxBgJobs;
- mConfigNumMinBgJobs = numMinBgJobs;
-
- mNumRunningFgJobs = 0;
- mNumRunningBgJobs = 0;
-
- mNumPendingFgJobs = 0;
- mNumPendingBgJobs = 0;
-
- mNumStartingFgJobs = 0;
- mNumStartingBgJobs = 0;
-
- mNumReservedForBg = 0;
- mNumActualMaxFgJobs = 0;
- mNumActualMaxBgJobs = 0;
+ mNumUnspecialized = mConfigMaxTotal;
+ mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_TOP);
+ mNumUnspecialized -= mConfigNumReservedSlots.get(WORK_TYPE_BG);
+ mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP);
+ mNumUnspecialized -= mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG);
+ calculateUnspecializedRemaining();
}
- void incrementRunningJobCount(boolean isFg) {
- if (isFg) {
- mNumRunningFgJobs++;
- } else {
- mNumRunningBgJobs++;
+ private void calculateUnspecializedRemaining() {
+ mNumUnspecializedRemaining = mNumUnspecialized;
+ for (int i = mNumRunningJobs.size() - 1; i >= 0; --i) {
+ mNumUnspecializedRemaining -= mNumRunningJobs.valueAt(i);
}
}
- void incrementPendingJobCount(boolean isFg) {
- if (isFg) {
- mNumPendingFgJobs++;
- } else {
- mNumPendingBgJobs++;
+ void resetCounts() {
+ mNumActuallyReservedSlots.clear();
+ mNumPendingJobs.clear();
+ mNumRunningJobs.clear();
+ resetStagingCount();
+ }
+
+ void resetStagingCount() {
+ mNumStartingJobs.clear();
+ }
+
+ void incrementRunningJobCount(@WorkType int workType) {
+ mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1);
+ }
+
+ void incrementPendingJobCount(int workTypes) {
+ // We don't know which type we'll classify the job as when we run it yet, so make sure
+ // we have space in all applicable slots.
+ if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
+ mNumPendingJobs.put(WORK_TYPE_TOP, mNumPendingJobs.get(WORK_TYPE_TOP) + 1);
+ }
+ if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
+ mNumPendingJobs.put(WORK_TYPE_BG, mNumPendingJobs.get(WORK_TYPE_BG) + 1);
}
}
- void onStartingNewJob(boolean isFg) {
- if (isFg) {
- mNumStartingFgJobs++;
+ void stageJob(@WorkType int workType) {
+ final int newNumStartingJobs = mNumStartingJobs.get(workType) + 1;
+ mNumStartingJobs.put(workType, newNumStartingJobs);
+ mNumPendingJobs.put(workType, Math.max(0, mNumPendingJobs.get(workType) - 1));
+ if (newNumStartingJobs + mNumRunningJobs.get(workType)
+ > mNumActuallyReservedSlots.get(workType)) {
+ mNumUnspecializedRemaining--;
+ }
+ }
+
+ void onStagedJobFailed(@WorkType int workType) {
+ final int oldNumStartingJobs = mNumStartingJobs.get(workType);
+ if (oldNumStartingJobs == 0) {
+ Slog.e(TAG, "# staged jobs for " + workType + " went negative.");
+ // We are in a bad state. We will eventually recover when the pending list is
+ // regenerated.
+ return;
+ }
+ mNumStartingJobs.put(workType, oldNumStartingJobs - 1);
+ maybeAdjustReservations(workType);
+ }
+
+ private void maybeAdjustReservations(@WorkType int workType) {
+ // Always make sure we reserve the minimum number of slots in case new jobs become ready
+ // soon.
+ final int numRemainingForType = Math.max(mConfigNumReservedSlots.get(workType),
+ mNumRunningJobs.get(workType) + mNumStartingJobs.get(workType)
+ + mNumPendingJobs.get(workType));
+ if (numRemainingForType < mNumActuallyReservedSlots.get(workType)) {
+ // We've run all jobs for this type. Let another type use it now.
+ mNumActuallyReservedSlots.put(workType, numRemainingForType);
+ mNumUnspecializedRemaining++;
+ }
+ }
+
+ void onJobStarted(@WorkType int workType) {
+ mNumRunningJobs.put(workType, mNumRunningJobs.get(workType) + 1);
+ final int oldNumStartingJobs = mNumStartingJobs.get(workType);
+ if (oldNumStartingJobs == 0) {
+ Slog.e(TAG, "# stated jobs for " + workType + " went negative.");
+ // We are in a bad state. We will eventually recover when the pending list is
+ // regenerated. For now, only modify the running count.
} else {
- mNumStartingBgJobs++;
+ mNumStartingJobs.put(workType, oldNumStartingJobs - 1);
}
}
void onCountDone() {
- // Note some variables are used only here but are made class members in order to have
- // them on logcat / dumpsys.
+ // Calculate how many slots to reserve for each work type. "Unspecialized" slots will
+ // be reserved for higher importance types first (ie. top before bg).
+ mNumUnspecialized = mConfigMaxTotal;
+ final int numTop = mNumRunningJobs.get(WORK_TYPE_TOP)
+ + mNumPendingJobs.get(WORK_TYPE_TOP);
+ final int resTop = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_TOP), numTop);
+ mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop);
+ mNumUnspecialized -= resTop;
+ final int numBg = mNumRunningJobs.get(WORK_TYPE_BG) + mNumPendingJobs.get(WORK_TYPE_BG);
+ final int resBg = Math.min(mConfigNumReservedSlots.get(WORK_TYPE_BG), numBg);
+ mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg);
+ mNumUnspecialized -= resBg;
+ calculateUnspecializedRemaining();
- // How many slots should we allocate to BG jobs at least?
- // That's basically "getMinBg()", but if there are less jobs, decrease it.
- // (e.g. even if min-bg is 2, if there's only 1 running+pending job, this has to be 1.)
- final int reservedForBg = Math.min(
- mConfigNumMinBgJobs,
- mNumRunningBgJobs + mNumPendingBgJobs);
-
- // However, if there are FG jobs already running, we have to adjust it.
- mNumReservedForBg = Math.min(reservedForBg,
- mConfigNumMaxTotalJobs - mNumRunningFgJobs);
-
- // Max FG is [total - [number needed for BG jobs]]
- // [number needed for BG jobs] is the bigger one of [running BG] or [reserved BG]
- final int maxFg =
- mConfigNumMaxTotalJobs - Math.max(mNumRunningBgJobs, mNumReservedForBg);
-
- // The above maxFg is the theoretical max. If there are less FG jobs, the actual
- // max FG will be lower accordingly.
- mNumActualMaxFgJobs = Math.min(
- maxFg,
- mNumRunningFgJobs + mNumPendingFgJobs);
-
- // Max BG is [total - actual max FG], but cap at [config max BG].
- final int maxBg = Math.min(
- mConfigNumMaxBgJobs,
- mConfigNumMaxTotalJobs - mNumActualMaxFgJobs);
-
- // If there are less BG jobs than maxBg, then reduce the actual max BG accordingly.
- // This isn't needed for the logic to work, but this will give consistent output
- // on logcat and dumpsys.
- mNumActualMaxBgJobs = Math.min(
- maxBg,
- mNumRunningBgJobs + mNumPendingBgJobs);
+ // Assign remaining unspecialized based on ranking.
+ int unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
+ Math.min(mNumUnspecializedRemaining, numTop - resTop)));
+ mNumActuallyReservedSlots.put(WORK_TYPE_TOP, resTop + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
+ unspecializedAssigned = Math.max(0,
+ Math.min(mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
+ Math.min(mNumUnspecializedRemaining, numBg - resBg)));
+ mNumActuallyReservedSlots.put(WORK_TYPE_BG, resBg + unspecializedAssigned);
+ mNumUnspecializedRemaining -= unspecializedAssigned;
}
- boolean canJobStart(boolean isFg) {
- if (isFg) {
- return mNumRunningFgJobs + mNumStartingFgJobs < mNumActualMaxFgJobs;
- } else {
- return mNumRunningBgJobs + mNumStartingBgJobs < mNumActualMaxBgJobs;
+ int canJobStart(int workTypes) {
+ if ((workTypes & WORK_TYPE_TOP) == WORK_TYPE_TOP) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_TOP),
+ mNumActuallyReservedSlots.get(WORK_TYPE_TOP) + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_TOP) + mNumStartingJobs.get(WORK_TYPE_TOP)
+ < maxAllowed) {
+ return WORK_TYPE_TOP;
+ }
}
- }
-
- public int getNumStartingFgJobs() {
- return mNumStartingFgJobs;
- }
-
- public int getNumStartingBgJobs() {
- return mNumStartingBgJobs;
- }
-
- int getTotalRunningJobCountToNote() {
- return mNumRunningFgJobs + mNumRunningBgJobs
- + mNumStartingFgJobs + mNumStartingBgJobs;
- }
-
- int getFgRunningJobCountToNote() {
- return mNumRunningFgJobs + mNumStartingFgJobs;
- }
-
- void logStatus() {
- if (DEBUG) {
- Slog.d(TAG, "assignJobsToContexts: " + this);
+ if ((workTypes & WORK_TYPE_BG) == WORK_TYPE_BG) {
+ final int maxAllowed = Math.min(
+ mConfigAbsoluteMaxSlots.get(WORK_TYPE_BG),
+ mNumActuallyReservedSlots.get(WORK_TYPE_BG) + mNumUnspecializedRemaining);
+ if (mNumRunningJobs.get(WORK_TYPE_BG) + mNumStartingJobs.get(WORK_TYPE_BG)
+ < maxAllowed) {
+ return WORK_TYPE_BG;
+ }
}
+ return WORK_TYPE_NONE;
+ }
+
+ int getRunningJobCount(@WorkType final int workType) {
+ return mNumRunningJobs.get(workType, 0);
}
public String toString() {
- final int totalFg = mNumRunningFgJobs + mNumStartingFgJobs;
- final int totalBg = mNumRunningBgJobs + mNumStartingBgJobs;
- return String.format(
- "Config={tot=%d bg min/max=%d/%d}"
- + " Running[FG/BG (total)]: %d / %d (%d)"
- + " Pending: %d / %d (%d)"
- + " Actual max: %d%s / %d%s (%d%s)"
- + " Res BG: %d"
- + " Starting: %d / %d (%d)"
- + " Total: %d%s / %d%s (%d%s)",
- mConfigNumMaxTotalJobs, mConfigNumMinBgJobs, mConfigNumMaxBgJobs,
+ StringBuilder sb = new StringBuilder();
- mNumRunningFgJobs, mNumRunningBgJobs, mNumRunningFgJobs + mNumRunningBgJobs,
+ sb.append("Config={");
+ sb.append("tot=").append(mConfigMaxTotal);
+ sb.append(" mins=");
+ sb.append(mConfigNumReservedSlots);
+ sb.append(" maxs=");
+ sb.append(mConfigAbsoluteMaxSlots);
+ sb.append("}");
- mNumPendingFgJobs, mNumPendingBgJobs, mNumPendingFgJobs + mNumPendingBgJobs,
+ sb.append(", act res=").append(mNumActuallyReservedSlots);
+ sb.append(", Pending=").append(mNumPendingJobs);
+ sb.append(", Running=").append(mNumRunningJobs);
+ sb.append(", Staged=").append(mNumStartingJobs);
+ sb.append(", # unspecialized remaining=").append(mNumUnspecializedRemaining);
- mNumActualMaxFgJobs, (totalFg <= mConfigNumMaxTotalJobs) ? "" : "*",
- mNumActualMaxBgJobs, (totalBg <= mConfigNumMaxBgJobs) ? "" : "*",
- mNumActualMaxFgJobs + mNumActualMaxBgJobs,
- (mNumActualMaxFgJobs + mNumActualMaxBgJobs <= mConfigNumMaxTotalJobs)
- ? "" : "*",
-
- mNumReservedForBg,
-
- mNumStartingFgJobs, mNumStartingBgJobs, mNumStartingFgJobs + mNumStartingBgJobs,
-
- totalFg, (totalFg <= mNumActualMaxFgJobs) ? "" : "*",
- totalBg, (totalBg <= mNumActualMaxBgJobs) ? "" : "*",
- totalFg + totalBg, (totalFg + totalBg <= mConfigNumMaxTotalJobs) ? "" : "*"
- );
- }
-
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
-
- proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_TOTAL_JOBS, mConfigNumMaxTotalJobs);
- proto.write(JobCountTrackerProto.CONFIG_NUM_MAX_BG_JOBS, mConfigNumMaxBgJobs);
- proto.write(JobCountTrackerProto.CONFIG_NUM_MIN_BG_JOBS, mConfigNumMinBgJobs);
-
- proto.write(JobCountTrackerProto.NUM_RUNNING_FG_JOBS, mNumRunningFgJobs);
- proto.write(JobCountTrackerProto.NUM_RUNNING_BG_JOBS, mNumRunningBgJobs);
-
- proto.write(JobCountTrackerProto.NUM_PENDING_FG_JOBS, mNumPendingFgJobs);
- proto.write(JobCountTrackerProto.NUM_PENDING_BG_JOBS, mNumPendingBgJobs);
-
- proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_FG_JOBS, mNumActualMaxFgJobs);
- proto.write(JobCountTrackerProto.NUM_ACTUAL_MAX_BG_JOBS, mNumActualMaxBgJobs);
-
- proto.write(JobCountTrackerProto.NUM_RESERVED_FOR_BG, mNumReservedForBg);
-
- proto.write(JobCountTrackerProto.NUM_STARTING_FG_JOBS, mNumStartingFgJobs);
- proto.write(JobCountTrackerProto.NUM_STARTING_BG_JOBS, mNumStartingBgJobs);
-
- proto.end(token);
+ return sb.toString();
}
}
}
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 97ba815..ba78bda 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerService.java
@@ -369,12 +369,6 @@
case Constants.KEY_MODERATE_USE_FACTOR:
mConstants.updateUseFactorConstantsLocked();
break;
- case Constants.KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS:
- if (!concurrencyUpdated) {
- mConstants.updateConcurrencyConstantsLocked();
- concurrencyUpdated = true;
- }
- break;
case Constants.KEY_MIN_LINEAR_BACKOFF_TIME_MS:
case Constants.KEY_MIN_EXP_BACKOFF_TIME_MS:
mConstants.updateBackoffConstantsLocked();
@@ -384,10 +378,9 @@
mConstants.updateConnectivityConstantsLocked();
break;
default:
- // Too many max_job_* strings to list.
- if (name.startsWith(Constants.KEY_PREFIX_MAX_JOB)
+ if (name.startsWith(JobConcurrencyManager.CONFIG_KEY_PREFIX_CONCURRENCY)
&& !concurrencyUpdated) {
- mConstants.updateConcurrencyConstantsLocked();
+ mConcurrencyManager.updateConfigLocked();
concurrencyUpdated = true;
} else {
for (int ctrlr = 0; ctrlr < mControllers.size(); ctrlr++) {
@@ -414,119 +407,6 @@
mConstants.API_QUOTA_SCHEDULE_WINDOW_MS);
}
- static class MaxJobCounts {
- private final int mTotalDefault;
- private final String mTotalKey;
- private final int mMaxBgDefault;
- private final String mMaxBgKey;
- private final int mMinBgDefault;
- private final String mMinBgKey;
- private int mTotal;
- private int mMaxBg;
- private int mMinBg;
-
- MaxJobCounts(int totalDefault, String totalKey,
- int maxBgDefault, String maxBgKey, int minBgDefault, String minBgKey) {
- mTotalKey = totalKey;
- mTotal = mTotalDefault = totalDefault;
- mMaxBgKey = maxBgKey;
- mMaxBg = mMaxBgDefault = maxBgDefault;
- mMinBgKey = minBgKey;
- mMinBg = mMinBgDefault = minBgDefault;
- }
-
- public void update() {
- mTotal = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- mTotalKey, mTotalDefault);
- mMaxBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- mMaxBgKey, mMaxBgDefault);
- mMinBg = DeviceConfig.getInt(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- mMinBgKey, mMinBgDefault);
-
- // Ensure total in the range [1, MAX_JOB_CONTEXTS_COUNT].
- mTotal = Math.min(Math.max(1, mTotal), MAX_JOB_CONTEXTS_COUNT);
-
- // Ensure maxBg in the range [1, total].
- mMaxBg = Math.min(Math.max(1, mMaxBg), mTotal);
-
- // Ensure minBg in the range [0, min(maxBg, total - 1)]
- mMinBg = Math.min(Math.max(0, mMinBg), Math.min(mMaxBg, mTotal - 1));
- }
-
- /** Total number of jobs to run simultaneously. */
- public int getMaxTotal() {
- return mTotal;
- }
-
- /** Max number of BG (== owned by non-TOP apps) jobs to run simultaneously. */
- public int getMaxBg() {
- return mMaxBg;
- }
-
- /**
- * We try to run at least this many BG (== owned by non-TOP apps) jobs, when there are any
- * pending, rather than always running the TOTAL number of FG jobs.
- */
- public int getMinBg() {
- return mMinBg;
- }
-
- public void dump(PrintWriter pw, String prefix) {
- pw.print(prefix);
- pw.print(mTotalKey);
- pw.print("=");
- pw.print(mTotal);
- pw.println();
-
- pw.print(prefix);
- pw.print(mMaxBgKey);
- pw.print("=");
- pw.print(mMaxBg);
- pw.println();
-
- pw.print(prefix);
- pw.print(mMinBgKey);
- pw.print("=");
- pw.print(mMinBg);
- pw.println();
- }
-
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- proto.write(MaxJobCountsProto.TOTAL_JOBS, mTotal);
- proto.write(MaxJobCountsProto.MAX_BG, mMaxBg);
- proto.write(MaxJobCountsProto.MIN_BG, mMinBg);
- proto.end(token);
- }
- }
-
- /** {@link MaxJobCounts} for each memory trim level. */
- static class MaxJobCountsPerMemoryTrimLevel {
- public final MaxJobCounts normal;
- public final MaxJobCounts moderate;
- public final MaxJobCounts low;
- public final MaxJobCounts critical;
-
- MaxJobCountsPerMemoryTrimLevel(
- MaxJobCounts normal,
- MaxJobCounts moderate, MaxJobCounts low,
- MaxJobCounts critical) {
- this.normal = normal;
- this.moderate = moderate;
- this.low = low;
- this.critical = critical;
- }
-
- public void dumpProto(ProtoOutputStream proto, long fieldId) {
- final long token = proto.start(fieldId);
- normal.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.NORMAL);
- moderate.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.MODERATE);
- low.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.LOW);
- critical.dumpProto(proto, MaxJobCountsPerMemoryTrimLevelProto.CRITICAL);
- proto.end(token);
- }
- }
-
/**
* All times are in milliseconds. Any access to this class or its fields should be done while
* holding the JobSchedulerService.mLock lock.
@@ -552,9 +432,6 @@
private static final String KEY_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT =
"aq_schedule_return_failure";
- private static final String KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
- "screen_off_job_concurrency_increase_delay_ms";
-
private static final int DEFAULT_MIN_READY_NON_ACTIVE_JOBS_COUNT = 5;
private static final long DEFAULT_MAX_NON_ACTIVE_JOB_BATCH_DELAY_MS = 31 * MINUTE_IN_MILLIS;
private static final float DEFAULT_HEAVY_USE_FACTOR = .9f;
@@ -568,7 +445,6 @@
private static final long DEFAULT_API_QUOTA_SCHEDULE_WINDOW_MS = MINUTE_IN_MILLIS;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_THROW_EXCEPTION = true;
private static final boolean DEFAULT_API_QUOTA_SCHEDULE_RETURN_FAILURE_RESULT = false;
- private static final long DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = 30_000;
/**
* Minimum # of non-ACTIVE jobs for which the JMS will be happy running some work early.
@@ -590,53 +466,6 @@
*/
float MODERATE_USE_FACTOR = DEFAULT_MODERATE_USE_FACTOR;
- /** Prefix for all of the max_job constants. */
- private static final String KEY_PREFIX_MAX_JOB = "max_job_";
-
- // Max job counts for screen on / off, for each memory trim level.
- final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_ON =
- new MaxJobCountsPerMemoryTrimLevel(
- new MaxJobCounts(
- 8, KEY_PREFIX_MAX_JOB + "total_on_normal",
- 6, KEY_PREFIX_MAX_JOB + "max_bg_on_normal",
- 2, KEY_PREFIX_MAX_JOB + "min_bg_on_normal"),
- new MaxJobCounts(
- 8, KEY_PREFIX_MAX_JOB + "total_on_moderate",
- 4, KEY_PREFIX_MAX_JOB + "max_bg_on_moderate",
- 2, KEY_PREFIX_MAX_JOB + "min_bg_on_moderate"),
- new MaxJobCounts(
- 5, KEY_PREFIX_MAX_JOB + "total_on_low",
- 1, KEY_PREFIX_MAX_JOB + "max_bg_on_low",
- 1, KEY_PREFIX_MAX_JOB + "min_bg_on_low"),
- new MaxJobCounts(
- 5, KEY_PREFIX_MAX_JOB + "total_on_critical",
- 1, KEY_PREFIX_MAX_JOB + "max_bg_on_critical",
- 1, KEY_PREFIX_MAX_JOB + "min_bg_on_critical"));
-
- final MaxJobCountsPerMemoryTrimLevel MAX_JOB_COUNTS_SCREEN_OFF =
- new MaxJobCountsPerMemoryTrimLevel(
- new MaxJobCounts(
- 10, KEY_PREFIX_MAX_JOB + "total_off_normal",
- 6, KEY_PREFIX_MAX_JOB + "max_bg_off_normal",
- 2, KEY_PREFIX_MAX_JOB + "min_bg_off_normal"),
- new MaxJobCounts(
- 10, KEY_PREFIX_MAX_JOB + "total_off_moderate",
- 4, KEY_PREFIX_MAX_JOB + "max_bg_off_moderate",
- 2, KEY_PREFIX_MAX_JOB + "min_bg_off_moderate"),
- new MaxJobCounts(
- 5, KEY_PREFIX_MAX_JOB + "total_off_low",
- 1, KEY_PREFIX_MAX_JOB + "max_bg_off_low",
- 1, KEY_PREFIX_MAX_JOB + "min_bg_off_low"),
- new MaxJobCounts(
- 5, KEY_PREFIX_MAX_JOB + "total_off_critical",
- 1, KEY_PREFIX_MAX_JOB + "max_bg_off_critical",
- 1, KEY_PREFIX_MAX_JOB + "min_bg_off_critical"));
-
-
- /** Wait for this long after screen off before increasing the job concurrency. */
- long SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS =
- DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS;
-
/**
* The minimum backoff time to allow for linear backoff.
*/
@@ -700,23 +529,6 @@
DEFAULT_MODERATE_USE_FACTOR);
}
- void updateConcurrencyConstantsLocked() {
- MAX_JOB_COUNTS_SCREEN_ON.normal.update();
- MAX_JOB_COUNTS_SCREEN_ON.moderate.update();
- MAX_JOB_COUNTS_SCREEN_ON.low.update();
- MAX_JOB_COUNTS_SCREEN_ON.critical.update();
-
- MAX_JOB_COUNTS_SCREEN_OFF.normal.update();
- MAX_JOB_COUNTS_SCREEN_OFF.moderate.update();
- MAX_JOB_COUNTS_SCREEN_OFF.low.update();
- MAX_JOB_COUNTS_SCREEN_OFF.critical.update();
-
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS = DeviceConfig.getLong(
- DeviceConfig.NAMESPACE_JOB_SCHEDULER,
- KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
- DEFAULT_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
- }
-
private void updateBackoffConstantsLocked() {
MIN_LINEAR_BACKOFF_TIME_MS = DeviceConfig.getLong(DeviceConfig.NAMESPACE_JOB_SCHEDULER,
KEY_MIN_LINEAR_BACKOFF_TIME_MS,
@@ -766,19 +578,6 @@
pw.print(KEY_HEAVY_USE_FACTOR, HEAVY_USE_FACTOR).println();
pw.print(KEY_MODERATE_USE_FACTOR, MODERATE_USE_FACTOR).println();
- MAX_JOB_COUNTS_SCREEN_ON.normal.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_ON.moderate.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_ON.low.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_ON.critical.dump(pw, "");
-
- MAX_JOB_COUNTS_SCREEN_OFF.normal.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_OFF.moderate.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_OFF.low.dump(pw, "");
- MAX_JOB_COUNTS_SCREEN_OFF.critical.dump(pw, "");
-
- pw.print(KEY_SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS).println();
-
pw.print(KEY_MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS).println();
pw.print(KEY_MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS).println();
pw.print(KEY_CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC).println();
@@ -803,12 +602,6 @@
proto.write(ConstantsProto.HEAVY_USE_FACTOR, HEAVY_USE_FACTOR);
proto.write(ConstantsProto.MODERATE_USE_FACTOR, MODERATE_USE_FACTOR);
- MAX_JOB_COUNTS_SCREEN_ON.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_ON);
- MAX_JOB_COUNTS_SCREEN_OFF.dumpProto(proto, ConstantsProto.MAX_JOB_COUNTS_SCREEN_OFF);
-
- proto.write(ConstantsProto.SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS,
- SCREEN_OFF_JOB_CONCURRENCY_INCREASE_DELAY_MS);
-
proto.write(ConstantsProto.MIN_LINEAR_BACKOFF_TIME_MS, MIN_LINEAR_BACKOFF_TIME_MS);
proto.write(ConstantsProto.MIN_EXP_BACKOFF_TIME_MS, MIN_EXP_BACKOFF_TIME_MS);
proto.write(ConstantsProto.CONN_CONGESTION_DELAY_FRAC, CONN_CONGESTION_DELAY_FRAC);
diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
index 26b5abe..247b421 100644
--- a/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
+++ b/apex/jobscheduler/service/java/com/android/server/job/JobServiceContext.java
@@ -16,6 +16,7 @@
package com.android.server.job;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
import static com.android.server.job.JobSchedulerService.RESTRICTED_INDEX;
import static com.android.server.job.JobSchedulerService.sElapsedRealtimeClock;
@@ -124,9 +125,12 @@
*
* Any reads (dereferences) not done from the handler thread must be synchronized on
* {@link #mLock}.
- * Writes can only be done from the handler thread, or {@link #executeRunnableJob(JobStatus)}.
+ * Writes can only be done from the handler thread,
+ * or {@link #executeRunnableJob(JobStatus, int)}.
*/
private JobStatus mRunningJob;
+ @JobConcurrencyManager.WorkType
+ private int mRunningJobWorkType;
private JobCallback mRunningCallback;
/** Used to store next job to run when current job is to be preempted. */
private int mPreferredUid;
@@ -181,30 +185,26 @@
JobServiceContext(JobSchedulerService service, IBatteryStats batteryStats,
JobPackageTracker tracker, Looper looper) {
- this(service.getContext(), service.getLock(), batteryStats, tracker, service, looper);
- }
-
- @VisibleForTesting
- JobServiceContext(Context context, Object lock, IBatteryStats batteryStats,
- JobPackageTracker tracker, JobCompletedListener completedListener, Looper looper) {
- mContext = context;
- mLock = lock;
+ mContext = service.getContext();
+ mLock = service.getLock();
mBatteryStats = batteryStats;
mJobPackageTracker = tracker;
mCallbackHandler = new JobServiceHandler(looper);
- mCompletedListener = completedListener;
+ mCompletedListener = service;
mAvailable = true;
mVerb = VERB_FINISHED;
mPreferredUid = NO_PREFERRED_UID;
}
/**
- * Give a job to this context for execution. Callers must first check {@link #getRunningJobLocked()}
+ * Give a job to this context for execution. Callers must first check {@link
+ * #getRunningJobLocked()}
* and ensure it is null to make sure this is a valid context.
+ *
* @param job The status of the job that we are going to run.
* @return True if the job is valid and is running. False if the job cannot be executed.
*/
- boolean executeRunnableJob(JobStatus job) {
+ boolean executeRunnableJob(JobStatus job, @JobConcurrencyManager.WorkType int workType) {
synchronized (mLock) {
if (!mAvailable) {
Slog.e(TAG, "Starting new runnable but context is unavailable > Error.");
@@ -214,6 +214,7 @@
mPreferredUid = NO_PREFERRED_UID;
mRunningJob = job;
+ mRunningJobWorkType = workType;
mRunningCallback = new JobCallback();
final boolean isDeadlineExpired =
job.hasDeadlineConstraint() &&
@@ -282,6 +283,7 @@
Slog.d(TAG, job.getServiceComponent().getShortClassName() + " unavailable.");
}
mRunningJob = null;
+ mRunningJobWorkType = WORK_TYPE_NONE;
mRunningCallback = null;
mParams = null;
mExecutionStartTimeElapsed = 0L;
@@ -326,6 +328,11 @@
return mRunningJob;
}
+ @JobConcurrencyManager.WorkType
+ int getRunningJobWorkType() {
+ return mRunningJobWorkType;
+ }
+
/**
* Used only for debugging. Will return <code>"<null>"</code> if there is no job running.
*/
@@ -831,6 +838,7 @@
mContext.unbindService(JobServiceContext.this);
mWakeLock = null;
mRunningJob = null;
+ mRunningJobWorkType = WORK_TYPE_NONE;
mRunningCallback = null;
mParams = null;
mVerb = VERB_FINISHED;
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 09dc7d2..539c3c9 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
@@ -305,6 +305,7 @@
public Network network;
public ServiceInfo serviceInfo;
+ /** The evaluated priority of the job when it started running. */
public int lastEvaluatedPriority;
// If non-null, this is work that has been enqueued for the job.
diff --git a/core/api/current.txt b/core/api/current.txt
index 61d7aad..b49e724 100644
--- a/core/api/current.txt
+++ b/core/api/current.txt
@@ -657,6 +657,7 @@
field @Deprecated public static final int fontProviderCerts = 16844125; // 0x101055d
field @Deprecated public static final int fontProviderPackage = 16844119; // 0x1010557
field @Deprecated public static final int fontProviderQuery = 16844113; // 0x1010551
+ field public static final int fontProviderSystemFontFamily = 16844322; // 0x1010622
field public static final int fontStyle = 16844095; // 0x101053f
field public static final int fontVariationSettings = 16844144; // 0x1010570
field public static final int fontWeight = 16844083; // 0x1010533
@@ -36822,9 +36823,10 @@
package android.security.keystore {
public class BackendBusyException extends java.security.ProviderException {
- ctor public BackendBusyException();
- ctor public BackendBusyException(@NonNull String);
- ctor public BackendBusyException(@NonNull String, @NonNull Throwable);
+ ctor public BackendBusyException(long);
+ ctor public BackendBusyException(long, @NonNull String);
+ ctor public BackendBusyException(long, @NonNull String, @NonNull Throwable);
+ method public long getBackOffHintMillis();
}
public class KeyExpiredException extends java.security.InvalidKeyException {
@@ -39714,7 +39716,6 @@
field public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
field public static final String EXTRA_INCOMING_CALL_ADDRESS = "android.telecom.extra.INCOMING_CALL_ADDRESS";
field public static final String EXTRA_INCOMING_CALL_EXTRAS = "android.telecom.extra.INCOMING_CALL_EXTRAS";
- field public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
field public static final String EXTRA_INCOMING_VIDEO_STATE = "android.telecom.extra.INCOMING_VIDEO_STATE";
field public static final String EXTRA_IS_DEFAULT_CALL_SCREENING_APP = "android.telecom.extra.IS_DEFAULT_CALL_SCREENING_APP";
field public static final String EXTRA_LOCATION = "android.telecom.extra.LOCATION";
@@ -39723,6 +39724,7 @@
field public static final String EXTRA_OUTGOING_CALL_EXTRAS = "android.telecom.extra.OUTGOING_CALL_EXTRAS";
field public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
field public static final String EXTRA_PHONE_ACCOUNT_HANDLE = "android.telecom.extra.PHONE_ACCOUNT_HANDLE";
+ field public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
field public static final String EXTRA_PRIORITY = "android.telecom.extra.PRIORITY";
field public static final String EXTRA_START_CALL_WITH_RTT = "android.telecom.extra.START_CALL_WITH_RTT";
field public static final String EXTRA_START_CALL_WITH_SPEAKERPHONE = "android.telecom.extra.START_CALL_WITH_SPEAKERPHONE";
@@ -46181,6 +46183,7 @@
method @NonNull public android.graphics.Rect getBoundingRectRight();
method @NonNull public android.graphics.Rect getBoundingRectTop();
method @NonNull public java.util.List<android.graphics.Rect> getBoundingRects();
+ method @Nullable public android.graphics.Path getCutoutPath();
method public int getSafeInsetBottom();
method public int getSafeInsetLeft();
method public int getSafeInsetRight();
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 0f1296e..0a0f77e 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -7758,11 +7758,13 @@
method public boolean setupInterfaceForSoftApMode(@NonNull String);
method @Nullable public android.net.wifi.nl80211.WifiNl80211Manager.SignalPollResult signalPoll(@NonNull String);
method public boolean startPnoScan(@NonNull String, @NonNull android.net.wifi.nl80211.PnoSettings, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.nl80211.WifiNl80211Manager.PnoScanRequestCallback);
- method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
+ method @Deprecated public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>);
+ method public boolean startScan(@NonNull String, int, @Nullable java.util.Set<java.lang.Integer>, @Nullable java.util.List<byte[]>, @Nullable android.os.Bundle);
method public boolean stopPnoScan(@NonNull String);
method public boolean tearDownClientInterface(@NonNull String);
method public boolean tearDownInterfaces();
method public boolean tearDownSoftApInterface(@NonNull String);
+ field public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR = "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
field public static final int SCAN_TYPE_PNO_SCAN = 1; // 0x1
field public static final int SCAN_TYPE_SINGLE_SCAN = 0; // 0x0
field public static final int SEND_MGMT_FRAME_ERROR_ALREADY_STARTED = 5; // 0x5
diff --git a/core/api/test-current.txt b/core/api/test-current.txt
index c237d7d..ea7dc03 100644
--- a/core/api/test-current.txt
+++ b/core/api/test-current.txt
@@ -684,6 +684,7 @@
method public void holdLock(android.os.IBinder, int);
field public static final String FEATURE_ADOPTABLE_STORAGE = "android.software.adoptable_storage";
field public static final String FEATURE_FILE_BASED_ENCRYPTION = "android.software.file_based_encryption";
+ field public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
field public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 128; // 0x80
field public static final int MATCH_KNOWN_PACKAGES = 4202496; // 0x402000
field public static final String SYSTEM_SHARED_LIBRARY_SERVICES = "android.ext.services";
diff --git a/core/java/android/app/Notification.java b/core/java/android/app/Notification.java
index 68d3a92..49f508d 100644
--- a/core/java/android/app/Notification.java
+++ b/core/java/android/app/Notification.java
@@ -5255,6 +5255,7 @@
// We still want a time to be set but gone, such that we can show and hide it
// on demand in case it's a child notification without anything in the header
contentView.setLong(R.id.time, "setTime", mN.when != 0 ? mN.when : mN.creationTime);
+ setTextViewColorSecondary(contentView, R.id.time, p);
}
}
diff --git a/core/java/android/app/people/ConversationChannel.aidl b/core/java/android/app/people/ConversationChannel.aidl
new file mode 100644
index 0000000..78df2f1
--- /dev/null
+++ b/core/java/android/app/people/ConversationChannel.aidl
@@ -0,0 +1,19 @@
+/**
+ * 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.app.people;
+
+parcelable ConversationChannel;
\ No newline at end of file
diff --git a/core/java/android/app/people/IPeopleManager.aidl b/core/java/android/app/people/IPeopleManager.aidl
index 0d12ed0..ebe9f60 100644
--- a/core/java/android/app/people/IPeopleManager.aidl
+++ b/core/java/android/app/people/IPeopleManager.aidl
@@ -17,6 +17,7 @@
package android.app.people;
import android.app.people.ConversationStatus;
+import android.app.people.ConversationChannel;
import android.content.pm.ParceledListSlice;
import android.net.Uri;
import android.os.IBinder;
@@ -26,6 +27,13 @@
* {@hide}
*/
interface IPeopleManager {
+
+ /**
+ * Returns the specified conversation from the conversations list. If the conversation can't be
+ * found, returns null.
+ */
+ ConversationChannel getConversation(in String packageName, int userId, in String shortcutId);
+
/**
* Returns the recent conversations. The conversations that have customized notification
* settings are excluded from the returned list.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 68792b2..9ae9c25 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -3385,6 +3385,7 @@
* {@link #hasSystemFeature}: This device supports HDMI-CEC.
* @hide
*/
+ @TestApi
@SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_HDMI_CEC = "android.hardware.hdmi.cec";
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index fe8bf9d..e6c0f6a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -6265,6 +6265,55 @@
}
/**
+ * Returns whether this {@code SigningDetails} has a signer in common with the provided
+ * {@code otherDetails} with the specified {@code flags} capabilities provided by this
+ * signer.
+ *
+ * <p>Note this method allows for the signing lineage to diverge, so this should only be
+ * used for instances where the only requirement is a common signer in the lineage with
+ * the specified capabilities. If the current signer of this instance is an ancestor of
+ * {@code otherDetails} then {@code true} is immediately returned since the current signer
+ * has all capabilities granted.
+ */
+ public boolean hasCommonSignerWithCapability(SigningDetails otherDetails,
+ @CertCapabilities int flags) {
+ if (this == UNKNOWN || otherDetails == UNKNOWN) {
+ return false;
+ }
+ // If either is signed with more than one signer then both must be signed by the same
+ // signers to consider the capabilities granted.
+ if (signatures.length > 1 || otherDetails.signatures.length > 1) {
+ return signaturesMatchExactly(otherDetails);
+ }
+ // The Signature class does not use the granted capabilities in the hashCode
+ // computation, so a Set can be used to check for a common signer.
+ Set<Signature> otherSignatures = new ArraySet<>();
+ if (otherDetails.hasPastSigningCertificates()) {
+ otherSignatures.addAll(Arrays.asList(otherDetails.pastSigningCertificates));
+ } else {
+ otherSignatures.addAll(Arrays.asList(otherDetails.signatures));
+ }
+ // If the current signer of this instance is an ancestor of the other than return true
+ // since all capabilities are granted to the current signer.
+ if (otherSignatures.contains(signatures[0])) {
+ return true;
+ }
+ if (hasPastSigningCertificates()) {
+ // Since the current signer was checked above and the last signature in the
+ // pastSigningCertificates is the current signer skip checking the last element.
+ for (int i = 0; i < pastSigningCertificates.length - 1; i++) {
+ if (otherSignatures.contains(pastSigningCertificates[i])) {
+ // If the caller specified multiple capabilities ensure all are set.
+ if ((pastSigningCertificates[i].getFlags() & flags) == flags) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
* Determines if the provided {@code oldDetails} is an ancestor of this one, and whether or
* not this one grants it the provided capability, represented by the {@code flags}
* parameter. In the event of signing certificate rotation, a package may still interact
diff --git a/core/java/android/content/res/FontResourcesParser.java b/core/java/android/content/res/FontResourcesParser.java
index 14eb11a..ecd240d 100644
--- a/core/java/android/content/res/FontResourcesParser.java
+++ b/core/java/android/content/res/FontResourcesParser.java
@@ -47,14 +47,17 @@
private final @NonNull String mProviderAuthority;
private final @NonNull String mProviderPackage;
private final @NonNull String mQuery;
+ private final @Nullable String mSystemFontFamilyName;
private final @Nullable List<List<String>> mCerts;
public ProviderResourceEntry(@NonNull String authority, @NonNull String pkg,
- @NonNull String query, @Nullable List<List<String>> certs) {
+ @NonNull String query, @Nullable List<List<String>> certs,
+ @Nullable String systemFontFamilyName) {
mProviderAuthority = authority;
mProviderPackage = pkg;
mQuery = query;
mCerts = certs;
+ mSystemFontFamilyName = systemFontFamilyName;
}
public @NonNull String getAuthority() {
@@ -69,6 +72,10 @@
return mQuery;
}
+ public @NonNull String getSystemFontFamilyName() {
+ return mSystemFontFamilyName;
+ }
+
public @Nullable List<List<String>> getCerts() {
return mCerts;
}
@@ -166,6 +173,8 @@
String providerPackage = array.getString(R.styleable.FontFamily_fontProviderPackage);
String query = array.getString(R.styleable.FontFamily_fontProviderQuery);
int certsId = array.getResourceId(R.styleable.FontFamily_fontProviderCerts, 0);
+ String systemFontFamilyName = array.getString(
+ R.styleable.FontFamily_fontProviderSystemFontFamily);
array.recycle();
if (authority != null && providerPackage != null && query != null) {
while (parser.next() != XmlPullParser.END_TAG) {
@@ -191,7 +200,13 @@
}
}
}
- return new ProviderResourceEntry(authority, providerPackage, query, certs);
+ return new ProviderResourceEntry(
+ authority,
+ providerPackage,
+ query,
+ certs,
+ systemFontFamilyName
+ );
}
List<FontFileResourceEntry> fonts = new ArrayList<>();
while (parser.next() != XmlPullParser.END_TAG) {
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 75893d9..588bc01 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -301,7 +301,8 @@
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
EnrollmentCallback callback, int[] disabledFeatures) {
- enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */);
+ enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures, null /* surface */,
+ false /* debugConsent */);
}
/**
@@ -313,18 +314,20 @@
* which point the object is no longer valid. The operation can be canceled by using the
* provided cancel object.
*
- * @param token a unique token provided by a recent creation or verification of device
- * credentials (e.g. pin, pattern or password).
- * @param cancel an object that can be used to cancel enrollment
- * @param userId the user to whom this face will belong to
- * @param callback an object to receive enrollment events
- * @param surface optional camera preview surface for a single-camera device. Must be null if
- * not used.
+ * @param hardwareAuthToken a unique token provided by a recent creation or
+ * verification of device credentials (e.g. pin, pattern or password).
+ * @param cancel an object that can be used to cancel enrollment
+ * @param userId the user to whom this face will belong to
+ * @param callback an object to receive enrollment events
+ * @param surface optional camera preview surface for a single-camera device.
+ * Must be null if not used.
+ * @param debugConsent a feature flag that the user has consented to debug.
* @hide
*/
@RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
- EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface) {
+ EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface surface,
+ boolean debugConsent) {
if (callback == null) {
throw new IllegalArgumentException("Must supply an enrollment callback");
}
@@ -343,7 +346,7 @@
mEnrollmentCallback = callback;
Trace.beginSection("FaceManager#enroll");
mService.enroll(userId, mToken, hardwareAuthToken, mServiceReceiver,
- mContext.getOpPackageName(), disabledFeatures, surface);
+ mContext.getOpPackageName(), disabledFeatures, surface, debugConsent);
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception in enroll: ", e);
// Though this may not be a hardware issue, it will cause apps to give up or
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 1b188e8..a3e7e2d 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -74,7 +74,7 @@
// Start face enrollment
void enroll(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
- String opPackageName, in int [] disabledFeatures, in Surface surface);
+ String opPackageName, in int [] disabledFeatures, in Surface surface, boolean debugConsent);
// Start remote face enrollment
void enrollRemotely(int userId, IBinder token, in byte [] hardwareAuthToken, IFaceServiceReceiver receiver,
diff --git a/core/java/android/os/incremental/IncrementalFileStorages.java b/core/java/android/os/incremental/IncrementalFileStorages.java
index 9fdc72b..59292baa 100644
--- a/core/java/android/os/incremental/IncrementalFileStorages.java
+++ b/core/java/android/os/incremental/IncrementalFileStorages.java
@@ -36,7 +36,6 @@
import android.content.Context;
import android.content.pm.DataLoaderParams;
import android.content.pm.IDataLoaderStatusListener;
-import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFileParcel;
import android.text.TextUtils;
@@ -71,8 +70,7 @@
@Nullable StorageHealthCheckParams healthCheckParams,
@Nullable IStorageHealthListener healthListener,
@NonNull List<InstallationFileParcel> addedFiles,
- @NonNull PerUidReadTimeouts[] perUidReadTimeouts,
- IPackageLoadingProgressCallback progressCallback) throws IOException {
+ @NonNull PerUidReadTimeouts[] perUidReadTimeouts) throws IOException {
// TODO(b/136132412): validity check if session should not be incremental
IncrementalManager incrementalManager = (IncrementalManager) context.getSystemService(
Context.INCREMENTAL_SERVICE);
@@ -97,11 +95,7 @@
throw new IOException("Unknown file location: " + file.location);
}
}
- // Register progress loading callback after files have been added
- if (progressCallback != null) {
- incrementalManager.registerLoadingProgressCallback(stageDir.getAbsolutePath(),
- progressCallback);
- }
+
result.startLoading();
return result;
@@ -186,7 +180,6 @@
try {
mDefaultStorage.unBind(mStageDir.getAbsolutePath());
- mDefaultStorage.unregisterLoadingProgressListener();
} catch (IOException ignored) {
}
mDefaultStorage = null;
diff --git a/core/java/android/permission/PermissionUsageHelper.java b/core/java/android/permission/PermissionUsageHelper.java
index b1b2925..8b6082b3 100644
--- a/core/java/android/permission/PermissionUsageHelper.java
+++ b/core/java/android/permission/PermissionUsageHelper.java
@@ -28,7 +28,10 @@
import static android.app.AppOpsManager.OP_FLAGS_ALL_TRUSTED;
import static android.app.AppOpsManager.opToPermission;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED;
+import static android.media.AudioSystem.MODE_IN_COMMUNICATION;
+import static android.telephony.TelephonyManager.CARRIER_PRIVILEGE_STATUS_HAS_ACCESS;
+import android.Manifest;
import android.annotation.NonNull;
import android.app.AppOpsManager;
import android.content.ComponentName;
@@ -41,12 +44,14 @@
import android.content.pm.ResolveInfo;
import android.icu.text.ListFormatter;
import android.location.LocationManager;
+import android.media.AudioManager;
import android.os.Process;
import android.os.UserHandle;
import android.provider.DeviceConfig;
import android.provider.Settings;
import android.speech.RecognitionService;
import android.speech.RecognizerIntent;
+import android.telephony.TelephonyManager;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.view.inputmethod.InputMethodInfo;
@@ -75,6 +80,9 @@
private static final String PROPERTY_LOCATION_INDICATORS_ENABLED =
"location_indicators_enabled";
+ /** Whether to show the Permissions Hub. */
+ private static final String PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled";
+
/** How long after an access to show it as "recent" */
private static final String RECENT_ACCESS_TIME_MS = "recent_acccess_time_ms";
@@ -84,17 +92,25 @@
/** The name of the expected voice IME subtype */
private static final String VOICE_IME_SUBTYPE = "voice";
+ private static final String SYSTEM_PKG = "android";
+
private static final long DEFAULT_RUNNING_TIME_MS = 5000L;
private static final long DEFAULT_RECENT_TIME_MS = 30000L;
+ private static boolean shouldShowPermissionsHub() {
+ return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
+ PROPERTY_PERMISSIONS_HUB_2_ENABLED, false);
+ }
+
private static boolean shouldShowIndicators() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_CAMERA_MIC_ICONS_ENABLED, true);
+ PROPERTY_CAMERA_MIC_ICONS_ENABLED, true) || shouldShowPermissionsHub();
}
private static boolean shouldShowLocationIndicator() {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
- PROPERTY_LOCATION_INDICATORS_ENABLED, false);
+ PROPERTY_LOCATION_INDICATORS_ENABLED, false)
+ || shouldShowPermissionsHub();
}
private static long getRecentThreshold(Long now) {
@@ -113,7 +129,7 @@
);
private static final List<String> MIC_OPS = List.of(
- OPSTR_PHONE_CALL_CAMERA,
+ OPSTR_PHONE_CALL_MICROPHONE,
OPSTR_RECORD_AUDIO
);
@@ -163,6 +179,13 @@
return mUserContexts.get(user);
}
+ // TODO ntmyren: Replace this with better check if this moves beyond teamfood
+ private boolean isAppPredictor(String packageName, UserHandle user) {
+ return shouldShowPermissionsHub() && getUserContext(user).getPackageManager()
+ .checkPermission(Manifest.permission.MANAGE_APP_PREDICTIONS, packageName)
+ == PackageManager.PERMISSION_GRANTED;
+ }
+
/**
* @see PermissionManager.getIndicatorAppOpUsageData
*/
@@ -186,7 +209,28 @@
Map<PackageAttribution, CharSequence> packagesWithAttributionLabels =
getTrustedAttributions(rawUsages.get(MICROPHONE), proxyChains);
- List<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
+ ArrayList<String> usedPermGroups = new ArrayList<>(rawUsages.keySet());
+
+ // If we have a phone call, and a carrier privileged app using microphone, hide the
+ // phone call.
+ AudioManager audioManager = mContext.getSystemService(AudioManager.class);
+ boolean hasPhoneCall = usedPermGroups.contains(OPSTR_PHONE_CALL_CAMERA)
+ || usedPermGroups.contains(OPSTR_PHONE_CALL_MICROPHONE);
+ if (hasPhoneCall && usedPermGroups.contains(MICROPHONE) && audioManager.getMode()
+ == MODE_IN_COMMUNICATION) {
+ TelephonyManager telephonyManager =
+ mContext.getSystemService(TelephonyManager.class);
+ List<OpUsage> permUsages = rawUsages.get(MICROPHONE);
+ for (int usageNum = 0; usageNum < permUsages.size(); usageNum++) {
+ if (telephonyManager.checkCarrierPrivilegesForPackage(
+ permUsages.get(usageNum).packageName)
+ == CARRIER_PRIVILEGE_STATUS_HAS_ACCESS) {
+ usedPermGroups.remove(OPSTR_PHONE_CALL_CAMERA);
+ usedPermGroups.remove(OPSTR_PHONE_CALL_MICROPHONE);
+ }
+ }
+ }
+
for (int permGroupNum = 0; permGroupNum < usedPermGroups.size(); permGroupNum++) {
boolean isPhone = false;
String permGroup = usedPermGroups.get(permGroupNum);
@@ -269,8 +313,11 @@
if (lastAccessTime < recentThreshold && !attrOpEntry.isRunning()) {
continue;
}
- if (!isUserSensitive(packageName, user, op)
- && !isLocationProvider(packageName, user)) {
+
+ if (packageName.equals(SYSTEM_PKG)
+ || (!isUserSensitive(packageName, user, op)
+ && !isLocationProvider(packageName, user)
+ && !isAppPredictor(packageName, user))) {
continue;
}
diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java
index 33e33ee..9bfd75e 100644
--- a/core/java/android/provider/CallLog.java
+++ b/core/java/android/provider/CallLog.java
@@ -249,11 +249,13 @@
// Nasty casework for the shadow calllog begins...
// First see if we're just inserting for one user. If so, insert into the shadow
// based on whether that user is unlocked.
- if (user != null) {
- Uri baseUri = userManager.isUserUnlocked(user) ? CALL_COMPOSER_PICTURE_URI
+ UserHandle realUser = UserHandle.CURRENT.equals(user)
+ ? android.os.Process.myUserHandle() : user;
+ if (realUser != null) {
+ Uri baseUri = userManager.isUserUnlocked(realUser) ? CALL_COMPOSER_PICTURE_URI
: SHADOW_CALL_COMPOSER_PICTURE_URI;
Uri pictureInsertionUri = ContentProvider.maybeAddUserId(baseUri,
- user.getIdentifier());
+ realUser.getIdentifier());
Log.i(LOG_TAG, "Inserting call composer for single user at "
+ pictureInsertionUri);
diff --git a/core/java/android/rotationresolver/OWNERS b/core/java/android/rotationresolver/OWNERS
index 81b6f05..733fca9 100644
--- a/core/java/android/rotationresolver/OWNERS
+++ b/core/java/android/rotationresolver/OWNERS
@@ -1 +1 @@
-include /core/java/android/rotationresolver/OWNERS
+include /core/java/android/service/rotationresolver/OWNERS
diff --git a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
index 7f8158f..dc0718a 100644
--- a/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
+++ b/core/java/android/service/voice/IVoiceInteractionSessionService.aidl
@@ -18,8 +18,6 @@
import android.os.Bundle;
-import android.service.voice.IVoiceInteractionSession;
-
/**
* @hide
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSessionService.java b/core/java/android/service/voice/VoiceInteractionSessionService.java
index 424ff9d..8300343 100644
--- a/core/java/android/service/voice/VoiceInteractionSessionService.java
+++ b/core/java/android/service/voice/VoiceInteractionSessionService.java
@@ -21,11 +21,14 @@
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
+import android.os.DeadObjectException;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.util.Log;
+
import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.os.HandlerCaller;
import com.android.internal.os.SomeArgs;
@@ -38,6 +41,8 @@
*/
public abstract class VoiceInteractionSessionService extends Service {
+ private static final String TAG = "VoiceInteractionSession";
+
static final int MSG_NEW_SESSION = 1;
IVoiceInteractionManagerService mSystemService;
@@ -120,10 +125,22 @@
mSession = null;
}
mSession = onNewSession(args);
- try {
- mSystemService.deliverNewSession(token, mSession.mSession, mSession.mInteractor);
+ if (deliverSession(token)) {
mSession.doCreate(mSystemService, token);
- } catch (RemoteException e) {
+ } else {
+ // TODO(b/178777121): Add an onError() method to let the application know what happened.
+ mSession.doDestroy();
+ mSession = null;
}
}
+
+ private boolean deliverSession(IBinder token) {
+ try {
+ return mSystemService.deliverNewSession(token, mSession.mSession, mSession.mInteractor);
+ } catch (DeadObjectException ignored) {
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to deliver session: " + e);
+ }
+ return false;
+ }
}
diff --git a/core/java/android/util/RotationUtils.java b/core/java/android/util/RotationUtils.java
index a44ed59..698cb77 100644
--- a/core/java/android/util/RotationUtils.java
+++ b/core/java/android/util/RotationUtils.java
@@ -21,7 +21,9 @@
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
+import android.annotation.Dimension;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.view.Surface.Rotation;
/**
@@ -69,4 +71,34 @@
}
return rotated;
}
+
+ /**
+ * Sets a matrix such that given a rotation, it transforms physical display
+ * coordinates to that rotation's logical coordinates.
+ *
+ * @param rotation the rotation to which the matrix should transform
+ * @param out the matrix to be set
+ */
+ public static void transformPhysicalToLogicalCoordinates(@Rotation int rotation,
+ @Dimension int physicalWidth, @Dimension int physicalHeight, Matrix out) {
+ switch (rotation) {
+ case ROTATION_0:
+ out.reset();
+ break;
+ case ROTATION_90:
+ out.setRotate(270);
+ out.postTranslate(0, physicalWidth);
+ break;
+ case ROTATION_180:
+ out.setRotate(180);
+ out.postTranslate(physicalWidth, physicalHeight);
+ break;
+ case ROTATION_270:
+ out.setRotate(90);
+ out.postTranslate(physicalHeight, 0);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown rotation: " + rotation);
+ }
+ }
}
diff --git a/core/java/android/view/DisplayCutout.java b/core/java/android/view/DisplayCutout.java
index 525ac53..e1a4402 100644
--- a/core/java/android/view/DisplayCutout.java
+++ b/core/java/android/view/DisplayCutout.java
@@ -23,6 +23,7 @@
import static android.view.DisplayCutoutProto.BOUND_RIGHT;
import static android.view.DisplayCutoutProto.BOUND_TOP;
import static android.view.DisplayCutoutProto.INSETS;
+import static android.view.Surface.ROTATION_0;
import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
@@ -31,13 +32,16 @@
import android.annotation.Nullable;
import android.content.res.Resources;
import android.graphics.Insets;
+import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Pair;
+import android.util.RotationUtils;
import android.util.proto.ProtoOutputStream;
+import android.view.Surface.Rotation;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
@@ -69,6 +73,9 @@
"com.android.internal.display_cutout_emulation";
private static final Rect ZERO_RECT = new Rect();
+ private static final CutoutPathParserInfo EMPTY_PARSER_INFO = new CutoutPathParserInfo(
+ 0 /* displayWidth */, 0 /* displayHeight */, 0f /* density */, "" /* cutoutSpec */,
+ 0 /* rotation */, 0f /* scale */);
/**
* An instance where {@link #isEmpty()} returns {@code true}.
@@ -76,7 +83,7 @@
* @hide
*/
public static final DisplayCutout NO_CUTOUT = new DisplayCutout(
- ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT,
+ ZERO_RECT, Insets.NONE, ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT, EMPTY_PARSER_INFO,
false /* copyArguments */);
@@ -96,11 +103,15 @@
@GuardedBy("CACHE_LOCK")
private static Insets sCachedWaterfallInsets;
+ @GuardedBy("CACHE_LOCK")
+ private static CutoutPathParserInfo sCachedCutoutPathParserInfo;
+ @GuardedBy("CACHE_LOCK")
+ private static Path sCachedCutoutPath;
+
private final Rect mSafeInsets;
@NonNull
private final Insets mWaterfallInsets;
-
/**
* The bound is at the left of the screen.
* @hide
@@ -210,6 +221,7 @@
}
return result;
}
+
@Override
public boolean equals(@Nullable Object o) {
if (o == this) {
@@ -232,6 +244,106 @@
private final Bounds mBounds;
/**
+ * Stores all the needed info to create the cutout paths.
+ *
+ * @hide
+ */
+ public static class CutoutPathParserInfo {
+ private final int mDisplayWidth;
+ private final int mDisplayHeight;
+ private final float mDensity;
+ private final String mCutoutSpec;
+ private final @Rotation int mRotation;
+ private final float mScale;
+
+ public CutoutPathParserInfo(int displayWidth, int displayHeight, float density,
+ String cutoutSpec, @Rotation int rotation, float scale) {
+ mDisplayWidth = displayWidth;
+ mDisplayHeight = displayHeight;
+ mDensity = density;
+ mCutoutSpec = cutoutSpec == null ? "" : cutoutSpec;
+ mRotation = rotation;
+ mScale = scale;
+ }
+
+ public CutoutPathParserInfo(CutoutPathParserInfo cutoutPathParserInfo) {
+ mDisplayWidth = cutoutPathParserInfo.mDisplayWidth;
+ mDisplayHeight = cutoutPathParserInfo.mDisplayHeight;
+ mDensity = cutoutPathParserInfo.mDensity;
+ mCutoutSpec = cutoutPathParserInfo.mCutoutSpec;
+ mRotation = cutoutPathParserInfo.mRotation;
+ mScale = cutoutPathParserInfo.mScale;
+ }
+
+ public int getDisplayWidth() {
+ return mDisplayWidth;
+ }
+
+ public int getDisplayHeight() {
+ return mDisplayHeight;
+ }
+
+ public float getDensity() {
+ return mDensity;
+ }
+
+ public @NonNull String getCutoutSpec() {
+ return mCutoutSpec;
+ }
+
+ public int getRotation() {
+ return mRotation;
+ }
+
+ public float getScale() {
+ return mScale;
+ }
+
+ private boolean hasCutout() {
+ return !mCutoutSpec.isEmpty();
+ }
+
+ @Override
+ public int hashCode() {
+ int result = 0;
+ result = result * 48271 + Integer.hashCode(mDisplayWidth);
+ result = result * 48271 + Integer.hashCode(mDisplayHeight);
+ result = result * 48271 + Float.hashCode(mDensity);
+ result = result * 48271 + mCutoutSpec.hashCode();
+ result = result * 48271 + Integer.hashCode(mRotation);
+ result = result * 48271 + Float.hashCode(mScale);
+ return result;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (o instanceof CutoutPathParserInfo) {
+ CutoutPathParserInfo c = (CutoutPathParserInfo) o;
+ return mDisplayWidth == c.mDisplayWidth && mDisplayHeight == c.mDisplayHeight
+ && mDensity == c.mDensity && mCutoutSpec.equals(c.mCutoutSpec)
+ && mRotation == c.mRotation && mScale == c.mScale;
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return "CutoutPathParserInfo{displayWidth=" + mDisplayWidth
+ + " displayHeight=" + mDisplayHeight
+ + " density={" + mDensity + "}"
+ + " cutoutSpec={" + mCutoutSpec + "}"
+ + " rotation={" + mRotation + "}"
+ + " scale={" + mScale + "}"
+ + "}";
+ }
+ }
+
+ private final @NonNull CutoutPathParserInfo mCutoutPathParserInfo;
+
+ /**
* Creates a DisplayCutout instance.
*
* <p>Note that this is only useful for tests. For production code, developers should always
@@ -251,7 +363,8 @@
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
public DisplayCutout(@NonNull Insets safeInsets, @Nullable Rect boundLeft,
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom) {
- this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, true);
+ this(safeInsets.toRect(), Insets.NONE, boundLeft, boundTop, boundRight, boundBottom, null,
+ true);
}
/**
@@ -276,7 +389,7 @@
@Nullable Rect boundTop, @Nullable Rect boundRight, @Nullable Rect boundBottom,
@NonNull Insets waterfallInsets) {
this(safeInsets.toRect(), waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
- true);
+ null, true);
}
/**
@@ -294,7 +407,7 @@
// TODO(b/73953958): @VisibleForTesting(visibility = PRIVATE)
@Deprecated
public DisplayCutout(@Nullable Rect safeInsets, @Nullable List<Rect> boundingRects) {
- this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects),
+ this(safeInsets, Insets.NONE, extractBoundsFromList(safeInsets, boundingRects), null,
true /* copyArguments */);
}
@@ -303,28 +416,42 @@
*
* @param safeInsets the insets from each edge which avoid the display cutout as returned by
* {@link #getSafeInsetTop()} etc.
+ * @param waterfallInsets the insets for the curved areas in waterfall display.
+ * @param boundLeft the left bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundTop the top bounding rect of the display cutout in pixels. If null is passed,
+ * it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundRight the right bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param boundBottom the bottom bounding rect of the display cutout in pixels. If null is
+ * passed, it's treated as an empty rectangle (0,0)-(0,0).
+ * @param info the cutout path parser info.
* @param copyArguments if true, create a copy of the arguments. If false, the passed arguments
* are not copied and MUST remain unchanged forever.
*/
- private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft,
- Rect boundTop, Rect boundRight, Rect boundBottom, boolean copyArguments) {
+ private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect boundLeft, Rect boundTop,
+ Rect boundRight, Rect boundBottom, CutoutPathParserInfo info,
+ boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(boundLeft, boundTop, boundRight, boundBottom, copyArguments);
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Rect[] bounds,
- boolean copyArguments) {
+ CutoutPathParserInfo info, boolean copyArguments) {
mSafeInsets = getCopyOrRef(safeInsets, copyArguments);
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = new Bounds(bounds, copyArguments);
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
- private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds) {
+ private DisplayCutout(Rect safeInsets, Insets waterfallInsets, Bounds bounds,
+ CutoutPathParserInfo info) {
mSafeInsets = safeInsets;
mWaterfallInsets = waterfallInsets == null ? Insets.NONE : waterfallInsets;
mBounds = bounds;
-
+ mCutoutPathParserInfo = info == null ? EMPTY_PARSER_INFO : info;
}
private static Rect getCopyOrRef(Rect r, boolean copyArguments) {
@@ -534,10 +661,70 @@
return mBounds.getRect(BOUNDS_POSITION_BOTTOM);
}
+ /**
+ * Returns a {@link Path} that contains the cutout paths of all sides on the display.
+ *
+ * To get a cutout path for one specific side, apps can intersect the {@link Path} with the
+ * {@link Rect} obtained from {@link #getBoundingRectLeft()}, {@link #getBoundingRectTop()},
+ * {@link #getBoundingRectRight()} or {@link #getBoundingRectBottom()}.
+ *
+ * @return a {@link Path} contains all the cutout paths based on display coordinate. Returns
+ * null if there is no cutout on the display.
+ */
+ public @Nullable Path getCutoutPath() {
+ if (!mCutoutPathParserInfo.hasCutout()) {
+ return null;
+ }
+ synchronized (CACHE_LOCK) {
+ if (mCutoutPathParserInfo.equals(sCachedCutoutPathParserInfo)) {
+ return sCachedCutoutPath;
+ }
+ }
+ final CutoutSpecification cutoutSpec = new CutoutSpecification.Parser(
+ mCutoutPathParserInfo.getDensity(), mCutoutPathParserInfo.getDisplayWidth(),
+ mCutoutPathParserInfo.getDisplayHeight())
+ .parse(mCutoutPathParserInfo.getCutoutSpec());
+
+ final Path cutoutPath = cutoutSpec.getPath();
+ if (cutoutPath == null || cutoutPath.isEmpty()) {
+ return null;
+ }
+ final Matrix matrix = new Matrix();
+ if (mCutoutPathParserInfo.getRotation() != ROTATION_0) {
+ RotationUtils.transformPhysicalToLogicalCoordinates(
+ mCutoutPathParserInfo.getRotation(),
+ mCutoutPathParserInfo.getDisplayWidth(),
+ mCutoutPathParserInfo.getDisplayHeight(),
+ matrix
+ );
+ }
+ matrix.postScale(mCutoutPathParserInfo.getScale(), mCutoutPathParserInfo.getScale());
+ cutoutPath.transform(matrix);
+
+ synchronized (CACHE_LOCK) {
+ sCachedCutoutPathParserInfo = new CutoutPathParserInfo(mCutoutPathParserInfo);
+ sCachedCutoutPath = cutoutPath;
+ }
+ return cutoutPath;
+ }
+
+ /**
+ * @return the {@link CutoutPathParserInfo};
+ *
+ * @hide
+ */
+ public CutoutPathParserInfo getCutoutPathParserInfo() {
+ return mCutoutPathParserInfo;
+ }
+
@Override
public int hashCode() {
- return (mSafeInsets.hashCode() * 48271 + mBounds.hashCode()) * 48271
- + mWaterfallInsets.hashCode();
+ int result = 0;
+ result = 48271 * result + mSafeInsets.hashCode();
+ result = 48271 * result + mBounds.hashCode();
+ result = 48271 * result + mWaterfallInsets.hashCode();
+ result = 48271 * result + mCutoutPathParserInfo.hashCode();
+ return result;
}
@Override
@@ -548,7 +735,8 @@
if (o instanceof DisplayCutout) {
DisplayCutout c = (DisplayCutout) o;
return mSafeInsets.equals(c.mSafeInsets) && mBounds.equals(c.mBounds)
- && mWaterfallInsets.equals(c.mWaterfallInsets);
+ && mWaterfallInsets.equals(c.mWaterfallInsets)
+ && mCutoutPathParserInfo.equals(c.mCutoutPathParserInfo);
}
return false;
}
@@ -558,6 +746,7 @@
return "DisplayCutout{insets=" + mSafeInsets
+ " waterfall=" + mWaterfallInsets
+ " boundingRect={" + mBounds + "}"
+ + " cutoutPathParserInfo={" + mCutoutPathParserInfo + "}"
+ "}";
}
@@ -607,7 +796,7 @@
}
return new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds,
- false /* copyArguments */);
+ mCutoutPathParserInfo, false /* copyArguments */);
}
private Rect insetInsets(int insetLeft, int insetTop, int insetRight, int insetBottom,
@@ -638,7 +827,8 @@
* @hide
*/
public DisplayCutout replaceSafeInsets(Rect safeInsets) {
- return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds);
+ return new DisplayCutout(new Rect(safeInsets), mWaterfallInsets, mBounds,
+ mCutoutPathParserInfo);
}
private static int atLeastZero(int value) {
@@ -658,16 +848,18 @@
for (int i = 0; i < BOUNDS_POSITION_LENGTH; ++i) {
bounds[i] = (pos == i) ? new Rect(left, top, right, bottom) : new Rect();
}
- return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+ return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null, false /* copyArguments */);
}
/**
- * Creates an instance from a bounding and waterfall insets.
+ * Creates an instance from bounds, waterfall insets and CutoutPathParserInfo.
*
* @hide
*/
- public static DisplayCutout fromBoundsAndWaterfall(Rect[] bounds, Insets waterfallInsets) {
- return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, false /* copyArguments */);
+ public static DisplayCutout constructDisplayCutout(Rect[] bounds, Insets waterfallInsets,
+ CutoutPathParserInfo info) {
+ return new DisplayCutout(ZERO_RECT, waterfallInsets, bounds, info,
+ false /* copyArguments */);
}
/**
@@ -676,7 +868,8 @@
* @hide
*/
public static DisplayCutout fromBounds(Rect[] bounds) {
- return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, false /* copyArguments */);
+ return new DisplayCutout(ZERO_RECT, Insets.NONE, bounds, null /* cutoutPathParserInfo */,
+ false /* copyArguments */);
}
/**
@@ -686,10 +879,12 @@
*
* @hide
*/
- public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth, int displayHeight) {
- return fromSpec(res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
+ public static DisplayCutout fromResourcesRectApproximation(Resources res, int displayWidth,
+ int displayHeight) {
+ return pathAndDisplayCutoutFromSpec(res.getString(R.string.config_mainBuiltInDisplayCutout),
+ res.getString(R.string.config_mainBuiltInDisplayCutoutRectApproximation),
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
- loadWaterfallInset(res));
+ loadWaterfallInset(res)).second;
}
/**
@@ -699,7 +894,7 @@
*/
public static Path pathFromResources(Resources res, int displayWidth, int displayHeight) {
return pathAndDisplayCutoutFromSpec(
- res.getString(R.string.config_mainBuiltInDisplayCutout),
+ res.getString(R.string.config_mainBuiltInDisplayCutout), null,
displayWidth, displayHeight, DENSITY_DEVICE_STABLE / (float) DENSITY_DEFAULT,
loadWaterfallInset(res)).first;
}
@@ -710,14 +905,30 @@
* @hide
*/
@VisibleForTesting(visibility = PRIVATE)
- public static DisplayCutout fromSpec(String spec, int displayWidth, int displayHeight,
- float density, Insets waterfallInsets) {
+ public static DisplayCutout fromSpec(String pathSpec, int displayWidth,
+ int displayHeight, float density, Insets waterfallInsets) {
return pathAndDisplayCutoutFromSpec(
- spec, displayWidth, displayHeight, density, waterfallInsets).second;
+ pathSpec, null, displayWidth, displayHeight, density, waterfallInsets)
+ .second;
}
- private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(String spec,
- int displayWidth, int displayHeight, float density, Insets waterfallInsets) {
+ /**
+ * Gets the cutout path and the corresponding DisplayCutout instance from the spec string.
+ *
+ * @param pathSpec the spec string read from config_mainBuiltInDisplayCutout.
+ * @param rectSpec the spec string read from config_mainBuiltInDisplayCutoutRectApproximation.
+ * @param displayWidth the display width.
+ * @param displayHeight the display height.
+ * @param density the display density.
+ * @param waterfallInsets the waterfall insets of the display.
+ * @return a Pair contains the cutout path and the corresponding DisplayCutout instance.
+ */
+ private static Pair<Path, DisplayCutout> pathAndDisplayCutoutFromSpec(
+ String pathSpec, String rectSpec, int displayWidth, int displayHeight, float density,
+ Insets waterfallInsets) {
+ // Always use the rect approximation spec to create the cutout if it's not null because
+ // transforming and sending a Region constructed from a path is very costly.
+ String spec = rectSpec != null ? rectSpec : pathSpec;
if (TextUtils.isEmpty(spec) && waterfallInsets.equals(Insets.NONE)) {
return NULL_PAIR;
}
@@ -750,9 +961,12 @@
Math.max(waterfallInsets.bottom, safeInset.bottom));
}
+ final CutoutPathParserInfo cutoutPathParserInfo = new CutoutPathParserInfo(displayWidth,
+ displayHeight, density, pathSpec.trim(), ROTATION_0, 1f /* scale */);
+
final DisplayCutout cutout = new DisplayCutout(
- safeInset, waterfallInsets, boundLeft, boundTop,
- boundRight, boundBottom, false /* copyArguments */);
+ safeInset, waterfallInsets, boundLeft, boundTop, boundRight, boundBottom,
+ cutoutPathParserInfo , false /* copyArguments */);
final Pair<Path, DisplayCutout> result = new Pair<>(cutoutSpec.getPath(), cutout);
synchronized (CACHE_LOCK) {
sCachedSpec = spec;
@@ -817,6 +1031,12 @@
out.writeTypedObject(cutout.mSafeInsets, flags);
out.writeTypedArray(cutout.mBounds.getRects(), flags);
out.writeTypedObject(cutout.mWaterfallInsets, flags);
+ out.writeInt(cutout.mCutoutPathParserInfo.getDisplayWidth());
+ out.writeInt(cutout.mCutoutPathParserInfo.getDisplayHeight());
+ out.writeFloat(cutout.mCutoutPathParserInfo.getDensity());
+ out.writeString(cutout.mCutoutPathParserInfo.getCutoutSpec());
+ out.writeInt(cutout.mCutoutPathParserInfo.getRotation());
+ out.writeFloat(cutout.mCutoutPathParserInfo.getScale());
}
}
@@ -860,9 +1080,17 @@
Rect[] bounds = new Rect[BOUNDS_POSITION_LENGTH];
in.readTypedArray(bounds, Rect.CREATOR);
Insets waterfallInsets = in.readTypedObject(Insets.CREATOR);
+ int displayWidth = in.readInt();
+ int displayHeight = in.readInt();
+ float density = in.readFloat();
+ String cutoutSpec = in.readString();
+ int rotation = in.readInt();
+ float scale = in.readFloat();
+ final CutoutPathParserInfo info = new CutoutPathParserInfo(
+ displayWidth, displayHeight, density, cutoutSpec, rotation, scale);
return new DisplayCutout(
- safeInsets, waterfallInsets, bounds, false /* copyArguments */);
+ safeInsets, waterfallInsets, bounds, info, false /* copyArguments */);
}
public DisplayCutout get() {
@@ -884,7 +1112,15 @@
bounds.scale(scale);
final Rect waterfallInsets = mInner.mWaterfallInsets.toRect();
waterfallInsets.scale(scale);
- mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds);
+ final CutoutPathParserInfo info = new CutoutPathParserInfo(
+ mInner.mCutoutPathParserInfo.getDisplayWidth(),
+ mInner.mCutoutPathParserInfo.getDisplayHeight(),
+ mInner.mCutoutPathParserInfo.getDensity(),
+ mInner.mCutoutPathParserInfo.getCutoutSpec(),
+ mInner.mCutoutPathParserInfo.getRotation(),
+ scale);
+
+ mInner = new DisplayCutout(safeInsets, Insets.of(waterfallInsets), bounds, info);
}
@Override
diff --git a/core/java/android/view/SurfaceControl.java b/core/java/android/view/SurfaceControl.java
index 106e392..0a1a231 100644
--- a/core/java/android/view/SurfaceControl.java
+++ b/core/java/android/view/SurfaceControl.java
@@ -414,6 +414,15 @@
*/
public static final int SECURE = 0x00000080;
+
+ /**
+ * Queue up BufferStateLayer buffers instead of dropping the oldest buffer when this flag is
+ * set. This blocks the client until all the buffers have been presented. If the buffers
+ * have presentation timestamps, then we may drop buffers.
+ * @hide
+ */
+ public static final int ENABLE_BACKPRESSURE = 0x00000100;
+
/**
* Surface creation flag: Creates a surface where color components are interpreted
* as "non pre-multiplied" by their alpha channel. Of course this flag is
diff --git a/core/java/android/view/SyncRtSurfaceTransactionApplier.java b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
index b10370a..acbcbfa 100644
--- a/core/java/android/view/SyncRtSurfaceTransactionApplier.java
+++ b/core/java/android/view/SyncRtSurfaceTransactionApplier.java
@@ -85,12 +85,13 @@
for (int i = params.length - 1; i >= 0; i--) {
SurfaceParams surfaceParams = params[i];
SurfaceControl surface = surfaceParams.surface;
- if (frame > 0) {
- t.deferTransactionUntil(surface, mTargetSc, frame);
- }
applyParams(t, surfaceParams, mTmpFloat9);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
}
public static void applyParams(Transaction t, SurfaceParams params, float[] tmpFloat9) {
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 844fc26..52d0062 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -10109,7 +10109,7 @@
* Merges the transaction passed in with the next transaction in BLASTBufferQueue. This ensures
* you can add transactions to the upcoming frame.
*/
- void mergeWithNextTransaction(Transaction t, long frameNumber) {
+ public void mergeWithNextTransaction(Transaction t, long frameNumber) {
if (mBlastBufferQueue != null) {
mBlastBufferQueue.mergeWithNextTransaction(t, frameNumber);
}
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 0e878fc..4ef63ae 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -262,7 +262,7 @@
}
}
- binder::Status onScreenCaptureComplete(
+ binder::Status onScreenCaptureCompleted(
const gui::ScreenCaptureResults& captureResults) override {
JNIEnv* env = getenv();
if (captureResults.result != NO_ERROR || captureResults.buffer == nullptr) {
@@ -270,6 +270,7 @@
gScreenCaptureListenerClassInfo.onScreenCaptureComplete, nullptr);
return binder::Status::ok();
}
+ captureResults.fence->waitForever("");
jobject jhardwareBuffer = android_hardware_HardwareBuffer_createFromAHardwareBuffer(
env, captureResults.buffer->toAHardwareBuffer());
const jint namedColorSpace =
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 396f954..4a0a35d 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2697,11 +2697,11 @@
The app can check whether it has this authorization by calling
{@link android.provider.Settings#canDrawOverlays
Settings.canDrawOverlays()}.
- <p>Protection level: signature|appop|preinstalled|pre23|development -->
+ <p>Protection level: signature|appop|installer|recents|appPredictor|pre23|development -->
<permission android:name="android.permission.SYSTEM_ALERT_WINDOW"
android:label="@string/permlab_systemAlertWindow"
android:description="@string/permdesc_systemAlertWindow"
- android:protectionLevel="signature|appop|preinstalled|pre23|development" />
+ android:protectionLevel="signature|appop|installer|recents|appPredictor|pre23|development" />
<!-- @SystemApi @hide Allows an application to create windows using the type
{@link android.view.WindowManager.LayoutParams#TYPE_APPLICATION_OVERLAY},
diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml
index 415a0a2..14f1e0e 100644
--- a/core/res/res/values/attrs.xml
+++ b/core/res/res/values/attrs.xml
@@ -9276,6 +9276,12 @@
{@deprecated Use app:fontProviderCerts with Jetpack Core library instead.}
-->
<attr name="fontProviderCerts" format="reference" />
+ <!-- Provides the system font family name to check before downloading the font. For example
+ if the fontProviderQuery asked for "Sans Serif", it is possible to define
+ fontProviderSystemFontFamily as "sans-serif" to tell the system to use "sans-serif" font
+ family if it exists on the system.
+ -->
+ <attr name="fontProviderSystemFontFamily" format="string" />
</declare-styleable>
<!-- Attributes that are read when parsing a tag. -->
diff --git a/core/res/res/values/colors.xml b/core/res/res/values/colors.xml
index 8940776..4f920da 100644
--- a/core/res/res/values/colors.xml
+++ b/core/res/res/values/colors.xml
@@ -240,34 +240,34 @@
<color name="system_main_0">#ffffff</color>
<!-- Shade of the main system color at 95% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_50">#ebf1f8</color>
+ <color name="system_main_50">#f0f0f0</color>
<!-- Shade of the main system color at 90% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_100">#dde3ea</color>
+ <color name="system_main_100">#e2e2e2</color>
<!-- Shade of the main system color at 80% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_200">#c1c7cd</color>
+ <color name="system_main_200">#c6c6c6</color>
<!-- Shade of the main system color at 70% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_300">#a6acb2</color>
+ <color name="system_main_300">#ababab</color>
<!-- Shade of the main system color at 60% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_400">#8b9197</color>
+ <color name="system_main_400">#909090</color>
<!-- Shade of the main system color at 50% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_500">#72787d</color>
+ <color name="system_main_500">#777777</color>
<!-- Shade of the main system color at 40% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_600">#595f64</color>
+ <color name="system_main_600">#5e5e5e</color>
<!-- Shade of the main system color at 30% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_700">#42474d</color>
+ <color name="system_main_700">#464646</color>
<!-- Shade of the main system color at 20% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_800">#2c3136</color>
+ <color name="system_main_800">#303030</color>
<!-- Shade of the main system color at 10% lightness.
This value can be overlaid at runtime by OverlayManager RROs. -->
- <color name="system_main_900">#171c21</color>
+ <color name="system_main_900">#1b1b1b</color>
<!-- Darkest shade of the main color used by the system. Black.
This value can be overlaid at runtime by OverlayManager RROs. -->
<color name="system_main_1000">#000000</color>
diff --git a/core/res/res/values/colors_device_defaults.xml b/core/res/res/values/colors_device_defaults.xml
index e46147e..6e9eb82 100644
--- a/core/res/res/values/colors_device_defaults.xml
+++ b/core/res/res/values/colors_device_defaults.xml
@@ -17,14 +17,14 @@
<!-- Colors specific to DeviceDefault themes. These are mostly pass-throughs to enable
overlaying new theme colors. -->
<resources>
- <color name="primary_device_default_dark">@color/primary_material_dark</color>
- <color name="primary_device_default_light">@color/primary_material_light</color>
- <color name="primary_device_default_settings">@color/primary_material_settings</color>
- <color name="primary_device_default_settings_light">@color/primary_material_settings_light</color>
- <color name="primary_dark_device_default_dark">@color/primary_dark_material_dark</color>
- <color name="primary_dark_device_default_light">@color/primary_dark_material_light</color>
- <color name="primary_dark_device_default_settings">@color/primary_dark_material_settings</color>
- <color name="primary_dark_device_default_settings_light">@color/primary_dark_material_settings_light</color>
+ <color name="primary_device_default_dark">@color/system_main_900</color>
+ <color name="primary_device_default_light">@color/system_main_50</color>
+ <color name="primary_device_default_settings">@color/system_main_900</color>
+ <color name="primary_device_default_settings_light">@color/primary_device_default_light</color>
+ <color name="primary_dark_device_default_dark">@color/primary_device_default_dark</color>
+ <color name="primary_dark_device_default_light">@color/primary_device_default_light</color>
+ <color name="primary_dark_device_default_settings">@color/primary_device_default_dark</color>
+ <color name="primary_dark_device_default_settings_light">@color/primary_device_default_light</color>
<color name="navigation_bar_divider_device_default_settings">#1f000000</color>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index a928408..5e0cda6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -4632,6 +4632,10 @@
maximum screen area that can be occupied by the app in the letterbox mode. -->
<item name="config_taskLetterboxAspectRatio" format="float" type="dimen">0.0</item>
+ <!-- Corners radius for activity presented the letterbox mode. Values < 0 will be ignored and
+ corners of the activity won't be rounded. -->
+ <integer name="config_letterboxActivityCornersRadius">0</integer>
+
<!-- If true, hide the display cutout with display area -->
<bool name="config_hideDisplayCutoutWithDisplayArea">false</bool>
@@ -4663,4 +4667,7 @@
<bool name="config_telephony5gStandalone">false</bool>
<!-- Whether the device enable the non-standalone (NSA) mode of 5G NR.-->
<bool name="config_telephony5gNonStandalone">false</bool>
+
+ <!-- Whether to select voice/data/sms preference without user confirmation -->
+ <bool name="config_voice_data_sms_auto_fallback">false</bool>
</resources>
diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml
index ddf3c5f..9c1c51c 100644
--- a/core/res/res/values/public.xml
+++ b/core/res/res/values/public.xml
@@ -3058,6 +3058,7 @@
<public name="sspSuffix" />
<public name="pathAdvancedPattern" />
<public name="sspAdvancedPattern" />
+ <public name="fontProviderSystemFontFamily" />
</public-group>
<public-group type="drawable" first-id="0x010800b5">
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index bcef680..dfccdf4 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -4132,6 +4132,7 @@
<java-symbol type="dimen" name="controls_thumbnail_image_max_width" />
<java-symbol type="dimen" name="config_taskLetterboxAspectRatio" />
+ <java-symbol type="integer" name="config_letterboxActivityCornersRadius" />
<java-symbol type="bool" name="config_hideDisplayCutoutWithDisplayArea" />
@@ -4166,4 +4167,6 @@
<java-symbol type="bool" name="config_telephony5gStandalone" />
<java-symbol type="bool" name="config_telephony5gNonStandalone" />
+
+ <java-symbol type="bool" name="config_voice_data_sms_auto_fallback" />
</resources>
diff --git a/core/tests/coretests/res/font/samplexmldownloadedfont.xml b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
index f1bdc47..9c32ffb 100644
--- a/core/tests/coretests/res/font/samplexmldownloadedfont.xml
+++ b/core/tests/coretests/res/font/samplexmldownloadedfont.xml
@@ -2,5 +2,6 @@
<font-family xmlns:android="http://schemas.android.com/apk/res/android"
android:fontProviderAuthority="com.example.test.fontprovider.authority"
android:fontProviderPackage="com.example.test.fontprovider.package"
- android:fontProviderQuery="MyRequestedFont">
+ android:fontProviderQuery="MyRequestedFont"
+ android:fontProviderSystemFontFamily="my-request-font">
</font-family>
\ No newline at end of file
diff --git a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
index 24f45a5..bffd1e4 100644
--- a/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
+++ b/core/tests/coretests/src/android/content/pm/SigningDetailsTest.java
@@ -28,6 +28,7 @@
import static org.junit.Assert.fail;
import android.content.pm.PackageParser.SigningDetails;
+import android.util.ArraySet;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
@@ -35,6 +36,8 @@
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.Set;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
public class SigningDetailsTest {
@@ -208,8 +211,8 @@
SigningDetails result1 = noLineageDetails.mergeLineageWith(lineageDetails);
SigningDetails result2 = lineageDetails.mergeLineageWith(noLineageDetails);
- assertTrue(result1 == lineageDetails);
- assertTrue(result2 == lineageDetails);
+ assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE);
+ assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE);
}
@Test
@@ -271,8 +274,10 @@
SigningDetails result1 = singleSignerDetails.mergeLineageWith(fullLineageDetails);
SigningDetails result2 = fullLineageDetails.mergeLineageWith(singleSignerDetails);
- assertTrue(result1 == fullLineageDetails);
- assertTrue(result2 == fullLineageDetails);
+ assertSigningDetailsContainsLineage(result1, FIRST_SIGNATURE, SECOND_SIGNATURE,
+ THIRD_SIGNATURE);
+ assertSigningDetailsContainsLineage(result2, FIRST_SIGNATURE, SECOND_SIGNATURE,
+ THIRD_SIGNATURE);
}
@Test
@@ -605,6 +610,213 @@
assertTrue(secondLineageDetails.hasCommonAncestor(firstLineageDetails));
}
+ @Test
+ public void hasCommonSignerWithCapabilities_singleMatchingSigner_returnsTrue()
+ throws Exception {
+ // The hasCommonSignerWithCapabilities method is intended to grant the specified
+ // capabilities to a requesting package that has a common signer in the lineage (or as the
+ // current signer) even if their signing identities have diverged. This test verifies if the
+ // two SigningDetails have the same single signer then the requested capability can be
+ // granted since the current signer always has all capabilities granted.
+ SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+ SigningDetails secondSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+ assertTrue(firstDetails.hasCommonSignerWithCapability(secondSignerDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_singleDifferentSigners_returnsFalse()
+ throws Exception {
+ // If each package is signed by a single different signer then the method should return
+ // false since there is no shared signer.
+ SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE);
+ SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE);
+
+ assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+ assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_oneWithMultipleSigners_returnsFalse()
+ throws Exception {
+ // If one of the packages is signed with multiple signers and the other only a single signer
+ // this method should return false since all signers must match exactly for multiple signer
+ // cases.
+ SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE);
+
+ assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+ assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_multipleMatchingSigners_returnsTrue()
+ throws Exception {
+ // if both packages are signed by the same multiple signers then this method should return
+ // true since the current signer is granted all capabilities.
+ SigningDetails firstDetails = createSigningDetails(FIRST_SIGNATURE, SECOND_SIGNATURE);
+ SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE, FIRST_SIGNATURE);
+
+ assertTrue(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+ assertTrue(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_singleSignerInLineage_returnsTrue()
+ throws Exception {
+ // if a single signer is in the lineage and that previous signer has the requested
+ // capability then this method should return true.
+ SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+ new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+ SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+ assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_singleSignerInLineageWOCapability_returnsFalse()
+ throws Exception {
+ // If a single signer is in the lineage and that previous signer does not have the requested
+ // capability then this method should return false.
+ SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+ new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES});
+ SigningDetails singleSignerDetails = createSigningDetails(FIRST_SIGNATURE);
+
+ assertFalse(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_singleSignerMatchesCurrentSigner_returnsTrue()
+ throws Exception {
+ // If a requesting app is signed by the same current signer as an app with a lineage the
+ // method should return true since the current signer is granted all capabilities.
+ SigningDetails lineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE},
+ new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES});
+ SigningDetails singleSignerDetails = createSigningDetails(SECOND_SIGNATURE);
+
+ assertTrue(lineageDetails.hasCommonSignerWithCapability(singleSignerDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_divergingSignersWithCommonSigner_returnsTrue()
+ throws Exception {
+ // This method is intended to allow granting a capability to another app that has a common
+ // signer in the lineage with the capability still granted; this test verifies when the
+ // current signers diverge but a common ancestor has the requested capability this method
+ // returns true.
+ SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+ SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+ PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_divergingSignersOneGrantsCapability_returnsTrue()
+ throws Exception {
+ // If apps have multiple common signers in the lineage with one denying the requested
+ // capability but the other granting it this method should return true.
+ SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+ SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+ PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_divergingSignersNoneGrantCapability_returnsFalse()
+ throws Exception {
+ // If apps have multiple common signers in the lineage with all denying the requested
+ // capability this method should return false.
+ SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{SHARED_USER_ID, AUTH, DEFAULT_CAPABILITIES});
+ SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+ PERMISSION));
+ }
+
+ @Test
+ public void
+ hasCommonSignerWithCapabilities_divergingSignersNoneGrantsAllCapabilities_returnsTrue()
+ throws Exception {
+ // If an app has multiple common signers in the lineage, each granting one of the requested
+ // capabilities but neither granting all this method should return false since a single
+ // common ancestor must grant all requested capabilities.
+ SigningDetails firstLineageDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{SHARED_USER_ID, PERMISSION, DEFAULT_CAPABILITIES});
+ SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, FOURTH_SIGNATURE);
+
+ assertFalse(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+ PERMISSION | SHARED_USER_ID));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_currentSignerInLineageOfRequestingApp_returnsTrue()
+ throws Exception {
+ // If the current signer of an app is in the lineage of the requesting app then this method
+ // should return true since the current signer is granted all capabilities.
+ SigningDetails firstLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE);
+ SigningDetails secondLineageDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE, THIRD_SIGNATURE);
+
+ assertTrue(firstLineageDetails.hasCommonSignerWithCapability(secondLineageDetails,
+ PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_currentSignerInLineageOfDeclaringApp_returnsTrue()
+ throws Exception {
+ // If the current signer of a requesting app with a lineage is in the lineage of the
+ // declaring app and that previous signature is granted the requested capability the method
+ // should return true.
+ SigningDetails declaringDetails = createSigningDetailsWithLineageAndCapabilities(
+ new String[]{FIRST_SIGNATURE, SECOND_SIGNATURE, THIRD_SIGNATURE},
+ new int[]{SHARED_USER_ID, DEFAULT_CAPABILITIES, DEFAULT_CAPABILITIES});
+ SigningDetails requestingDetails = createSigningDetailsWithLineage(FIRST_SIGNATURE,
+ SECOND_SIGNATURE);
+
+ assertTrue(declaringDetails.hasCommonSignerWithCapability(requestingDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_oneSignerNullLineage_returns() throws Exception {
+ // While the pastSigningCertificates should only be null in the case of multiple current
+ // signers there are instances where this can be null with a single signer; verify that a
+ // null pastSigningCertificates array in either SigningDetails does not result in a
+ // NullPointerException.
+ SigningDetails firstDetails = createSigningDetails(true, FIRST_SIGNATURE);
+ SigningDetails secondDetails = createSigningDetails(SECOND_SIGNATURE);
+
+ assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+ assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+ }
+
+ @Test
+ public void hasCommonSignerWithCapabilities_unknownSigner_returnsFalse() throws Exception {
+ // An unknown SigningDetails for either instance should immediately result in false being
+ // returned.
+ SigningDetails firstDetails = SigningDetails.UNKNOWN;
+ SigningDetails secondDetails = createSigningDetails(FIRST_SIGNATURE);
+
+ assertFalse(firstDetails.hasCommonSignerWithCapability(secondDetails, PERMISSION));
+ assertFalse(secondDetails.hasCommonSignerWithCapability(firstDetails, PERMISSION));
+ }
+
private SigningDetails createSigningDetailsWithLineage(String... signers) throws Exception {
int[] capabilities = new int[signers.length];
for (int i = 0; i < capabilities.length; i++) {
@@ -629,10 +841,34 @@
}
private SigningDetails createSigningDetails(String... signers) throws Exception {
+ return createSigningDetails(false, signers);
+ }
+
+ private SigningDetails createSigningDetails(boolean useNullPastSigners, String... signers)
+ throws Exception {
Signature[] currentSignatures = new Signature[signers.length];
for (int i = 0; i < signers.length; i++) {
currentSignatures[i] = new Signature(signers[i]);
}
- return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
+ // If there are multiple signers then the pastSigningCertificates should be set to null, but
+ // if there is only a single signer both the current signer and the past signers should be
+ // set to that one signer.
+ if (signers.length > 1) {
+ return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, null);
+ }
+ return new SigningDetails(currentSignatures, SIGNING_BLOCK_V3, currentSignatures);
+ }
+
+ private void assertSigningDetailsContainsLineage(SigningDetails details,
+ String... pastSigners) {
+ // This method should only be invoked for results that contain a single signer.
+ assertEquals(1, details.signatures.length);
+ assertTrue(details.signatures[0].toCharsString().equalsIgnoreCase(
+ pastSigners[pastSigners.length - 1]));
+ Set<String> signatures = new ArraySet<>(pastSigners);
+ for (Signature pastSignature : details.pastSigningCertificates) {
+ assertTrue(signatures.remove(pastSignature.toCharsString()));
+ }
+ assertEquals(0, signatures.size());
}
}
diff --git a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
index 7ab9d7f..57f01e9 100644
--- a/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
+++ b/core/tests/coretests/src/android/content/res/FontResourcesParserTest.java
@@ -105,6 +105,7 @@
assertEquals("com.example.test.fontprovider.authority", providerEntry.getAuthority());
assertEquals("com.example.test.fontprovider.package", providerEntry.getPackage());
assertEquals("MyRequestedFont", providerEntry.getQuery());
+ assertEquals("my-request-font", providerEntry.getSystemFontFamilyName());
}
@Test
diff --git a/core/tests/coretests/src/android/view/DisplayCutoutTest.java b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
index d02c6d5..a5261ae 100644
--- a/core/tests/coretests/src/android/view/DisplayCutoutTest.java
+++ b/core/tests/coretests/src/android/view/DisplayCutoutTest.java
@@ -22,6 +22,8 @@
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -30,6 +32,7 @@
import static org.junit.Assert.assertTrue;
import android.graphics.Insets;
+import android.graphics.Path;
import android.graphics.Rect;
import android.os.Parcel;
import android.platform.test.annotations.Presubmit;
@@ -130,6 +133,8 @@
@Test
public void testHasCutout_noCutout() throws Exception {
assertTrue(NO_CUTOUT.isBoundsEmpty());
+ assertThat(NO_CUTOUT.getWaterfallInsets(), equalTo(Insets.NONE));
+ assertThat(NO_CUTOUT.getCutoutPath(), nullValue());
}
@Test
@@ -165,6 +170,59 @@
}
@Test
+ public void testGetCutoutPath() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE);
+ assertThat(cutout.getCutoutPath(), notNullValue());
+ }
+
+ @Test
+ public void testGetCutoutPath_caches() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final Path first = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ final Path second = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ assertThat(first, equalTo(second));
+ }
+
+ @Test
+ public void testGetCutoutPath_wontCacheIfCutoutPathParerInfoChanged() throws Exception {
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final Path first = fromSpec("L1,0 L1,1 L0,1 z", displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ final Path second = fromSpec("L2,0 L2,2 L0,2 z", displayWidth, displayHeight,
+ density, Insets.NONE).getCutoutPath();
+ assertThat(first, not(equalTo(second)));
+ }
+
+ @Test
+ public void testGetCutoutPathParserInfo() throws Exception {
+ final String cutoutSpecString = "L1,0 L1,1 L0,1 z";
+ final int displayWidth = 200;
+ final int displayHeight = 400;
+ final float density = 1f;
+ final DisplayCutout cutout = fromSpec(cutoutSpecString, displayWidth, displayHeight,
+ density, Insets.NONE);
+ assertThat(displayWidth, equalTo(cutout.getCutoutPathParserInfo().getDisplayWidth()));
+ assertThat(displayHeight, equalTo(cutout.getCutoutPathParserInfo().getDisplayHeight()));
+ assertThat(density, equalTo(cutout.getCutoutPathParserInfo().getDensity()));
+ assertThat(cutoutSpecString.trim(),
+ equalTo(cutout.getCutoutPathParserInfo().getCutoutSpec()));
+ assertThat(0, equalTo(cutout.getCutoutPathParserInfo().getRotation()));
+ assertThat(1f, equalTo(cutout.getCutoutPathParserInfo().getScale()));
+ }
+
+ @Test
public void testHashCode() throws Exception {
assertEquals(mCutoutWithWaterfall.hashCode(), createCutoutWithWaterfall().hashCode());
assertNotEquals(mCutoutWithWaterfall.hashCode(), mCutoutNumbers.hashCode());
diff --git a/graphics/java/android/graphics/Typeface.java b/graphics/java/android/graphics/Typeface.java
index a0568bf..005a726 100644
--- a/graphics/java/android/graphics/Typeface.java
+++ b/graphics/java/android/graphics/Typeface.java
@@ -70,6 +70,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
/**
* The Typeface class specifies the typeface and intrinsic style of a font.
@@ -249,6 +250,21 @@
}
/**
+ * Returns true if the system has the font family with the name [familyName]. For example
+ * querying with "sans-serif" would check if the "sans-serif" family is defined in the system
+ * and return true if does.
+ *
+ * @param familyName The name of the font family, cannot be null. If null, exception will be
+ * thrown.
+ */
+ private static boolean hasFontFamily(@NonNull String familyName) {
+ Objects.requireNonNull(familyName, "familyName cannot be null");
+ synchronized (SYSTEM_FONT_MAP_LOCK) {
+ return sSystemFontMap.containsKey(familyName);
+ }
+ }
+
+ /**
* @hide
* Used by Resources to load a font resource of type xml.
*/
@@ -257,6 +273,11 @@
FamilyResourceEntry entry, AssetManager mgr, String path) {
if (entry instanceof ProviderResourceEntry) {
final ProviderResourceEntry providerEntry = (ProviderResourceEntry) entry;
+
+ String systemFontFamilyName = providerEntry.getSystemFontFamilyName();
+ if (systemFontFamilyName != null && hasFontFamily(systemFontFamilyName)) {
+ return Typeface.create(systemFontFamilyName, NORMAL);
+ }
// Downloadable font
List<List<String>> givenCerts = providerEntry.getCerts();
List<List<byte[]>> certs = new ArrayList<>();
diff --git a/keystore/java/android/security/KeyStoreSecurityLevel.java b/keystore/java/android/security/KeyStoreSecurityLevel.java
index bc669ba..372add9 100644
--- a/keystore/java/android/security/KeyStoreSecurityLevel.java
+++ b/keystore/java/android/security/KeyStoreSecurityLevel.java
@@ -96,20 +96,21 @@
} catch (ServiceSpecificException e) {
switch (e.errorCode) {
case ResponseCode.BACKEND_BUSY: {
+ long backOffHint = (long) (Math.random() * 80 + 20);
if (CompatChanges.isChangeEnabled(
KeyStore2.KEYSTORE_OPERATION_CREATION_MAY_FAIL)) {
// Starting with Android S we inform the caller about the
// backend being busy.
- throw new BackendBusyException();
+ throw new BackendBusyException(backOffHint);
} else {
// Before Android S operation creation must always succeed. So we
// just have to retry. We do so with a randomized back-off between
- // 50 and 250ms.
+ // 20 and 100ms.
// It is a little awkward that we cannot break out of this loop
// by interrupting this thread. But that is the expected behavior.
// There is some comfort in the fact that interrupting a thread
// also does not unblock a thread waiting for a binder transaction.
- interruptedPreservingSleep((long) (Math.random() * 200 + 50));
+ interruptedPreservingSleep(backOffHint);
}
break;
}
diff --git a/keystore/java/android/security/keystore/BackendBusyException.java b/keystore/java/android/security/keystore/BackendBusyException.java
index 1a88469..a813e93 100644
--- a/keystore/java/android/security/keystore/BackendBusyException.java
+++ b/keystore/java/android/security/keystore/BackendBusyException.java
@@ -16,37 +16,66 @@
package android.security.keystore;
+import android.annotation.DurationMillisLong;
import android.annotation.NonNull;
import java.security.ProviderException;
/**
* Indicates a transient error that prevented a key operation from being created.
- * Callers should try again with a back-off period of 10-30 milliseconds.
+ * Callers should try again with a back-off period of {@link #getBackOffHintMillis()}
+ * milliseconds.
*/
public class BackendBusyException extends ProviderException {
+ private final long mBackOffHintMillis;
+
/**
* Constructs a new {@code BackendBusyException} without detail message and cause.
+ *
*/
- public BackendBusyException() {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis) {
super("The keystore backend has no operation slots available. Retry later.");
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* no cause.
*/
- public BackendBusyException(@NonNull String message) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message) {
super(message);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
/**
* Constructs a new {@code BackendBusyException} with the provided detail message and
* cause.
*/
- public BackendBusyException(@NonNull String message, @NonNull Throwable cause) {
+ public BackendBusyException(@DurationMillisLong long backOffHintMillis,
+ @NonNull String message, @NonNull Throwable cause) {
super(message, cause);
+ if (backOffHintMillis < 0) {
+ throw new IllegalArgumentException("Back-off hint cannot be negative.");
+ }
+ mBackOffHintMillis = backOffHintMillis;
}
+ /**
+ * When retrying to start a Keystore operation after receiving this exception, this can be
+ * used to determine how long to wait before retrying. It is not guaranteed that the operation
+ * will succeeds after this time. Multiple retries may be necessary if the system is congested.
+ *
+ * @return Number of milliseconds to back off before retrying.
+ */
+ public @DurationMillisLong long getBackOffHintMillis() {
+ return mBackOffHintMillis;
+ }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
index 1df2a4a..bb8a973 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/TaskView.java
@@ -225,8 +225,9 @@
mTaskOrganizer.applyTransaction(wct);
// TODO(b/151449487): Only call callback once we enable synchronization
if (mListener != null) {
+ final int taskId = mTaskInfo.taskId;
mListenerExecutor.execute(() -> {
- mListener.onTaskVisibilityChanged(mTaskInfo.taskId, mSurfaceCreated);
+ mListener.onTaskVisibilityChanged(taskId, mSurfaceCreated);
});
}
}
@@ -256,8 +257,10 @@
}
if (mListener != null) {
+ final int taskId = taskInfo.taskId;
+ final ComponentName baseActivity = taskInfo.baseActivity;
mListenerExecutor.execute(() -> {
- mListener.onTaskCreated(taskInfo.taskId, taskInfo.baseActivity);
+ mListener.onTaskCreated(taskId, baseActivity);
});
}
}
@@ -267,8 +270,9 @@
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
if (mListener != null) {
+ final int taskId = taskInfo.taskId;
mListenerExecutor.execute(() -> {
- mListener.onTaskRemovalStarted(taskInfo.taskId);
+ mListener.onTaskRemovalStarted(taskId);
});
}
mTaskOrganizer.setInterceptBackPressedOnTaskRoot(mTaskToken, false);
@@ -289,8 +293,9 @@
public void onBackPressedOnTaskRoot(ActivityManager.RunningTaskInfo taskInfo) {
if (mTaskToken == null || !mTaskToken.equals(taskInfo.token)) return;
if (mListener != null) {
+ final int taskId = taskInfo.taskId;
mListenerExecutor.execute(() -> {
- mListener.onBackPressedOnTaskRoot(taskInfo.taskId);
+ mListener.onBackPressedOnTaskRoot(taskId);
});
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
index 3181dbf..58a4baf 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/DisplayLayout.java
@@ -356,11 +356,11 @@
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return null;
}
- final Insets waterfallInsets =
- RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
if (rotation == ROTATION_0) {
return computeSafeInsets(cutout, displayWidth, displayHeight);
}
+ final Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
Rect[] cutoutRects = cutout.getBoundingRectsAll();
final Rect[] newBounds = new Rect[cutoutRects.length];
@@ -372,8 +372,12 @@
}
newBounds[getBoundIndexFromRotation(i, rotation)] = rect;
}
+ final DisplayCutout.CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+ final DisplayCutout.CutoutPathParserInfo newInfo = new DisplayCutout.CutoutPathParserInfo(
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+ info.getCutoutSpec(), rotation, info.getScale());
return computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+ DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? displayHeight : displayWidth,
rotated ? displayWidth : displayHeight);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
index 4874d3c..a4cd3c5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/HandlerExecutor.java
@@ -47,6 +47,11 @@
}
@Override
+ public void removeAllCallbacks() {
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
+ @Override
public void removeCallbacks(@NonNull Runnable r) {
mHandler.removeCallbacks(r);
}
@@ -55,9 +60,4 @@
public boolean hasCallback(Runnable r) {
return mHandler.hasCallbacks(r);
}
-
- @Override
- public Looper getLooper() {
- return mHandler.getLooper();
- }
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
index 1149cce..b736fb0 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/ShellExecutor.java
@@ -73,6 +73,11 @@
void executeDelayed(Runnable runnable, long delayMillis);
/**
+ * Removes all pending callbacks.
+ */
+ void removeAllCallbacks();
+
+ /**
* See {@link android.os.Handler#removeCallbacks}.
*/
void removeCallbacks(Runnable runnable);
@@ -81,9 +86,4 @@
* See {@link android.os.Handler#hasCallbacks(Runnable)}.
*/
boolean hasCallback(Runnable runnable);
-
- /**
- * Returns the looper that this executor is running on.
- */
- Looper getLooper();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
index d22abe4..125e322 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedAnimationController.java
@@ -24,6 +24,7 @@
import android.view.SurfaceControl;
import android.view.animation.Interpolator;
import android.view.animation.OvershootInterpolator;
+import android.window.WindowContainerToken;
import androidx.annotation.VisibleForTesting;
@@ -55,7 +56,7 @@
private final Interpolator mOvershootInterpolator;
private final OneHandedSurfaceTransactionHelper mSurfaceTransactionHelper;
- private final HashMap<SurfaceControl, OneHandedTransitionAnimator> mAnimatorMap =
+ private final HashMap<WindowContainerToken, OneHandedTransitionAnimator> mAnimatorMap =
new HashMap<>();
/**
@@ -67,23 +68,23 @@
}
@SuppressWarnings("unchecked")
- OneHandedTransitionAnimator getAnimator(SurfaceControl leash, Rect startBounds,
- Rect endBounds) {
- final OneHandedTransitionAnimator animator = mAnimatorMap.get(leash);
+ OneHandedTransitionAnimator getAnimator(WindowContainerToken token, SurfaceControl leash,
+ Rect startBounds, Rect endBounds) {
+ final OneHandedTransitionAnimator animator = mAnimatorMap.get(token);
if (animator == null) {
- mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ mAnimatorMap.put(token, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds)));
} else if (animator.isRunning()) {
animator.updateEndValue(endBounds);
} else {
animator.cancel();
- mAnimatorMap.put(leash, setupOneHandedTransitionAnimator(
- OneHandedTransitionAnimator.ofBounds(leash, startBounds, endBounds)));
+ mAnimatorMap.put(token, setupOneHandedTransitionAnimator(
+ OneHandedTransitionAnimator.ofBounds(token, leash, startBounds, endBounds)));
}
- return mAnimatorMap.get(leash);
+ return mAnimatorMap.get(token);
}
- HashMap<SurfaceControl, OneHandedTransitionAnimator> getAnimatorMap() {
+ HashMap<WindowContainerToken, OneHandedTransitionAnimator> getAnimatorMap() {
return mAnimatorMap;
}
@@ -91,8 +92,8 @@
return mAnimatorMap.isEmpty();
}
- void removeAnimator(SurfaceControl key) {
- final OneHandedTransitionAnimator animator = mAnimatorMap.remove(key);
+ void removeAnimator(WindowContainerToken token) {
+ final OneHandedTransitionAnimator animator = mAnimatorMap.remove(token);
if (animator != null && animator.isRunning()) {
animator.cancel();
}
@@ -116,6 +117,7 @@
ValueAnimator.AnimatorListener {
private final SurfaceControl mLeash;
+ private final WindowContainerToken mToken;
private T mStartValue;
private T mEndValue;
private T mCurrentValue;
@@ -128,8 +130,10 @@
private @TransitionDirection int mTransitionDirection;
- private OneHandedTransitionAnimator(SurfaceControl leash, T startValue, T endValue) {
+ private OneHandedTransitionAnimator(WindowContainerToken token, SurfaceControl leash,
+ T startValue, T endValue) {
mLeash = leash;
+ mToken = token;
mStartValue = startValue;
mEndValue = endValue;
addListener(this);
@@ -208,8 +212,8 @@
return this;
}
- SurfaceControl getLeash() {
- return mLeash;
+ WindowContainerToken getToken() {
+ return mToken;
}
Rect getDestinationBounds() {
@@ -254,10 +258,10 @@
}
@VisibleForTesting
- static OneHandedTransitionAnimator<Rect> ofBounds(SurfaceControl leash,
- Rect startValue, Rect endValue) {
+ static OneHandedTransitionAnimator<Rect> ofBounds(WindowContainerToken token,
+ SurfaceControl leash, Rect startValue, Rect endValue) {
- return new OneHandedTransitionAnimator<Rect>(leash, new Rect(startValue),
+ return new OneHandedTransitionAnimator<Rect>(token, leash, new Rect(startValue),
new Rect(endValue)) {
private final Rect mTmpRect = new Rect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
index a74f476..37a91d0c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedBackgroundPanelOrganizer.java
@@ -56,7 +56,7 @@
private final float[] mColor;
private final float mAlpha;
private final Rect mRect;
- private final Handler mHandler;
+ private final Executor mMainExecutor;
private final Point mDisplaySize = new Point();
private final OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
mSurfaceControlTransactionFactory;
@@ -76,13 +76,13 @@
@Override
public void onOneHandedAnimationStart(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mHandler.post(() -> showBackgroundPanelLayer());
+ mMainExecutor.execute(() -> showBackgroundPanelLayer());
}
};
@Override
public void onStopFinished(Rect bounds) {
- mHandler.post(() -> removeBackgroundPanelLayer());
+ mMainExecutor.execute(() -> removeBackgroundPanelLayer());
}
public OneHandedBackgroundPanelOrganizer(Context context, DisplayController displayController,
@@ -94,7 +94,7 @@
mColor = new float[]{defaultRGB, defaultRGB, defaultRGB};
mAlpha = res.getFloat(R.dimen.config_one_handed_background_alpha);
mRect = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
- mHandler = new Handler();
+ mMainExecutor = executor;
mSurfaceControlTransactionFactory = SurfaceControl.Transaction::new;
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
index d2d5591..1da72f8 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java
@@ -26,11 +26,11 @@
import android.graphics.Rect;
import android.os.SystemProperties;
import android.util.ArrayMap;
-import android.util.Log;
import android.view.SurfaceControl;
import android.window.DisplayAreaAppearedInfo;
import android.window.DisplayAreaInfo;
import android.window.DisplayAreaOrganizer;
+import android.window.WindowContainerToken;
import android.window.WindowContainerTransaction;
import androidx.annotation.NonNull;
@@ -44,8 +44,6 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
-import java.util.Objects;
-import java.util.concurrent.Executor;
/**
* Manages OneHanded display areas such as offset.
@@ -69,7 +67,7 @@
private int mEnterExitAnimationDurationMs;
@VisibleForTesting
- ArrayMap<DisplayAreaInfo, SurfaceControl> mDisplayAreaMap = new ArrayMap();
+ ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap();
private DisplayController mDisplayController;
private OneHandedAnimationController mAnimationController;
private OneHandedSurfaceTransactionHelper.SurfaceControlTransactionFactory
@@ -89,7 +87,7 @@
@Override
public void onOneHandedAnimationEnd(SurfaceControl.Transaction tx,
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mAnimationController.removeAnimator(animator.getLeash());
+ mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
@@ -99,7 +97,7 @@
@Override
public void onOneHandedAnimationCancel(
OneHandedAnimationController.OneHandedTransitionAnimator animator) {
- mAnimationController.removeAnimator(animator.getLeash());
+ mAnimationController.removeAnimator(animator.getToken());
if (mAnimationController.isAnimatorsConsumed()) {
finishOffset(animator.getDestinationOffset(),
animator.getTransitionDirection());
@@ -119,7 +117,6 @@
super(mainExecutor);
mAnimationController = animationController;
mDisplayController = displayController;
- mDefaultDisplayBounds.set(getDisplayBounds());
mLastVisualDisplayBounds.set(getDisplayBounds());
final int animationDurationConfig = context.getResources().getInteger(
R.integer.config_one_handed_translate_animation_duration);
@@ -134,24 +131,12 @@
@Override
public void onDisplayAreaAppeared(@NonNull DisplayAreaInfo displayAreaInfo,
@NonNull SurfaceControl leash) {
- Objects.requireNonNull(displayAreaInfo, "displayAreaInfo must not be null");
- Objects.requireNonNull(leash, "leash must not be null");
- if (mDisplayAreaMap.get(displayAreaInfo) == null) {
- // mDefaultDisplayBounds may out of date after removeDisplayChangingController()
- mDefaultDisplayBounds.set(getDisplayBounds());
- mDisplayAreaMap.put(displayAreaInfo, leash);
- }
+ mDisplayAreaTokenMap.put(displayAreaInfo.token, leash);
}
@Override
public void onDisplayAreaVanished(@NonNull DisplayAreaInfo displayAreaInfo) {
- Objects.requireNonNull(displayAreaInfo,
- "Requires valid displayArea, and displayArea must not be null");
- if (!mDisplayAreaMap.containsKey(displayAreaInfo)) {
- Log.w(TAG, "Unrecognized token: " + displayAreaInfo.token);
- return;
- }
- mDisplayAreaMap.remove(displayAreaInfo);
+ mDisplayAreaTokenMap.remove(displayAreaInfo.token);
}
@Override
@@ -162,6 +147,7 @@
final DisplayAreaAppearedInfo info = displayAreaInfos.get(i);
onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash());
}
+ mDefaultDisplayBounds.set(getDisplayBounds());
return displayAreaInfos;
}
@@ -176,9 +162,9 @@
* handles 90 degree display rotation changes {@link Surface.Rotation}.
*
* @param fromRotation starting rotation of the display.
- * @param toRotation target rotation of the display (after rotating).
- * @param wct A task transaction {@link WindowContainerTransaction} from
- * {@link DisplayChangeController} to populate.
+ * @param toRotation target rotation of the display (after rotating).
+ * @param wct A task transaction {@link WindowContainerTransaction} from
+ * {@link DisplayChangeController} to populate.
*/
public void onRotateDisplay(int fromRotation, int toRotation, WindowContainerTransaction wct) {
// Stop one handed without animation and reset cropped size immediately
@@ -210,11 +196,11 @@
: TRANSITION_DIRECTION_EXIT;
final WindowContainerTransaction wct = new WindowContainerTransaction();
- mDisplayAreaMap.forEach(
- (key, leash) -> {
- animateWindows(leash, fromBounds, toBounds, direction,
+ mDisplayAreaTokenMap.forEach(
+ (token, leash) -> {
+ animateWindows(token, leash, fromBounds, toBounds, direction,
mEnterExitAnimationDurationMs);
- wct.setBounds(key.token, toBounds);
+ wct.setBounds(token, toBounds);
});
applyTransaction(wct);
}
@@ -222,10 +208,10 @@
private void resetWindowsOffset(WindowContainerTransaction wct) {
final SurfaceControl.Transaction tx =
mSurfaceControlTransactionFactory.getTransaction();
- mDisplayAreaMap.forEach(
- (key, leash) -> {
+ mDisplayAreaTokenMap.forEach(
+ (token, leash) -> {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimatorMap().remove(leash);
+ mAnimationController.getAnimatorMap().remove(token);
if (animator != null && animator.isRunning()) {
animator.cancel();
}
@@ -233,16 +219,17 @@
.setWindowCrop(leash, -1/* reset */, -1/* reset */);
// DisplayRotationController will applyTransaction() after finish rotating
if (wct != null) {
- wct.setBounds(key.token, null/* reset */);
+ wct.setBounds(token, null/* reset */);
}
});
tx.apply();
}
- private void animateWindows(SurfaceControl leash, Rect fromBounds, Rect toBounds,
- @OneHandedAnimationController.TransitionDirection int direction, int durationMs) {
+ private void animateWindows(WindowContainerToken token, SurfaceControl leash, Rect fromBounds,
+ Rect toBounds, @OneHandedAnimationController.TransitionDirection int direction,
+ int durationMs) {
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
- mAnimationController.getAnimator(leash, fromBounds, toBounds);
+ mAnimationController.getAnimator(token, leash, fromBounds, toBounds);
if (animator != null) {
animator.setTransitionDirection(direction)
.addOneHandedAnimationCallback(mOneHandedAnimationCallback)
@@ -311,8 +298,8 @@
pw.println(TAG + "states: ");
pw.print(innerPrefix + "mIsInOneHanded=");
pw.println(mIsInOneHanded);
- pw.print(innerPrefix + "mDisplayAreaMap=");
- pw.println(mDisplayAreaMap);
+ pw.print(innerPrefix + "mDisplayAreaTokenMap=");
+ pw.println(mDisplayAreaTokenMap);
pw.print(innerPrefix + "mDefaultDisplayBounds=");
pw.println(mDefaultDisplayBounds);
pw.print(innerPrefix + "mLastVisualDisplayBounds=");
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
index 1ed121f..49b7e05 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedGestureHandler.java
@@ -221,8 +221,14 @@
displaySize.y);
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-gesture-offset", DEFAULT_DISPLAY);
- mInputEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ mInputEventReceiver = new EventReceiver(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ });
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to create input event receiver", e);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
index 60709be..c7a49ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedTouchHandler.java
@@ -132,8 +132,14 @@
if (mIsEnabled) {
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"onehanded-touch", DEFAULT_DISPLAY);
- mInputEventReceiver = new EventReceiver(
- mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ mInputEventReceiver = new EventReceiver(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ });
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to create input event receiver", e);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
index 7a634c3..6e3a20d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipInputConsumer.java
@@ -147,7 +147,7 @@
// Choreographer.getSfInstance() must be called on the thread that the input event
// receiver should be receiving events
mInputEventReceiver = new InputEventReceiver(inputChannel,
- mMainExecutor.getLooper(), Choreographer.getSfInstance());
+ Looper.myLooper(), Choreographer.getSfInstance());
if (mRegistrationListener != null) {
mRegistrationListener.onRegistrationChanged(true /* isRegistered */);
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
index 41cc59d..8fb358a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java
@@ -212,8 +212,14 @@
// Register input event receiver
mInputMonitor = InputManager.getInstance().monitorGestureInput(
"pip-resize", mDisplayId);
- mInputEventReceiver = new PipResizeInputEventReceiver(
- mInputMonitor.getInputChannel(), mMainExecutor.getLooper());
+ try {
+ mMainExecutor.executeBlocking(() -> {
+ mInputEventReceiver = new PipResizeInputEventReceiver(
+ mInputMonitor.getInputChannel(), Looper.myLooper());
+ });
+ } catch (InterruptedException e) {
+ throw new RuntimeException("Failed to create input event receiver", e);
+ }
}
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index c9f5ae2..2b8b53c 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -95,7 +95,6 @@
private int mDeferResizeToNormalBoundsUntilRotation = -1;
private int mDisplayRotation;
- private final Handler mHandler = new Handler();
private final PipAccessibilityInteractionConnection mConnection;
// Behaviour states
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
index b5d5d0f..564a418 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestCannotPairNonResizeableApps.kt
@@ -54,7 +54,7 @@
@Parameterized.Parameters(name = "{0}")
@JvmStatic
fun getParams(): List<Array<Any>> {
- val testTag = "testAppPairs_unpairPrimaryAndSecondaryApps"
+ val testTag = "testAppPairs_cannotPairNonResizeableApps"
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
buildTestTag(testTag, configuration)
@@ -71,7 +71,7 @@
appPairsDividerIsInvisible()
}
windowManagerTrace {
- end {
+ end("onlyResizeableAppWindowVisible") {
val nonResizeableApp = nonResizeableApp
require(nonResizeableApp != null) {
"Non resizeable app not initialized"
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
index 54e074c..f63eb1d 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestPairPrimaryAndSecondaryApps.kt
@@ -76,7 +76,7 @@
}
}
windowManagerTrace {
- end {
+ end("bothAppWindowsVisible") {
isVisible(primaryApp.defaultWindowName)
isVisible(secondaryApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
index 854a504..731d998 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/AppPairsTestUnpairPrimaryAndSecondaryApps.kt
@@ -85,7 +85,7 @@
}
}
windowManagerTrace {
- end {
+ end("bothAppWindowsInvisible") {
isInvisible(primaryApp.defaultWindowName)
isInvisible(secondaryApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
index c436eb2..da3450b 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsInAppPairsMode.kt
@@ -66,7 +66,7 @@
val instrumentation = InstrumentationRegistry.getInstrumentation()
val testSpec: FlickerBuilder.(Bundle) -> Unit = { configuration ->
withTestName {
- buildTestTag("testRotateAndEnterAppPairsMode", configuration)
+ buildTestTag("testRotateTwoLaunchedAppsInAppPairsMode", configuration)
}
transitions {
executeShellCommand(composePairsCommand(
@@ -88,7 +88,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("bothAppWindowsVisible") {
isVisible(primaryApp.defaultWindowName)
.isVisible(secondaryApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
index cd4f4f6..05543fd 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/apppairs/RotateTwoLaunchedAppsRotateAndEnterAppPairsMode.kt
@@ -88,7 +88,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("bothAppWindowsVisible") {
isVisible(primaryApp.defaultWindowName)
isVisible(secondaryApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
index af99543..dea5c30 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/EnterLegacySplitScreenTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
@@ -115,7 +116,7 @@
)
}
windowManagerTrace {
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
}
}
@@ -150,7 +151,7 @@
)
}
windowManagerTrace {
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
.isVisible(secondaryApp.defaultWindowName)
}
@@ -162,6 +163,7 @@
}
}
+ @FlakyTest(bugId = 173875043)
@Test
fun testNonResizeableNotDocked() {
val testTag = "testNonResizeableNotDocked"
@@ -185,7 +187,7 @@
)
}
windowManagerTrace {
- end {
+ end("appWindowIsVisible") {
isInvisible(nonResizeableApp.defaultWindowName)
}
visibleWindowsShownMoreThanOneConsecutiveEntry(
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
index cd9a3c9..701b0d0 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/ExitLegacySplitScreenTest.kt
@@ -104,7 +104,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsInvisible") {
isInvisible(splitScreenApp.defaultWindowName)
}
}
@@ -132,7 +132,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
index e79820f..6fca580 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableDismissInLegacySplitScreenTest.kt
@@ -72,7 +72,7 @@
)
}
windowManagerTrace {
- end {
+ end("nonResizeableAppWindowIsVisible") {
isVisible(nonResizeableApp.defaultWindowName)
.isInvisible(splitScreenApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
index 280af5d..deae41f 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/NonResizableLaunchInLegacySplitScreenTest.kt
@@ -72,7 +72,7 @@
)
}
windowManagerTrace {
- end {
+ end("nonResizeableAppWindowIsVisible") {
isVisible(nonResizeableApp.defaultWindowName)
.isInvisible(splitScreenApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
index fdf88df..07571c3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateOneLaunchedAppTest.kt
@@ -99,7 +99,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
}
}
@@ -131,7 +131,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
}
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
index 785ccf0..d8014d3 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/legacysplitscreen/RotateTwoLaunchedAppTest.kt
@@ -18,6 +18,7 @@
import android.platform.test.annotations.Presubmit
import android.view.Surface
+import androidx.test.filters.FlakyTest
import androidx.test.filters.RequiresDevice
import com.android.server.wm.flicker.dsl.FlickerBuilder
import com.android.server.wm.flicker.dsl.runWithFlicker
@@ -104,7 +105,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
.isVisible(secondaryApp.defaultWindowName)
}
@@ -113,6 +114,7 @@
}
}
+ @FlakyTest(bugId = 173875043)
@Test
fun testRotateAndEnterSplitScreenMode() {
val testTag = "testRotateAndEnterSplitScreenMode"
@@ -141,7 +143,7 @@
windowManagerTrace {
navBarWindowIsAlwaysVisible()
statusBarWindowIsAlwaysVisible()
- end {
+ end("appWindowIsVisible") {
isVisible(splitScreenApp.defaultWindowName)
.isVisible(secondaryApp.defaultWindowName)
}
diff --git a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
index 812353f..c21b594 100644
--- a/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
+++ b/libs/WindowManager/Shell/tests/flicker/src/com/android/wm/shell/flicker/pip/PipKeyboardTest.kt
@@ -131,7 +131,7 @@
}
assertions {
windowManagerTrace {
- end {
+ end("imeWindowAboveApp") {
isAboveWindow(IME_WINDOW_NAME, testApp.defaultWindowName)
}
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
index 5f5c30b..bf84a6e 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/TestShellExecutor.java
@@ -40,6 +40,11 @@
}
@Override
+ public void removeAllCallbacks() {
+ mRunnables.clear();
+ }
+
+ @Override
public void removeCallbacks(Runnable r) {
mRunnables.remove(r);
}
@@ -49,11 +54,6 @@
return mRunnables.contains(r);
}
- @Override
- public Looper getLooper() {
- return null;
- }
-
public void flushAll() {
for (Runnable r : mRunnables) {
r.run();
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
index 17fc057..8d5139b 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedAnimationControllerTest.java
@@ -22,6 +22,7 @@
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.SurfaceControl;
+import android.window.WindowContainerToken;
import androidx.test.filters.SmallTest;
@@ -50,6 +51,8 @@
@Mock
private SurfaceControl mMockLeash;
+ @Mock
+ private WindowContainerToken mMockToken;
@Mock
private ShellExecutor mMainExecutor;
@@ -69,7 +72,7 @@
destinationBounds.offset(0, 300);
final OneHandedAnimationController.OneHandedTransitionAnimator animator =
mOneHandedAnimationController
- .getAnimator(mMockLeash, originalBounds, destinationBounds);
+ .getAnimator(mMockToken, mMockLeash, originalBounds, destinationBounds);
assertNotNull(animator);
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
index 6cfd0c4..01162b5c 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java
@@ -24,13 +24,14 @@
import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.any;
+import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.res.Configuration;
-import android.os.Handler;
+import android.os.Binder;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.Display;
@@ -89,12 +90,14 @@
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mTestableLooper = TestableLooper.get(this);
+ Binder binder = new Binder();
+ doReturn(binder).when(mMockRealToken).asBinder();
mToken = new WindowContainerToken(mMockRealToken);
mLeash = new SurfaceControl();
mDisplay = mContext.getDisplay();
mDisplayAreaInfo = new DisplayAreaInfo(mToken, DEFAULT_DISPLAY, FEATURE_ONE_HANDED);
mDisplayAreaInfo.configuration.orientation = Configuration.ORIENTATION_PORTRAIT;
- when(mMockAnimationController.getAnimator(any(), any(), any())).thenReturn(null);
+ when(mMockAnimationController.getAnimator(any(), any(), any(), any())).thenReturn(null);
when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay);
when(mMockSurfaceTransactionHelper.translate(any(), any(), anyFloat())).thenReturn(
mMockSurfaceTransactionHelper);
@@ -121,7 +124,7 @@
public void testOnDisplayAreaAppeared() {
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
- verify(mMockAnimationController, never()).getAnimator(any(), any(), any());
+ verify(mMockAnimationController, never()).getAnimator(any(), any(), any(), any());
}
@Test
@@ -129,7 +132,7 @@
mDisplayAreaOrganizer.onDisplayAreaAppeared(mDisplayAreaInfo, mLeash);
mDisplayAreaOrganizer.onDisplayAreaVanished(mDisplayAreaInfo);
- assertThat(mDisplayAreaOrganizer.mDisplayAreaMap).isEmpty();
+ assertThat(mDisplayAreaOrganizer.mDisplayAreaTokenMap).isEmpty();
}
@Test
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
index 9219f15..bbe8891 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedTimeoutHandlerTest.java
@@ -33,6 +33,7 @@
import androidx.test.filters.SmallTest;
+import com.android.wm.shell.TestShellExecutor;
import com.android.wm.shell.common.ShellExecutor;
import org.junit.Before;
@@ -48,7 +49,7 @@
@RunWith(AndroidTestingRunner.class)
public class OneHandedTimeoutHandlerTest extends OneHandedTestCase {
private OneHandedTimeoutHandler mTimeoutHandler;
- private ShellExecutor mMainExecutor;
+ private TestShellExecutor mMainExecutor;
@Before
public void setUp() throws Exception {
@@ -104,34 +105,4 @@
mTimeoutHandler.resetTimer();
assertTrue(mTimeoutHandler.hasScheduledTimeout());
}
-
- private class TestShellExecutor implements ShellExecutor {
- private ArrayList<Runnable> mExecuted = new ArrayList<>();
- private ArrayList<Runnable> mDelayed = new ArrayList<>();
-
- @Override
- public void execute(Runnable runnable) {
- mExecuted.add(runnable);
- }
-
- @Override
- public void executeDelayed(Runnable r, long delayMillis) {
- mDelayed.add(r);
- }
-
- @Override
- public void removeCallbacks(Runnable r) {
- mDelayed.remove(r);
- }
-
- @Override
- public boolean hasCallback(Runnable r) {
- return mDelayed.contains(r);
- }
-
- @Override
- public Looper getLooper() {
- return Looper.myLooper();
- }
- }
}
diff --git a/libs/hwui/HardwareBitmapUploader.cpp b/libs/hwui/HardwareBitmapUploader.cpp
index ab9b8b5..859a555 100644
--- a/libs/hwui/HardwareBitmapUploader.cpp
+++ b/libs/hwui/HardwareBitmapUploader.cpp
@@ -362,13 +362,8 @@
return source;
} else {
SkBitmap bitmap;
- const SkImageInfo& info = source.info();
- bitmap.allocPixels(info.makeColorType(kN32_SkColorType));
-
- SkCanvas canvas(bitmap);
- canvas.drawColor(0);
- canvas.drawBitmap(source, 0.0f, 0.0f, nullptr);
-
+ bitmap.allocPixels(source.info().makeColorType(kN32_SkColorType));
+ bitmap.writePixels(source.pixmap());
return bitmap;
}
}
diff --git a/libs/hwui/Layer.cpp b/libs/hwui/Layer.cpp
index f4c633f..ca2ada9 100644
--- a/libs/hwui/Layer.cpp
+++ b/libs/hwui/Layer.cpp
@@ -127,10 +127,11 @@
const SkMatrix& totalMatrix = canvas->getTotalMatrix();
SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height());
+ SkSamplingOptions sampling;
if (getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) {
- paint.setFilterQuality(kLow_SkFilterQuality);
+ sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint);
// restore the original matrix
if (nonIdentityMatrix) {
canvas->restore();
diff --git a/libs/hwui/RecordingCanvas.cpp b/libs/hwui/RecordingCanvas.cpp
index 11a9086..96118aa 100644
--- a/libs/hwui/RecordingCanvas.cpp
+++ b/libs/hwui/RecordingCanvas.cpp
@@ -487,7 +487,9 @@
tree->getPaintFor(&paint, tree->stagingProperties());
}
- void draw(SkCanvas* canvas, const SkMatrix&) const { mRoot->draw(canvas, mBounds, paint); }
+ void draw(SkCanvas* canvas, const SkMatrix&) const {
+ mRoot->draw(canvas, mBounds, paint);
+ }
sp<VectorDrawableRoot> mRoot;
SkRect mBounds;
diff --git a/libs/hwui/SkiaCanvas.cpp b/libs/hwui/SkiaCanvas.cpp
index 1a8d9eb..8fddf71 100644
--- a/libs/hwui/SkiaCanvas.cpp
+++ b/libs/hwui/SkiaCanvas.cpp
@@ -565,7 +565,8 @@
void SkiaCanvas::drawBitmap(Bitmap& bitmap, float left, float top, const Paint* paint) {
auto image = bitmap.makeImage();
apply_looper(paint, [&](const SkPaint& p) {
- mCanvas->drawImage(image, left, top, &p);
+ auto sampling = SkSamplingOptions(p.getFilterQuality());
+ mCanvas->drawImage(image, left, top, sampling, &p);
});
}
@@ -574,7 +575,8 @@
SkAutoCanvasRestore acr(mCanvas, true);
mCanvas->concat(matrix);
apply_looper(paint, [&](const SkPaint& p) {
- mCanvas->drawImage(image, 0, 0, &p);
+ auto sampling = SkSamplingOptions(p.getFilterQuality());
+ mCanvas->drawImage(image, 0, 0, sampling, &p);
});
}
@@ -586,10 +588,17 @@
SkRect dstRect = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
apply_looper(paint, [&](const SkPaint& p) {
- mCanvas->drawImageRect(image, srcRect, dstRect, &p, SkCanvas::kFast_SrcRectConstraint);
+ auto sampling = SkSamplingOptions(p.getFilterQuality());
+ mCanvas->drawImageRect(image, srcRect, dstRect, sampling, &p,
+ SkCanvas::kFast_SrcRectConstraint);
});
}
+static SkFilterMode paintToFilter(const Paint* paint) {
+ return paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
+ : SkFilterMode::kNearest;
+}
+
void SkiaCanvas::drawBitmapMesh(Bitmap& bitmap, int meshWidth, int meshHeight,
const float* vertices, const int* colors, const Paint* paint) {
const int ptCount = (meshWidth + 1) * (meshHeight + 1);
@@ -664,18 +673,25 @@
}
#endif
+ auto image = bitmap.makeImage();
+
// cons-up a shader for the bitmap
Paint pnt;
if (paint) {
pnt = *paint;
}
- SkSamplingOptions sampling(pnt.isFilterBitmap() ? SkFilterMode::kLinear
- : SkFilterMode::kNearest,
- SkMipmapMode::kNone);
- pnt.setShader(bitmap.makeImage()->makeShader(sampling));
+ SkSamplingOptions sampling(paintToFilter(&pnt));
+ pnt.setShader(image->makeShader(sampling));
+
auto v = builder.detach();
apply_looper(&pnt, [&](const SkPaint& p) {
- mCanvas->drawVertices(v, SkBlendMode::kModulate, p);
+ SkPaint copy(p);
+ auto s = SkSamplingOptions(p.getFilterQuality());
+ if (s != sampling) {
+ // apply_looper changed the quality?
+ copy.setShader(image->makeShader(s));
+ }
+ mCanvas->drawVertices(v, SkBlendMode::kModulate, copy);
});
}
@@ -700,13 +716,11 @@
NinePatchUtils::SetLatticeFlags(&lattice, flags.get(), numFlags, chunk, colors.get());
}
- SkFilterMode filter = paint && paint->isFilterBitmap() ? SkFilterMode::kLinear
- : SkFilterMode::kNearest;
-
lattice.fBounds = nullptr;
SkRect dst = SkRect::MakeLTRB(dstLeft, dstTop, dstRight, dstBottom);
auto image = bitmap.makeImage();
apply_looper(paint, [&](const SkPaint& p) {
+ auto filter = SkSamplingOptions(p.getFilterQuality()).filter;
mCanvas->drawImageLattice(image.get(), lattice, dst, filter, &p);
});
}
diff --git a/libs/hwui/VectorDrawable.cpp b/libs/hwui/VectorDrawable.cpp
index 6030c36..4a21ad6 100644
--- a/libs/hwui/VectorDrawable.cpp
+++ b/libs/hwui/VectorDrawable.cpp
@@ -507,10 +507,12 @@
sk_sp<SkImage> cachedBitmap = getBitmapUpdateIfDirty().makeImage();
+ // HWUI always draws VD with bilinear filtering.
+ auto sampling = SkSamplingOptions(SkFilterMode::kLinear);
int scaledWidth = SkScalarCeilToInt(mProperties.getScaledWidth());
int scaledHeight = SkScalarCeilToInt(mProperties.getScaledHeight());
canvas->drawImageRect(cachedBitmap, SkRect::MakeWH(scaledWidth, scaledHeight), bounds,
- &paint, SkCanvas::kFast_SrcRectConstraint);
+ sampling, &paint, SkCanvas::kFast_SrcRectConstraint);
}
void Tree::updateBitmapCache(Bitmap& bitmap, bool useStagingData) {
diff --git a/libs/hwui/canvas/CanvasOps.h b/libs/hwui/canvas/CanvasOps.h
index cceba59..86b1ac7 100644
--- a/libs/hwui/canvas/CanvasOps.h
+++ b/libs/hwui/canvas/CanvasOps.h
@@ -351,21 +351,24 @@
const sk_sp<Bitmap>& bitmap,
float left,
float top,
+ SkFilterMode filter,
SkPaint paint
) : left(left),
top(top),
+ filter(filter),
paint(std::move(paint)),
bitmap(bitmap),
image(bitmap->makeImage()) { }
float left;
float top;
+ SkFilterMode filter;
SkPaint paint;
sk_sp<Bitmap> bitmap;
sk_sp<SkImage> image;
void draw(SkCanvas* canvas) const {
- canvas->drawImage(image, left, top, &paint);
+ canvas->drawImage(image, left, top, SkSamplingOptions(filter), &paint);
}
ASSERT_DRAWABLE()
};
@@ -377,15 +380,18 @@
const sk_sp<Bitmap>& bitmap,
SkRect src,
SkRect dst,
+ SkFilterMode filter,
SkPaint paint
) : src(src),
dst(dst),
+ filter(filter),
paint(std::move(paint)),
bitmap(bitmap),
image(bitmap->makeImage()) { }
SkRect src;
SkRect dst;
+ SkFilterMode filter;
SkPaint paint;
sk_sp<Bitmap> bitmap;
sk_sp<SkImage> image;
@@ -394,6 +400,7 @@
canvas->drawImageRect(image,
src,
dst,
+ SkSamplingOptions(filter),
&paint,
SkCanvas::kFast_SrcRectConstraint
);
diff --git a/libs/hwui/hwui/ImageDecoder.cpp b/libs/hwui/hwui/ImageDecoder.cpp
index f055c6e..ade63e5 100644
--- a/libs/hwui/hwui/ImageDecoder.cpp
+++ b/libs/hwui/hwui/ImageDecoder.cpp
@@ -437,11 +437,11 @@
if (outputMatrix.invert(&inverse)) {
SkCanvas canvas(tmp, SkCanvas::ColorBehavior::kLegacy);
canvas.setMatrix(inverse);
- SkPaint paint;
- paint.setFilterQuality(kLow_SkFilterQuality); // bilinear
SkBitmap priorFrame;
priorFrame.installPixels(outputInfo, pixels, rowBytes);
- canvas.drawBitmap(priorFrame, 0, 0, &paint);
+ priorFrame.setImmutable(); // Don't want asImage() to force a copy
+ canvas.drawImage(priorFrame.asImage(), 0, 0,
+ SkSamplingOptions(SkFilterMode::kLinear));
} else {
ALOGE("Failed to invert matrix!");
}
@@ -458,11 +458,11 @@
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
- paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
SkCanvas canvas(scaledBm, SkCanvas::ColorBehavior::kLegacy);
canvas.setMatrix(outputMatrix);
- canvas.drawBitmap(tmp, 0.0f, 0.0f, &paint);
+ tmp.setImmutable(); // Don't want asImage() to force copy
+ canvas.drawImage(tmp.asImage(), 0, 0, SkSamplingOptions(SkFilterMode::kLinear), &paint);
}
return result;
diff --git a/libs/hwui/jni/BitmapFactory.cpp b/libs/hwui/jni/BitmapFactory.cpp
index cf02051..4e9daa4 100644
--- a/libs/hwui/jni/BitmapFactory.cpp
+++ b/libs/hwui/jni/BitmapFactory.cpp
@@ -457,11 +457,12 @@
// outputBitmap. Otherwise we would blend by default, which is not
// what we want.
paint.setBlendMode(SkBlendMode::kSrc);
- paint.setFilterQuality(kLow_SkFilterQuality); // bilinear filtering
SkCanvas canvas(outputBitmap, SkCanvas::ColorBehavior::kLegacy);
canvas.scale(scaleX, scaleY);
- canvas.drawBitmap(decodingBitmap, 0.0f, 0.0f, &paint);
+ decodingBitmap.setImmutable(); // so .asImage() doesn't make a copy
+ canvas.drawImage(decodingBitmap.asImage(), 0.0f, 0.0f,
+ SkSamplingOptions(SkFilterMode::kLinear), &paint);
} else {
outputBitmap.swap(decodingBitmap);
}
diff --git a/libs/hwui/pipeline/skia/LayerDrawable.cpp b/libs/hwui/pipeline/skia/LayerDrawable.cpp
index f95f347..34df5dd 100644
--- a/libs/hwui/pipeline/skia/LayerDrawable.cpp
+++ b/libs/hwui/pipeline/skia/LayerDrawable.cpp
@@ -141,18 +141,20 @@
// then use nearest neighbor, otherwise use bilerp sampling.
// Skia TextureOp has the above logic build-in, but not NonAAFillRectOp. TextureOp works
// only for SrcOver blending and without color filter (readback uses Src blending).
+ SkSamplingOptions sampling(SkFilterMode::kNearest);
if (layer->getForceFilter() ||
shouldFilterRect(totalMatrix, skiaSrcRect, skiaDestRect)) {
- paint.setFilterQuality(kLow_SkFilterQuality);
+ sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, &paint,
+ canvas->drawImageRect(layerImage.get(), skiaSrcRect, skiaDestRect, sampling, &paint,
SkCanvas::kFast_SrcRectConstraint);
} else {
SkRect imageRect = SkRect::MakeIWH(layerImage->width(), layerImage->height());
+ SkSamplingOptions sampling(SkFilterMode::kNearest);
if (layer->getForceFilter() || shouldFilterRect(totalMatrix, imageRect, imageRect)) {
- paint.setFilterQuality(kLow_SkFilterQuality);
+ sampling = SkSamplingOptions(SkFilterMode::kLinear);
}
- canvas->drawImage(layerImage.get(), 0, 0, &paint);
+ canvas->drawImage(layerImage.get(), 0, 0, sampling, &paint);
}
// restore the original matrix
if (nonIdentityMatrix) {
diff --git a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
index 070a765..75815bb6 100644
--- a/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
+++ b/libs/hwui/pipeline/skia/RenderNodeDrawable.cpp
@@ -169,7 +169,6 @@
static bool layerNeedsPaint(const LayerProperties& properties, float alphaMultiplier,
SkPaint* paint) {
- paint->setFilterQuality(kLow_SkFilterQuality);
if (alphaMultiplier < 1.0f || properties.alpha() < 255 ||
properties.xferMode() != SkBlendMode::kSrcOver || properties.getColorFilter() != nullptr ||
properties.getImageFilter() != nullptr) {
@@ -226,6 +225,7 @@
SkASSERT(properties.effectiveLayerType() == LayerType::RenderLayer);
SkPaint paint;
layerNeedsPaint(layerProperties, alphaMultiplier, &paint);
+ SkSamplingOptions sampling(SkFilterMode::kLinear);
// surfaces for layers are created on LAYER_SIZE boundaries (which are >= layer size) so
// we need to restrict the portion of the surface drawn to the size of the renderNode.
@@ -239,7 +239,7 @@
"SurfaceID|%" PRId64, renderNode->uniqueId()).c_str(), nullptr);
}
canvas->drawImageRect(renderNode->getLayerSurface()->makeImageSnapshot(), bounds,
- bounds, &paint);
+ bounds, sampling, &paint, SkCanvas::kStrict_SrcRectConstraint);
if (!renderNode->getSkiaLayer()->hasRenderedSinceRepaint) {
renderNode->getSkiaLayer()->hasRenderedSinceRepaint = true;
diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
index 80eddaf..6456e36 100644
--- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp
+++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp
@@ -656,7 +656,7 @@
SkPaint paint;
const SkColor* colors = kOverdrawColors[static_cast<int>(Properties::overdrawColorSet)];
paint.setColorFilter(SkOverdrawColorFilter::MakeWithSkColors(colors));
- surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, &paint);
+ surface->getCanvas()->drawImage(counts.get(), 0.0f, 0.0f, SkSamplingOptions(), &paint);
}
} /* namespace skiapipeline */
diff --git a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
index bc8ce42..bae11f7 100644
--- a/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
+++ b/libs/hwui/pipeline/skia/VkInteropFunctorDrawable.cpp
@@ -182,7 +182,7 @@
auto functorImage = SkImage::MakeFromAHardwareBuffer(mFrameBuffer.get(), kPremul_SkAlphaType,
canvas->imageInfo().refColorSpace(),
kBottomLeft_GrSurfaceOrigin);
- canvas->drawImage(functorImage, 0, 0, &paint);
+ canvas->drawImage(functorImage, 0, 0, SkSamplingOptions(), &paint);
canvas->restore();
}
diff --git a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
index 1d17a02..716d397 100644
--- a/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
+++ b/libs/hwui/tests/common/scenes/ReadbackFromHardwareBitmap.cpp
@@ -51,7 +51,7 @@
hardwareBitmap->height(), &canvasBitmap));
SkCanvas skCanvas(canvasBitmap);
- skCanvas.drawBitmap(readback, 0, 0);
+ skCanvas.drawImage(readback.asImage(), 0, 0);
canvas.drawBitmap(*heapBitmap, 0, 0, nullptr);
canvas.drawBitmap(*hardwareBitmap, 0, 500, nullptr);
diff --git a/libs/hwui/tests/microbench/CanvasOpBench.cpp b/libs/hwui/tests/microbench/CanvasOpBench.cpp
index ef5749e..e7ba471 100644
--- a/libs/hwui/tests/microbench/CanvasOpBench.cpp
+++ b/libs/hwui/tests/microbench/CanvasOpBench.cpp
@@ -85,6 +85,7 @@
iconBitmap,
0,
0,
+ SkFilterMode::kNearest,
SkPaint{}
});
canvas.restore();
diff --git a/libs/hwui/tests/unit/CanvasOpTests.cpp b/libs/hwui/tests/unit/CanvasOpTests.cpp
index c9e8d80..54970df 100644
--- a/libs/hwui/tests/unit/CanvasOpTests.cpp
+++ b/libs/hwui/tests/unit/CanvasOpTests.cpp
@@ -474,6 +474,7 @@
bitmap,
7,
19,
+ SkFilterMode::kNearest,
SkPaint{}
}
);
@@ -496,7 +497,7 @@
buffer.push<Op::DrawImageRect> ({
bitmap, SkRect::MakeWH(100, 100),
SkRect::MakeLTRB(120, 110, 220, 210),
- SkPaint{}
+ SkFilterMode::kNearest, SkPaint{}
}
);
diff --git a/packages/SettingsLib/res/values-af/strings.xml b/packages/SettingsLib/res/values-af/strings.xml
index 6f4f72a..3837743 100644
--- a/packages/SettingsLib/res/values-af/strings.xml
+++ b/packages/SettingsLib/res/values-af/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sal nie outomaties koppel nie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang nie"</string>
<string name="saved_network" msgid="7143698034077223645">"Gestoor deur <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Outomaties deur %1$s gekoppel"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Outomaties deur netwerkgraderingverskaffer gekoppel"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Gekoppel via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-am/strings.xml b/packages/SettingsLib/res/values-am/strings.xml
index 3f5df34..4ce01d6 100644
--- a/packages/SettingsLib/res/values-am/strings.xml
+++ b/packages/SettingsLib/res/values-am/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"በራስ-ሰር አይገናኝም"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ምንም የበይነመረብ መዳረሻ ያለም"</string>
<string name="saved_network" msgid="7143698034077223645">"የተቀመጠው በ<xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"በ%1$s በኩል በራስ-ሰር ተገናኝቷል"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"በአውታረ መረብ ደረጃ ሰጪ አቅራቢ በኩል በራስ-ሰር ተገናኝቷል"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"በ%1$s በኩል መገናኘት"</string>
diff --git a/packages/SettingsLib/res/values-ar/strings.xml b/packages/SettingsLib/res/values-ar/strings.xml
index 0351d94..2580d0f 100644
--- a/packages/SettingsLib/res/values-ar/strings.xml
+++ b/packages/SettingsLib/res/values-ar/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"لن يتم الاتصال تلقائيًا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"لا يتوفّر اتصال بالإنترنت"</string>
<string name="saved_network" msgid="7143698034077223645">"تم الحفظ بواسطة <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"تم الاتصال تلقائيًا عبر %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"تم الاتصال تلقائيًا عبر مقدم خدمة تقييم الشبكة"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"تم الاتصال عبر %1$s"</string>
diff --git a/packages/SettingsLib/res/values-as/strings.xml b/packages/SettingsLib/res/values-as/strings.xml
index 0f7db8f..b61ff50 100644
--- a/packages/SettingsLib/res/values-as/strings.xml
+++ b/packages/SettingsLib/res/values-as/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"স্বয়ংক্ৰিয়ভাৱে সংযোগ নহ’ব"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইণ্টাৰনেট সংযোগ নাই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>এ ছেভ কৰিছে"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s মাধ্যমেদি স্বয়ংক্ৰিয়ভাৱে সংযোগ কৰা হৈছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটৱৰ্ক ৰেটিং প্ৰদানকাৰীৰ জৰিয়তে স্বয়ং সংয়োগ কৰা হ’ল"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ৰ মাধ্যমেদি সংযোগ কৰা হৈছে"</string>
diff --git a/packages/SettingsLib/res/values-az/strings.xml b/packages/SettingsLib/res/values-az/strings.xml
index 0855d17e..d063776 100644
--- a/packages/SettingsLib/res/values-az/strings.xml
+++ b/packages/SettingsLib/res/values-az/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik qoşulmayacaq"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet girişi yoxdur"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tərəfindən saxlandı"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzərindən avtomatik qoşuldu"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Avtomatik olaraq şəbəkə reytinq provayderi ilə qoşuludur"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s vasitəsilə qoşuludur"</string>
diff --git a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
index 3386fe86..2976bb5 100644
--- a/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
+++ b/packages/SettingsLib/res/values-b+sr+Latn/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automatsko povezivanje nije uspelo"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvao/la je <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano preko %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano preko dobavljača ocene mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Veza je uspostavljena preko pristupne tačke %1$s"</string>
diff --git a/packages/SettingsLib/res/values-be/strings.xml b/packages/SettingsLib/res/values-be/strings.xml
index 6ac2172..7af9e93 100644
--- a/packages/SettingsLib/res/values-be/strings.xml
+++ b/packages/SettingsLib/res/values-be/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не будзе аўтаматычна падключацца"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма доступу да інтэрнэту"</string>
<string name="saved_network" msgid="7143698034077223645">"Захавана праз: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аўтаматычна падключана праз %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аўтаматычна падключана праз пастаўшчыка паслугі ацэнкі сеткі"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Падключана праз %1$s"</string>
diff --git a/packages/SettingsLib/res/values-bg/strings.xml b/packages/SettingsLib/res/values-bg/strings.xml
index 19ed5bd..77b6493 100644
--- a/packages/SettingsLib/res/values-bg/strings.xml
+++ b/packages/SettingsLib/res/values-bg/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Няма да се свърже автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Няма достъп до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Запазено от <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично е установена връзка чрез %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично е установена връзка чрез доставчик на услуги за оценяване на мрежите"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Установена е връзка през „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-bn/strings.xml b/packages/SettingsLib/res/values-bn/strings.xml
index b0e9342..819625b 100644
--- a/packages/SettingsLib/res/values-bn/strings.xml
+++ b/packages/SettingsLib/res/values-bn/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"অটোমেটিক কানেক্ট করবে না"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ইন্টারনেট অ্যাক্সেস নেই"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> দ্বারা সেভ করা"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"স্বয়ংক্রিয়ভাবে %1$s এর মাধ্যমে কানেক্ট হয়েছে"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"নেটওয়ার্কের রেটিং প্রদানকারীর মাধ্যমে অটোমেটিক কানেক্ট"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s মাধ্যমে কানেক্ট হয়েছে"</string>
diff --git a/packages/SettingsLib/res/values-bs/strings.xml b/packages/SettingsLib/res/values-bs/strings.xml
index 9377624..193ac60 100644
--- a/packages/SettingsLib/res/values-bs/strings.xml
+++ b/packages/SettingsLib/res/values-bs/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se automatski povezati"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Sačuvano: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezano koristeći %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezano putem ocjenjivača mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Povezani preko %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ca/strings.xml b/packages/SettingsLib/res/values-ca/strings.xml
index a2a7770..fef9bfb 100644
--- a/packages/SettingsLib/res/values-ca/strings.xml
+++ b/packages/SettingsLib/res/values-ca/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No es connectarà automàticament"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hi ha accés a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Desada per <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connectada automàticament a través de: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connectada automàticament a través d\'un proveïdor de valoració de xarxes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connectada mitjançant %1$s"</string>
diff --git a/packages/SettingsLib/res/values-cs/strings.xml b/packages/SettingsLib/res/values-cs/strings.xml
index 4db4d5c..281a788 100644
--- a/packages/SettingsLib/res/values-cs/strings.xml
+++ b/packages/SettingsLib/res/values-cs/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Připojení nebude automaticky navázáno"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nebyl zjištěn žádný přístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uloženo uživatelem <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky připojeno přes poskytovatele %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky připojeno přes poskytovatele hodnocení sítí"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Připojeno prostřednictvím %1$s"</string>
diff --git a/packages/SettingsLib/res/values-da/strings.xml b/packages/SettingsLib/res/values-da/strings.xml
index b76afa5c..69cc8d8 100644
--- a/packages/SettingsLib/res/values-da/strings.xml
+++ b/packages/SettingsLib/res/values-da/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Der oprettes ikke automatisk forbindelse"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetadgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Gemt af <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilsluttet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk forbundet via udbyder af netværksvurdering"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tilsluttet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-de/strings.xml b/packages/SettingsLib/res/values-de/strings.xml
index ba760dd..737ea16 100644
--- a/packages/SettingsLib/res/values-de/strings.xml
+++ b/packages/SettingsLib/res/values-de/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kein automatischer Verbindungsaufbau"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Kein Internetzugriff"</string>
<string name="saved_network" msgid="7143698034077223645">"Gespeichert von <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch über %1$s verbunden"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch über Anbieter von Netzwerkbewertungen verbunden"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Über %1$s verbunden"</string>
diff --git a/packages/SettingsLib/res/values-el/strings.xml b/packages/SettingsLib/res/values-el/strings.xml
index c608a62..8e1d5e3 100644
--- a/packages/SettingsLib/res/values-el/strings.xml
+++ b/packages/SettingsLib/res/values-el/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Δεν θα συνδεθεί αυτόματα"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Δεν υπάρχει πρόσβαση στο διαδίκτυο"</string>
<string name="saved_network" msgid="7143698034077223645">"Αποθηκεύτηκε από <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Συνδέθηκε αυτόματα μέσω %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Συνδέθηκε αυτόματα μέσω παρόχου αξιολόγησης δικτύου"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Συνδέθηκε μέσω %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rAU/strings.xml b/packages/SettingsLib/res/values-en-rAU/strings.xml
index e67c3d1..b98c4b8 100644
--- a/packages/SettingsLib/res/values-en-rAU/strings.xml
+++ b/packages/SettingsLib/res/values-en-rAU/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rCA/strings.xml b/packages/SettingsLib/res/values-en-rCA/strings.xml
index b0830fc..aa0d3f1 100644
--- a/packages/SettingsLib/res/values-en-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-en-rCA/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rGB/strings.xml b/packages/SettingsLib/res/values-en-rGB/strings.xml
index e67c3d1..b98c4b8 100644
--- a/packages/SettingsLib/res/values-en-rGB/strings.xml
+++ b/packages/SettingsLib/res/values-en-rGB/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rIN/strings.xml b/packages/SettingsLib/res/values-en-rIN/strings.xml
index e67c3d1..b98c4b8 100644
--- a/packages/SettingsLib/res/values-en-rIN/strings.xml
+++ b/packages/SettingsLib/res/values-en-rIN/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No Internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-en-rXC/strings.xml b/packages/SettingsLib/res/values-en-rXC/strings.xml
index 4c0af75..c01f3a0 100644
--- a/packages/SettingsLib/res/values-en-rXC/strings.xml
+++ b/packages/SettingsLib/res/values-en-rXC/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Won\'t automatically connect"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No internet access"</string>
<string name="saved_network" msgid="7143698034077223645">"Saved by <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Connected to metered network"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatically connected via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatically connected via network rating provider"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connected via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-es-rUS/strings.xml b/packages/SettingsLib/res/values-es-rUS/strings.xml
index 9ae7781..f79072f 100644
--- a/packages/SettingsLib/res/values-es-rUS/strings.xml
+++ b/packages/SettingsLib/res/values-es-rUS/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se conectará automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No hay acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conexión automática mediante %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente mediante proveedor de calificación de red"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conexión a través de %1$s"</string>
@@ -116,8 +118,8 @@
<string name="bluetooth_pairing_accept_all_caps" msgid="2734383073450506220">"SINCRONIZAR"</string>
<string name="bluetooth_pairing_decline" msgid="6483118841204885890">"Cancelar"</string>
<string name="bluetooth_pairing_will_share_phonebook" msgid="3064334458659165176">"La sincronización te permite acceder a los contactos y al historial de llamadas cuando el dispositivo está conectado."</string>
- <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
- <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"No se pudo sincronizar con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que el PIN o la clave de acceso son incorrectos."</string>
+ <string name="bluetooth_pairing_error_message" msgid="6626399020672335565">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
+ <string name="bluetooth_pairing_pin_error_message" msgid="264422127613704940">"No se pudo vincular con <xliff:g id="DEVICE_NAME">%1$s</xliff:g> debido a que el PIN o la clave de acceso son incorrectos."</string>
<string name="bluetooth_pairing_device_down_error_message" msgid="2554424863101358857">"No se puede establecer la comunicación con <xliff:g id="DEVICE_NAME">%1$s</xliff:g>."</string>
<string name="bluetooth_pairing_rejected_error_message" msgid="5943444352777314442">"Vínculo rechazado por <xliff:g id="DEVICE_NAME">%1$s</xliff:g>"</string>
<string name="bluetooth_talkback_computer" msgid="3736623135703893773">"Computadora"</string>
diff --git a/packages/SettingsLib/res/values-es/strings.xml b/packages/SettingsLib/res/values-es/strings.xml
index 203726a..f99a3f1 100644
--- a/packages/SettingsLib/res/values-es/strings.xml
+++ b/packages/SettingsLib/res/values-es/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"No se establecerá conexión automáticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"No se ha detectado ningún acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectada automáticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automáticamente a través de un proveedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-et/strings.xml b/packages/SettingsLib/res/values-et/strings.xml
index 379ed6c..fa2aa46 100644
--- a/packages/SettingsLib/res/values-et/strings.xml
+++ b/packages/SettingsLib/res/values-et/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Automaatselt ei ühendata"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Juurdepääs Internetile puudub"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvestas: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ühendus loodi automaatselt teenusega %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ühendus loodi automaatselt võrgukvaliteedi hinnangute pakkuja kaudu"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Ühendatud üksuse %1$s kaudu"</string>
diff --git a/packages/SettingsLib/res/values-eu/strings.xml b/packages/SettingsLib/res/values-eu/strings.xml
index 2582854..2c4a8ee 100644
--- a/packages/SettingsLib/res/values-eu/strings.xml
+++ b/packages/SettingsLib/res/values-eu/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ez da konektatuko automatikoki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ezin da konektatu Internetera"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> aplikazioak gorde du"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s bidez automatikoki konektatuta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikoki konektatuta sare-balorazioen hornitzailearen bidez"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s bidez konektatuta"</string>
diff --git a/packages/SettingsLib/res/values-fa/strings.xml b/packages/SettingsLib/res/values-fa/strings.xml
index cb598e5..1c6ca76 100644
--- a/packages/SettingsLib/res/values-fa/strings.xml
+++ b/packages/SettingsLib/res/values-fa/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"اتصال بهصورت خودکار انجام نمیشود"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"دسترسی به اینترنت ندارد"</string>
<string name="saved_network" msgid="7143698034077223645">"ذخیرهشده توسط <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"اتصال خودکار ازطریق %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"اتصال خودکار ازطریق ارائهدهنده رتبهبندی شبکه"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"متصل از طریق %1$s"</string>
@@ -263,8 +265,8 @@
<string name="bluetooth_select_map_version_dialog_title" msgid="7085934373987428460">"انتخاب نسخه MAP بلوتوث"</string>
<string name="bluetooth_select_a2dp_codec_type" msgid="952001408455456494">"کدک بلوتوث صوتی"</string>
<string name="bluetooth_select_a2dp_codec_type_dialog_title" msgid="7510542404227225545">"راهاندازی کدک صوتی بلوتوثی\nانتخاب"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"سرعت نمونه بلوتوث صوتی"</string>
- <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: سرعت نمونه"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate" msgid="1638623076480928191">"بسامد نمونه صوتی بلوتوث"</string>
+ <string name="bluetooth_select_a2dp_codec_sample_rate_dialog_title" msgid="5876305103137067798">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: بسامد نمونه"</string>
<string name="bluetooth_select_a2dp_codec_type_help_info" msgid="8647200416514412338">"«خاکستری» به این معناست که تلفن یا هدست از آن پشتیبانی نمیکند"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample" msgid="6253965294594390806">"بیتهای بلوتوث صوتی در هر نمونه"</string>
<string name="bluetooth_select_a2dp_codec_bits_per_sample_dialog_title" msgid="4898693684282596143">"راهاندازی کدک صوتی بلوتوثی\nانتخاب: تعداد بیت در نمونه"</string>
diff --git a/packages/SettingsLib/res/values-fi/strings.xml b/packages/SettingsLib/res/values-fi/strings.xml
index 31dfe18..04c9130 100644
--- a/packages/SettingsLib/res/values-fi/strings.xml
+++ b/packages/SettingsLib/res/values-fi/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Yhteyttä ei muodosteta automaattisesti"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ei internetyhteyttä"</string>
<string name="saved_network" msgid="7143698034077223645">"Tallentaja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaattinen yhteys muodostettu palvelun %1$s kautta"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Yhdistetty automaattisesti verkon arviointipalvelun kautta"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Yhdistetty seuraavan kautta: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr-rCA/strings.xml b/packages/SettingsLib/res/values-fr-rCA/strings.xml
index a8476dd..aa0cd2a 100644
--- a/packages/SettingsLib/res/values-fr-rCA/strings.xml
+++ b/packages/SettingsLib/res/values-fr-rCA/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistrés par <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiquement connecté par %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement par le fournisseur d\'avis sur le réseau"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté par %1$s"</string>
diff --git a/packages/SettingsLib/res/values-fr/strings.xml b/packages/SettingsLib/res/values-fr/strings.xml
index a79ed0c..dbdc160 100644
--- a/packages/SettingsLib/res/values-fr/strings.xml
+++ b/packages/SettingsLib/res/values-fr/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Reconnexion automatique impossible"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Aucun accès à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Enregistré lors de : <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Connecté automatiquement via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Connecté automatiquement via un fournisseur d\'évaluation de l\'état du réseau"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Connecté via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gl/strings.xml b/packages/SettingsLib/res/values-gl/strings.xml
index 97662a6..90c1303 100644
--- a/packages/SettingsLib/res/values-gl/strings.xml
+++ b/packages/SettingsLib/res/values-gl/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sen acceso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Gardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectouse automaticamente a través de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectada automaticamente a través dun provedor de valoración de redes"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado a través de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-gu/strings.xml b/packages/SettingsLib/res/values-gu/strings.xml
index 46bd71b..4caeda2 100644
--- a/packages/SettingsLib/res/values-gu/strings.xml
+++ b/packages/SettingsLib/res/values-gu/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ઑટોમૅટિક રીતે કનેક્ટ કરશે નહીં"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"કોઈ ઇન્ટરનેટ ઍક્સેસ નથી"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> દ્વારા સચવાયું"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s દ્વારા સ્વત: કનેક્ટ થયેલ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"નેટવર્ક રેટિંગ પ્રદાતા દ્વારા ઑટોમૅટિક રીતે કનેક્ટ થયું"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s દ્વારા કનેક્ટ થયેલ"</string>
diff --git a/packages/SettingsLib/res/values-hi/strings.xml b/packages/SettingsLib/res/values-hi/strings.xml
index 9cae311..468808b 100644
--- a/packages/SettingsLib/res/values-hi/strings.xml
+++ b/packages/SettingsLib/res/values-hi/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"अपने आप कनेक्ट नहीं होगा"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट नहीं है"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> के द्वारा सहेजा गया"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s के ज़रिए ऑटोमैटिक रूप से कनेक्ट है"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग कंपनी के ज़रिए अपने आप कनेक्ट है"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s के द्वारा उपलब्ध"</string>
diff --git a/packages/SettingsLib/res/values-hr/strings.xml b/packages/SettingsLib/res/values-hr/strings.xml
index 83bb2d1..932c256 100644
--- a/packages/SettingsLib/res/values-hr/strings.xml
+++ b/packages/SettingsLib/res/values-hr/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Neće se povezati automatski"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nema pristupa internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Spremila aplik. <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatski povezan putem %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatski povezan putem ocjenjivača mreže"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Povezano putem %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hu/strings.xml b/packages/SettingsLib/res/values-hu/strings.xml
index 9f184c7..fbaffac 100644
--- a/packages/SettingsLib/res/values-hu/strings.xml
+++ b/packages/SettingsLib/res/values-hu/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nem csatlakozik automatikusan"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nincs internet-hozzáférés"</string>
<string name="saved_network" msgid="7143698034077223645">"Mentette: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatikusan csatlakozott a következőn keresztül: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatikusan csatlakozott a hálózatértékelés szolgáltatóján keresztül"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Csatlakozva a következőn keresztül: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-hy/strings.xml b/packages/SettingsLib/res/values-hy/strings.xml
index cd6cbf3..224d641 100644
--- a/packages/SettingsLib/res/values-hy/strings.xml
+++ b/packages/SettingsLib/res/values-hy/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Չի միանա ավտոմատ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ինտերնետ կապ չկա"</string>
<string name="saved_network" msgid="7143698034077223645">"Ով է պահել՝ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ավտոմատ կերպով կապակցվել է %1$s-ի միջոցով"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ավտոմատ միացել է ցանցերի վարկանիշի մատակարարի միջոցով"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Միացված է %1$s-ի միջոցով"</string>
diff --git a/packages/SettingsLib/res/values-in/strings.xml b/packages/SettingsLib/res/values-in/strings.xml
index 3b80918..8cf13cd 100644
--- a/packages/SettingsLib/res/values-in/strings.xml
+++ b/packages/SettingsLib/res/values-in/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan tersambung otomatis"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tidak ada akses internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Disimpan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Tersambung otomatis melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Otomatis tersambung melalui penyedia rating jaringan"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Terhubung melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-is/strings.xml b/packages/SettingsLib/res/values-is/strings.xml
index ce9e665..aa8893e 100644
--- a/packages/SettingsLib/res/values-is/strings.xml
+++ b/packages/SettingsLib/res/values-is/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Mun ekki tengjast sjálfkrafa"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Enginn netaðgangur"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> vistaði"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Sjálfkrafa tengt um %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Sjálfkrafa tengt um netgæðaveitu"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tengt í gegnum %1$s"</string>
@@ -226,12 +228,12 @@
<string name="adb_pairing_device_dialog_pairing_code_label" msgid="3639239786669722731">"Wi-Fi pörunarkóði"</string>
<string name="adb_pairing_device_dialog_failed_title" msgid="3426758947882091735">"Pörun mistókst"</string>
<string name="adb_pairing_device_dialog_failed_msg" msgid="6611097519661997148">"Gakktu úr skugga um að tækið sé tengt sama neti."</string>
- <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Tengja tæki með Wi-Fi með því að skanna QR-kóða"</string>
+ <string name="adb_wireless_qrcode_summary" msgid="8051414549011801917">"Para tæki gegnum Wi-Fi með því að skanna QR-kóða"</string>
<string name="adb_wireless_verifying_qrcode_text" msgid="6123192424916029207">"Parar tæki…"</string>
<string name="adb_qrcode_pairing_device_failed_msg" msgid="6936292092592914132">"Ekki tókst að para við tækið. Annað hvort var QR-kóðinn rangur eða tækið ekki tengt sama neti."</string>
<string name="adb_wireless_ip_addr_preference_title" msgid="8335132107715311730">"IP-tala og gátt"</string>
<string name="adb_wireless_qrcode_pairing_title" msgid="1906409667944674707">"Skanna QR-kóða"</string>
- <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Tengja tæki með Wi-Fi með því að skanna QR-kóða"</string>
+ <string name="adb_wireless_qrcode_pairing_description" msgid="6014121407143607851">"Para tæki gegnum Wi-Fi með því að skanna QR-kóða"</string>
<string name="adb_wireless_no_network_msg" msgid="2365795244718494658">"Tengstu Wi-Fi neti"</string>
<string name="keywords_adb_wireless" msgid="6507505581882171240">"adb, villuleit, dev"</string>
<string name="bugreport_in_power" msgid="8664089072534638709">"Flýtileið í villutilkynningu"</string>
diff --git a/packages/SettingsLib/res/values-it/strings.xml b/packages/SettingsLib/res/values-it/strings.xml
index 93b24a0..0199f54 100644
--- a/packages/SettingsLib/res/values-it/strings.xml
+++ b/packages/SettingsLib/res/values-it/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Non verrà eseguita la connessione automatica"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nessun accesso a Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvata da <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Collegato automaticamente tramite %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Collegato automaticamente tramite fornitore di servizi di valutazione rete"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Collegato tramite %1$s"</string>
diff --git a/packages/SettingsLib/res/values-iw/strings.xml b/packages/SettingsLib/res/values-iw/strings.xml
index ab67809..a50a22d 100644
--- a/packages/SettingsLib/res/values-iw/strings.xml
+++ b/packages/SettingsLib/res/values-iw/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"לא יתבצע חיבור באופן אוטומטי"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"אין גישה לאינטרנט"</string>
<string name="saved_network" msgid="7143698034077223645">"נשמר על ידי <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"מחובר אוטומטית דרך %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"מחובר אוטומטית דרך ספק של דירוג רשת"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"מחובר דרך %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ja/strings.xml b/packages/SettingsLib/res/values-ja/strings.xml
index 711a22f..a1d1b70 100644
--- a/packages/SettingsLib/res/values-ja/strings.xml
+++ b/packages/SettingsLib/res/values-ja/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"自動的に接続されません"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"インターネット接続なし"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>で保存"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s 経由で自動的に接続しています"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ネットワーク評価プロバイダ経由で自動的に接続しています"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s経由で接続"</string>
diff --git a/packages/SettingsLib/res/values-ka/strings.xml b/packages/SettingsLib/res/values-ka/strings.xml
index 0813305..77fd4b1 100644
--- a/packages/SettingsLib/res/values-ka/strings.xml
+++ b/packages/SettingsLib/res/values-ka/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ავტომატურად დაკავშირება ვერ მოხერხდება"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ინტერნეტ-კავშირი არ არის"</string>
<string name="saved_network" msgid="7143698034077223645">"შენახული <xliff:g id="NAME">%1$s</xliff:g>-ის მიერ"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ავტომატურად დაკავშირდა %1$s-ის მეშვეობით"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ავტომატურად დაკავშირდა ქსელის ხარისხის შეფასების პროვაიდერის მეშვეობით"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-ით დაკავშირებული"</string>
diff --git a/packages/SettingsLib/res/values-kk/strings.xml b/packages/SettingsLib/res/values-kk/strings.xml
index 16ee9c0..eb5cd54 100644
--- a/packages/SettingsLib/res/values-kk/strings.xml
+++ b/packages/SettingsLib/res/values-kk/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматты қосылмайды"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетпен байланыс жоқ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> сақтаған"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s арқылы автоматты қосылды"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Желі рейтингі провайдері арқылы автоматты түрде қосылған"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s арқылы қосылған"</string>
diff --git a/packages/SettingsLib/res/values-km/strings.xml b/packages/SettingsLib/res/values-km/strings.xml
index 5b47381..38abb80 100644
--- a/packages/SettingsLib/res/values-km/strings.xml
+++ b/packages/SettingsLib/res/values-km/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"នឹងមិនភ្ជាប់ដោយស្វ័យប្រវត្តិទេ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"មិនមានការតភ្ជាប់អ៊ីនធឺណិតទេ"</string>
<string name="saved_network" msgid="7143698034077223645">"បានរក្សាទុកដោយ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈ %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"បានភ្ជាប់ដោយស្វ័យប្រវត្តិតាមរយៈក្រុមហ៊ុនផ្តល់ការវាយតម្លៃលើបណ្តាញ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"បានភ្ជាប់តាមរយៈ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-kn/strings.xml b/packages/SettingsLib/res/values-kn/strings.xml
index fd63dcb..560fba15 100644
--- a/packages/SettingsLib/res/values-kn/strings.xml
+++ b/packages/SettingsLib/res/values-kn/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ಯಾವುದೇ ಇಂಟರ್ನೆಟ್ ಪ್ರವೇಶವಿಲ್ಲ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ನಿಂದ ಉಳಿಸಲಾಗಿದೆ"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ನೆಟ್ವರ್ಕ್ ರೇಟಿಂಗ್ ಒದಗಿಸುವವರ ಮೂಲಕ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಸಂಪರ್ಕಿಸಲಾಗಿದೆ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ಮೂಲಕ ಸಂಪರ್ಕಗೊಂಡಿದೆ"</string>
diff --git a/packages/SettingsLib/res/values-ko/strings.xml b/packages/SettingsLib/res/values-ko/strings.xml
index a9c3f31..f43ce16 100644
--- a/packages/SettingsLib/res/values-ko/strings.xml
+++ b/packages/SettingsLib/res/values-ko/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"자동으로 연결되지 않습니다."</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"인터넷에 연결되어 있지 않음"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g>(으)로 저장됨"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s을(를) 통해 자동으로 연결됨"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"네트워크 평가 제공업체를 통해 자동으로 연결됨"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s을(를) 통해 연결됨"</string>
diff --git a/packages/SettingsLib/res/values-ky/strings.xml b/packages/SettingsLib/res/values-ky/strings.xml
index bc3656f..35b1ecac 100644
--- a/packages/SettingsLib/res/values-ky/strings.xml
+++ b/packages/SettingsLib/res/values-ky/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматтык түрдө туташпайт"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернетке туташпай турат"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> тарабынан сакталды"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Тармактар рейтингинин булагы аркылуу автоматтык түрдө туташты"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s аркылуу жеткиликтүү"</string>
diff --git a/packages/SettingsLib/res/values-lo/strings.xml b/packages/SettingsLib/res/values-lo/strings.xml
index 8408c93..f60fe7f 100644
--- a/packages/SettingsLib/res/values-lo/strings.xml
+++ b/packages/SettingsLib/res/values-lo/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ຈະບໍ່ເຊື່ອມຕໍ່ອັດຕະໂນມັດ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ບໍ່ມີການເຊື່ອມຕໍ່ອິນເຕີເນັດ"</string>
<string name="saved_network" msgid="7143698034077223645">"ບັນທຶກໂດຍ <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"ເຊື່ອມຕໍ່ຜ່ານທາງ %1$s ໂດຍອັດຕະໂນມັດ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ເຊື່ອມຕໍ່ກັບອັດຕະໂນມັດແລ້ວຜ່ານຜູ້ໃຫ້ບໍລິການຄະແນນເຄືອຂ່າຍ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"ເຊື່ອມຕໍ່ຜ່ານ %1$s ແລ້ວ"</string>
diff --git a/packages/SettingsLib/res/values-lt/strings.xml b/packages/SettingsLib/res/values-lt/strings.xml
index 1dcfcf7..e66e3c5 100644
--- a/packages/SettingsLib/res/values-lt/strings.xml
+++ b/packages/SettingsLib/res/values-lt/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nebus automatiškai prisijungiama"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nėra interneto ryšio"</string>
<string name="saved_network" msgid="7143698034077223645">"Išsaugojo <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiškai prisijungta naudojant „%1$s“"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiškai prisijungta naudojant tinklo įvertinimo paslaugos teikėjo paslaugomis"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Prisijungta naudojant „%1$s“"</string>
diff --git a/packages/SettingsLib/res/values-lv/strings.xml b/packages/SettingsLib/res/values-lv/strings.xml
index 3d9b78a..a8bd2cc 100644
--- a/packages/SettingsLib/res/values-lv/strings.xml
+++ b/packages/SettingsLib/res/values-lv/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Savienojums netiks izveidots automātiski"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nav piekļuves internetam"</string>
<string name="saved_network" msgid="7143698034077223645">"Saglabāja: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Izveidots savienojums ar maksas tīklu"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automātiski savienots, izmantojot %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automātiski izveidots savienojums, izmantojot tīkla vērtējuma sniedzēju"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Savienots, izmantojot %1$s"</string>
diff --git a/packages/SettingsLib/res/values-mk/strings.xml b/packages/SettingsLib/res/values-mk/strings.xml
index 0674f11..90bcc04 100644
--- a/packages/SettingsLib/res/values-mk/strings.xml
+++ b/packages/SettingsLib/res/values-mk/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не може да се поврзе автоматски"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема пристап до интернет"</string>
<string name="saved_network" msgid="7143698034077223645">"Зачувано од <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматски поврзано преку %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматски поврзано преку оценувач на мрежа"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Поврзано преку %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ml/strings.xml b/packages/SettingsLib/res/values-ml/strings.xml
index 1455669..ba1987b 100644
--- a/packages/SettingsLib/res/values-ml/strings.xml
+++ b/packages/SettingsLib/res/values-ml/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"സ്വയമേവ കണക്റ്റുചെയ്യില്ല"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ഇന്റർനെറ്റ് ആക്സസ് ഇല്ല"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> സംരക്ഷിച്ചത്"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s വഴി സ്വയമേവ ബന്ധിപ്പിച്ചു"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"നെറ്റ്വർക്ക് റേറ്റിംഗ് ദാതാവുമായി സ്വയം കണക്റ്റുചെയ്തു"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s വഴി ബന്ധിപ്പിച്ചു"</string>
diff --git a/packages/SettingsLib/res/values-mn/strings.xml b/packages/SettingsLib/res/values-mn/strings.xml
index f13cb0b..45a831d 100644
--- a/packages/SettingsLib/res/values-mn/strings.xml
+++ b/packages/SettingsLib/res/values-mn/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Автоматаар холбогдохгүй"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Интернет хандалт алга"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> хадгалсан"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s-р автоматаар холбогдсон"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Сүлжээний үнэлгээ үзүүлэгчээр автоматаар холбогдох"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s-р холбогдсон"</string>
diff --git a/packages/SettingsLib/res/values-mr/arrays.xml b/packages/SettingsLib/res/values-mr/arrays.xml
index aaf51b3..6cc0130 100644
--- a/packages/SettingsLib/res/values-mr/arrays.xml
+++ b/packages/SettingsLib/res/values-mr/arrays.xml
@@ -155,14 +155,28 @@
<item msgid="253388653486517049">", अॅक्टिव्ह (मीडिया)"</item>
<item msgid="5001852592115448348">", अॅक्टिव्ह (फोन)"</item>
</string-array>
- <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
+ <string-array name="select_logd_size_titles">
+ <item msgid="1191094707770726722">"बंद"</item>
+ <item msgid="7839165897132179888">"64K"</item>
+ <item msgid="2715700596495505626">"256K"</item>
+ <item msgid="7099386891713159947">"1M"</item>
+ <item msgid="6069075827077845520">"4M"</item>
+ <item msgid="6078203297886482480">"८MB"</item>
+ </string-array>
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"बंद"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
+ <string-array name="select_logd_size_summaries">
+ <item msgid="409235464399258501">"बंद"</item>
+ <item msgid="4195153527464162486">"प्रति लॉग बफर 64K"</item>
+ <item msgid="7464037639415220106">"प्रति लॉग बफर 256K"</item>
+ <item msgid="8539423820514360724">"प्रति लॉग बफर 1M"</item>
+ <item msgid="1984761927103140651">"प्रति लॉग बफर 4M"</item>
+ <item msgid="2983219471251787208">"८MB प्रति लॉग बफर"</item>
+ </string-array>
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"बंद"</item>
<item msgid="6014837961827347618">"सर्व"</item>
diff --git a/packages/SettingsLib/res/values-mr/strings.xml b/packages/SettingsLib/res/values-mr/strings.xml
index 72ddc8f..1e69b28 100644
--- a/packages/SettingsLib/res/values-mr/strings.xml
+++ b/packages/SettingsLib/res/values-mr/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वयंचलितपणे कनेक्ट करणार नाही"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इंटरनेट अॅक्सेस नाही"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारे सेव्ह केले"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s द्वारे स्वयंचलितपणे कनेक्ट केले"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्क रेटिंग प्रदात्याद्वारे स्वयंचलितपणे कनेक्ट केले"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s द्वारे कनेक्ट केले"</string>
diff --git a/packages/SettingsLib/res/values-ms/strings.xml b/packages/SettingsLib/res/values-ms/strings.xml
index 9527793..71c9eea 100644
--- a/packages/SettingsLib/res/values-ms/strings.xml
+++ b/packages/SettingsLib/res/values-ms/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Tidak akan menyambung secara automatik"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Tiada akses Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Diselamatkan oleh <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Disambungkan secara automatik melalui %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Disambungkan secara automatik melalui pembekal penilaian rangkaian"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Disambungkan melalui %1$s"</string>
diff --git a/packages/SettingsLib/res/values-my/strings.xml b/packages/SettingsLib/res/values-my/strings.xml
index 1dcb3cf..3b3983e 100644
--- a/packages/SettingsLib/res/values-my/strings.xml
+++ b/packages/SettingsLib/res/values-my/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"အလိုအလျောက်ချိတ်ဆက်မည်မဟုတ်ပါ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"အင်တာနက် ချိတ်ဆက်မှု မရှိပါ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> က သိမ်းဆည်းခဲ့သည်"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ကွန်ရက်အဆင့်သတ်မှတ်ပေးသူ မှတစ်ဆင့် အလိုအလျောက် ချိတ်ဆက်ထားပါသည်"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s မှတစ်ဆင့် ချိတ်ဆက်ထားသည်"</string>
diff --git a/packages/SettingsLib/res/values-nb/strings.xml b/packages/SettingsLib/res/values-nb/strings.xml
index 13f69c1..a8c01b3 100644
--- a/packages/SettingsLib/res/values-nb/strings.xml
+++ b/packages/SettingsLib/res/values-nb/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Kobler ikke til automatisk"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internettilgang"</string>
<string name="saved_network" msgid="7143698034077223645">"Lagret av <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisk tilkoblet via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisk tilkoblet via leverandør av nettverksvurdering"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Tilkoblet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ne/strings.xml b/packages/SettingsLib/res/values-ne/strings.xml
index 5795cc9..c285637 100644
--- a/packages/SettingsLib/res/values-ne/strings.xml
+++ b/packages/SettingsLib/res/values-ne/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"स्वतः जडान हुने छैन"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"इन्टरनेटमाथिको पहुँच छैन"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> द्वारा सुरक्षित गरियो"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s मार्फत् स्वतः जडान गरिएको"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"नेटवर्कको दर्जा प्रदायक मार्फत स्वत: जडान गरिएको"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s मार्फत जडित"</string>
diff --git a/packages/SettingsLib/res/values-nl/strings.xml b/packages/SettingsLib/res/values-nl/strings.xml
index 718ce5d..9f4ea68 100644
--- a/packages/SettingsLib/res/values-nl/strings.xml
+++ b/packages/SettingsLib/res/values-nl/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Er wordt niet automatisch verbinding gemaakt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Geen internettoegang"</string>
<string name="saved_network" msgid="7143698034077223645">"Opgeslagen door \'<xliff:g id="NAME">%1$s</xliff:g>\'"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatisch verbonden via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatisch verbonden via provider van netwerkbeoordelingen"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Verbonden via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-or/strings.xml b/packages/SettingsLib/res/values-or/strings.xml
index 41e84f9..31bd7af 100644
--- a/packages/SettingsLib/res/values-or/strings.xml
+++ b/packages/SettingsLib/res/values-or/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ସ୍ୱଚାଳିତ ଭାବେ ସଂଯୁକ୍ତ ହେବନାହିଁ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ଇଣ୍ଟରନେଟ୍ର କୌଣସି ଆକ୍ସେସ୍ ନାହିଁ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ଦ୍ୱାରା ସେଭ କରାଯାଇଛି"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲୀ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ନେଟୱର୍କ ମୂଲ୍ୟାୟନ ପ୍ରଦାତାଙ୍କ ମାଧ୍ୟମରେ ଅଟୋମେଟିକାଲ୍ୟ ସଂଯୁକ୍ତ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ମାଧ୍ୟମରେ ସଂଯୁକ୍ତ"</string>
diff --git a/packages/SettingsLib/res/values-pa/arrays.xml b/packages/SettingsLib/res/values-pa/arrays.xml
index c64ee76..c6116aa 100644
--- a/packages/SettingsLib/res/values-pa/arrays.xml
+++ b/packages/SettingsLib/res/values-pa/arrays.xml
@@ -155,14 +155,28 @@
<item msgid="253388653486517049">", ਕਿਰਿਆਸ਼ੀਲ (ਮੀਡੀਆ)"</item>
<item msgid="5001852592115448348">", ਕਿਰਿਆਸ਼ੀਲ (ਫ਼ੋਨ)"</item>
</string-array>
- <!-- no translation found for select_logd_size_titles:5 (6078203297886482480) -->
+ <string-array name="select_logd_size_titles">
+ <item msgid="1191094707770726722">"ਬੰਦ"</item>
+ <item msgid="7839165897132179888">"64K"</item>
+ <item msgid="2715700596495505626">"256K"</item>
+ <item msgid="7099386891713159947">"1M"</item>
+ <item msgid="6069075827077845520">"4M"</item>
+ <item msgid="6078203297886482480">"8M"</item>
+ </string-array>
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"ਬੰਦ"</item>
<item msgid="4064786181089783077">"64K"</item>
<item msgid="3052710745383602630">"256K"</item>
<item msgid="3691785423374588514">"1M"</item>
</string-array>
- <!-- no translation found for select_logd_size_summaries:5 (2983219471251787208) -->
+ <string-array name="select_logd_size_summaries">
+ <item msgid="409235464399258501">"ਬੰਦ"</item>
+ <item msgid="4195153527464162486">"64K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="7464037639415220106">"256K ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="8539423820514360724">"1M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="1984761927103140651">"4M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
+ <item msgid="2983219471251787208">"8M ਪ੍ਰਤੀ ਲੌਗ ਬਫ਼ਰ"</item>
+ </string-array>
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"ਬੰਦ"</item>
<item msgid="6014837961827347618">"ਸਭ"</item>
diff --git a/packages/SettingsLib/res/values-pa/strings.xml b/packages/SettingsLib/res/values-pa/strings.xml
index e04e201..f21c4ce 100644
--- a/packages/SettingsLib/res/values-pa/strings.xml
+++ b/packages/SettingsLib/res/values-pa/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਨਹੀਂ ਕੀਤਾ ਜਾਵੇਗਾ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ਕੋਈ ਇੰਟਰਨੈੱਟ ਪਹੁੰਚ ਨਹੀਂ"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ਵੱਲੋਂ ਰੱਖਿਅਤ ਕੀਤਾ ਗਿਆ"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ਰਾਹੀਂ ਆਪਣੇ-ਆਪ ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ਨੈੱਟਵਰਕ ਰੇਟਿੰਗ ਪ੍ਰਦਾਨਕ ਰਾਹੀਂ ਸਵੈਚਲਿਤ ਤੌਰ \'ਤੇ ਕਨੈਕਟ ਹੋਇਆ"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ਰਾਹੀਂ ਕਨੈਕਟ ਕੀਤਾ"</string>
diff --git a/packages/SettingsLib/res/values-pl/strings.xml b/packages/SettingsLib/res/values-pl/strings.xml
index a57e541..c9c4a6c 100644
--- a/packages/SettingsLib/res/values-pl/strings.xml
+++ b/packages/SettingsLib/res/values-pl/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nie można połączyć automatycznie"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Brak dostępu do internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Zapisane przez: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatycznie połączono przez: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatycznie połączono przez dostawcę ocen jakości sieci"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Połączono przez %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rBR/strings.xml b/packages/SettingsLib/res/values-pt-rBR/strings.xml
index f24b52b..881bccb 100644
--- a/packages/SettingsLib/res/values-pt-rBR/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rBR/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt-rPT/strings.xml b/packages/SettingsLib/res/values-pt-rPT/strings.xml
index 42ad0fe..94cad918 100644
--- a/packages/SettingsLib/res/values-pt-rPT/strings.xml
+++ b/packages/SettingsLib/res/values-pt-rPT/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não é efetuada uma ligação automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet."</string>
<string name="saved_network" msgid="7143698034077223645">"Guardada por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Ligação estabelecida a uma rede de acesso limitado."</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ligado automaticamente através de %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ligado automaticamente através do fornecedor de classificação de rede"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Ligado através de %1$s"</string>
diff --git a/packages/SettingsLib/res/values-pt/strings.xml b/packages/SettingsLib/res/values-pt/strings.xml
index f24b52b..881bccb 100644
--- a/packages/SettingsLib/res/values-pt/strings.xml
+++ b/packages/SettingsLib/res/values-pt/strings.xml
@@ -36,6 +36,7 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Não se conectará automaticamente"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Sem acesso à Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salva por <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <string name="connected_to_metered_access_point" msgid="9179693207918156341">"Conectado a uma rede limitada"</string>
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectado automaticamente via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectado automaticamente via provedor de avaliação de rede"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectado via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ro/strings.xml b/packages/SettingsLib/res/values-ro/strings.xml
index 0743fe9..4b71085 100644
--- a/packages/SettingsLib/res/values-ro/strings.xml
+++ b/packages/SettingsLib/res/values-ro/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nu se va conecta automat"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nu există acces la internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Salvată de <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Conectată automat prin %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Conectată automat prin furnizor de evaluări ale rețelei"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Conectată prin %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ru/strings.xml b/packages/SettingsLib/res/values-ru/strings.xml
index d19438a..4aeb985 100644
--- a/packages/SettingsLib/res/values-ru/strings.xml
+++ b/packages/SettingsLib/res/values-ru/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Подключение не будет выполняться автоматически"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Без доступа к Интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Кто сохранил: <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматически подключено к %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматически подключено через автора рейтинга сетей"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Подключено к %1$s"</string>
diff --git a/packages/SettingsLib/res/values-si/strings.xml b/packages/SettingsLib/res/values-si/strings.xml
index 15fe8c8..525d423 100644
--- a/packages/SettingsLib/res/values-si/strings.xml
+++ b/packages/SettingsLib/res/values-si/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"ස්වයංක්රිය නැවත සම්බන්ධ නොවනු ඇත"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"අන්තර්ජාල ප්රවේශය නැත"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> විසින් සුරකින ලදී"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s හරහා ස්වයංක්රියව සම්බන්ධ විය"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"ජාල ශ්රේණිගත සපයන්නා හරහා ස්වයංක්රියව සම්බන්ධ විය"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s හරහා සම්බන්ධ විය"</string>
diff --git a/packages/SettingsLib/res/values-sk/strings.xml b/packages/SettingsLib/res/values-sk/strings.xml
index ee53b7c..ee9ae6a 100644
--- a/packages/SettingsLib/res/values-sk/strings.xml
+++ b/packages/SettingsLib/res/values-sk/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nedôjde k automatickému pripojeniu"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Žiadny prístup k internetu"</string>
<string name="saved_network" msgid="7143698034077223645">"Uložila aplikácia <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automaticky pripojené prostredníctvom %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automaticky pripojené prostredníctvom poskytovateľa hodnotenia siete"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Pripojené prostredníctvom %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sl/strings.xml b/packages/SettingsLib/res/values-sl/strings.xml
index 66d33a7..ff60a32 100644
--- a/packages/SettingsLib/res/values-sl/strings.xml
+++ b/packages/SettingsLib/res/values-sl/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Samodejna vnovična vzpostavitev povezave se ne bo izvedla"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ni dostopa do interneta"</string>
<string name="saved_network" msgid="7143698034077223645">"Shranil(-a): <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Samodejno vzpostavljena povezava prek: %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Samodejno vzpostavljena povezava prek ponudnika ocenjevanja omrežij"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Vzpostavljena povezava prek: %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sq/strings.xml b/packages/SettingsLib/res/values-sq/strings.xml
index d5c0231..78e6ed6 100644
--- a/packages/SettingsLib/res/values-sq/strings.xml
+++ b/packages/SettingsLib/res/values-sq/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Nuk do të lidhet automatikisht"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Nuk ka qasje në internet"</string>
<string name="saved_network" msgid="7143698034077223645">"E ruajtur nga <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Lidhur automatikisht përmes %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Lidhur automatikisht nëpërmjet ofruesit të vlerësimit të rrjetit"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"E lidhur përmes %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sr/strings.xml b/packages/SettingsLib/res/values-sr/strings.xml
index ce74b84..8bb9277 100644
--- a/packages/SettingsLib/res/values-sr/strings.xml
+++ b/packages/SettingsLib/res/values-sr/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Аутоматско повезивање није успело"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Нема приступа интернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Сачувао/ла је <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Аутоматски повезано преко %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Аутоматски повезано преко добављача оцене мреже"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Веза је успостављена преко приступне тачке %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sv/strings.xml b/packages/SettingsLib/res/values-sv/strings.xml
index eacb7a8..03fb223 100644
--- a/packages/SettingsLib/res/values-sv/strings.xml
+++ b/packages/SettingsLib/res/values-sv/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Det går inte att ansluta automatiskt"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Ingen internetåtkomst"</string>
<string name="saved_network" msgid="7143698034077223645">"Sparades av <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Automatiskt ansluten via %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Automatiskt ansluten via leverantör av nätverksbetyg"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Anslutet via %1$s"</string>
diff --git a/packages/SettingsLib/res/values-sw/arrays.xml b/packages/SettingsLib/res/values-sw/arrays.xml
index 62ae06b..b95d69c 100644
--- a/packages/SettingsLib/res/values-sw/arrays.xml
+++ b/packages/SettingsLib/res/values-sw/arrays.xml
@@ -161,7 +161,7 @@
<item msgid="2715700596495505626">"K256"</item>
<item msgid="7099386891713159947">"M1"</item>
<item msgid="6069075827077845520">"M4"</item>
- <item msgid="6078203297886482480">"M8"</item>
+ <item msgid="6078203297886482480">"MB 8"</item>
</string-array>
<string-array name="select_logd_size_lowram_titles">
<item msgid="1145807928339101085">"Imezimwa"</item>
@@ -175,7 +175,7 @@
<item msgid="7464037639415220106">"K256 kwa kila akiba ya kumbukumbu"</item>
<item msgid="8539423820514360724">"M1 kwa kila akiba ya kumbukumbu"</item>
<item msgid="1984761927103140651">"M4 kwa kila akiba ya kumbukumbu"</item>
- <item msgid="2983219471251787208">"M8 kwa kila akiba ya kumbukumbu"</item>
+ <item msgid="2983219471251787208">"MB 8 kwa kila akiba ya kumbukumbu"</item>
</string-array>
<string-array name="select_logpersist_titles">
<item msgid="704720725704372366">"Yamezimwa"</item>
diff --git a/packages/SettingsLib/res/values-sw/strings.xml b/packages/SettingsLib/res/values-sw/strings.xml
index e7045a7..631a413 100644
--- a/packages/SettingsLib/res/values-sw/strings.xml
+++ b/packages/SettingsLib/res/values-sw/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Haiwezi kuunganisha kiotomatiki"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Hakuna muunganisho wa intaneti"</string>
<string name="saved_network" msgid="7143698034077223645">"Ilihifadhiwa na <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Imeunganishwa kiotomatiki kupitia %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Imeunganishwa kiotomatiki kupitia mtoa huduma wa ukadiriaji wa mtandao"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Imeunganishwa kupitia %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ta/strings.xml b/packages/SettingsLib/res/values-ta/strings.xml
index 06c7ccb..5796603 100644
--- a/packages/SettingsLib/res/values-ta/strings.xml
+++ b/packages/SettingsLib/res/values-ta/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"தானாக இணைக்கப்படாது"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"இண்டர்நெட் அணுகல் இல்லை"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> சேமித்தது"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s மூலம் தானாக இணைக்கப்பட்டது"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"நெட்வொர்க் மதிப்பீடு வழங்குநரால் தானாக இணைக்கப்பட்டது"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s வழியாக இணைக்கப்பட்டது"</string>
diff --git a/packages/SettingsLib/res/values-te/strings.xml b/packages/SettingsLib/res/values-te/strings.xml
index 5e880a9..9027ca1 100644
--- a/packages/SettingsLib/res/values-te/strings.xml
+++ b/packages/SettingsLib/res/values-te/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"స్వయంచాలకంగా కనెక్ట్ కాదు"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"ఇంటర్నెట్ యాక్సెస్ లేదు"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> ద్వారా సేవ్ చేయబడింది"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"నెట్వర్క్ రేటింగ్ ప్రదాత ద్వారా స్వయంచాలకంగా కనెక్ట్ చేయబడింది"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s ద్వారా కనెక్ట్ చేయబడింది"</string>
diff --git a/packages/SettingsLib/res/values-th/strings.xml b/packages/SettingsLib/res/values-th/strings.xml
index 6316452..8358dbb 100644
--- a/packages/SettingsLib/res/values-th/strings.xml
+++ b/packages/SettingsLib/res/values-th/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"จะไม่เชื่อมต่อโดยอัตโนมัติ"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"เข้าถึงอินเทอร์เน็ตไม่ได้"</string>
<string name="saved_network" msgid="7143698034077223645">"บันทึกโดย<xliff:g id="NAME">%1$s</xliff:g> แล้ว"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"เชื่อมต่ออัตโนมัติผ่าน %1$s แล้ว"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"เชื่อมต่ออัตโนมัติผ่านผู้ให้บริการการจัดอันดับเครือข่าย"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"เชื่อมต่อผ่าน %1$s แล้ว"</string>
diff --git a/packages/SettingsLib/res/values-tl/strings.xml b/packages/SettingsLib/res/values-tl/strings.xml
index 0aabbe5..d5250a4 100644
--- a/packages/SettingsLib/res/values-tl/strings.xml
+++ b/packages/SettingsLib/res/values-tl/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Hindi awtomatikong kokonekta"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Walang access sa internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Na-save ng <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Awtomatikong nakakonekta sa pamamagitan ng %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Awtomatikong nakakonekta sa pamamagitan ng provider ng rating ng network"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Nakakonekta sa pamamagitan ng %1$s"</string>
diff --git a/packages/SettingsLib/res/values-tr/strings.xml b/packages/SettingsLib/res/values-tr/strings.xml
index 44c8f13..8e0b889 100644
--- a/packages/SettingsLib/res/values-tr/strings.xml
+++ b/packages/SettingsLib/res/values-tr/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Otomatik olarak bağlanma"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"İnternet erişimi yok"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tarafından kaydedildi"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s üzerinden otomatik olarak bağlı"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Ağ derecelendirme sağlayıcı aracılığıyla otomatik olarak bağlandı"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s üzerinden bağlı"</string>
diff --git a/packages/SettingsLib/res/values-uk/strings.xml b/packages/SettingsLib/res/values-uk/strings.xml
index 7851111..c2f71c3 100644
--- a/packages/SettingsLib/res/values-uk/strings.xml
+++ b/packages/SettingsLib/res/values-uk/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Не під’єднуватиметься автоматично"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Немає доступу до Інтернету"</string>
<string name="saved_network" msgid="7143698034077223645">"Збережено додатком <xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Автоматично під’єднано через %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Автоматично під’єднано через постачальника оцінки якості мережі"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Під’єднано через %1$s"</string>
diff --git a/packages/SettingsLib/res/values-ur/strings.xml b/packages/SettingsLib/res/values-ur/strings.xml
index 82783b3..a0c5f94 100644
--- a/packages/SettingsLib/res/values-ur/strings.xml
+++ b/packages/SettingsLib/res/values-ur/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"خودکار طور پر منسلک نہیں ہو گا"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"انٹرنیٹ تک کوئی رسائی نہیں"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> کی جانب سے محفوظ کردہ"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s کے ذریعے از خود منسلک کردہ"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"نیٹ ورک درجہ بندی کے فراہم کنندہ کے ذریعے از خود منسلک"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"منسلک بذریعہ %1$s"</string>
diff --git a/packages/SettingsLib/res/values-uz/strings.xml b/packages/SettingsLib/res/values-uz/strings.xml
index c2a96c6..1fb610b 100644
--- a/packages/SettingsLib/res/values-uz/strings.xml
+++ b/packages/SettingsLib/res/values-uz/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Avtomatik ravishda ulanilmaydi"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Internet aloqasi yo‘q"</string>
<string name="saved_network" msgid="7143698034077223645">"<xliff:g id="NAME">%1$s</xliff:g> tomonidan saqlangan"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"%1$s orqali avtomatik ulandi"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tarmoqlar reytingi muallifi orqali avtomatik ulandi"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"%1$s orqali ulangan"</string>
diff --git a/packages/SettingsLib/res/values-vi/strings.xml b/packages/SettingsLib/res/values-vi/strings.xml
index 2596424..1f3ea48 100644
--- a/packages/SettingsLib/res/values-vi/strings.xml
+++ b/packages/SettingsLib/res/values-vi/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Sẽ không tự động kết nối"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Không có quyền truy cập Internet"</string>
<string name="saved_network" msgid="7143698034077223645">"Do <xliff:g id="NAME">%1$s</xliff:g> lưu"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Tự động được kết nối qua %1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Tự động được kết nối qua nhà cung cấp dịch vụ xếp hạng mạng"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Được kết nối qua %1$s"</string>
diff --git a/packages/SettingsLib/res/values-zh-rCN/strings.xml b/packages/SettingsLib/res/values-zh-rCN/strings.xml
index 60afd6d..828d25f 100644
--- a/packages/SettingsLib/res/values-zh-rCN/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rCN/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"无法自动连接"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"无法访问互联网"</string>
<string name="saved_network" msgid="7143698034077223645">"由“<xliff:g id="NAME">%1$s</xliff:g>”保存"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已通过%1$s自动连接"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已自动连接(通过网络评分服务提供方)"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已通过%1$s连接"</string>
diff --git a/packages/SettingsLib/res/values-zh-rHK/strings.xml b/packages/SettingsLib/res/values-zh-rHK/strings.xml
index 925f738..54e1f41 100644
--- a/packages/SettingsLib/res/values-zh-rHK/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rHK/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"不會自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"無法連接互聯網"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網絡評分供應商自動連線"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zh-rTW/strings.xml b/packages/SettingsLib/res/values-zh-rTW/strings.xml
index e40d351..b8f1f58 100644
--- a/packages/SettingsLib/res/values-zh-rTW/strings.xml
+++ b/packages/SettingsLib/res/values-zh-rTW/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"無法自動連線"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"沒有可用的網際網路連線"</string>
<string name="saved_network" msgid="7143698034077223645">"由「<xliff:g id="NAME">%1$s</xliff:g>」儲存"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"已透過 %1$s 自動連線"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"已透過網路評分供應商自動連線"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"已透過 %1$s 連線"</string>
diff --git a/packages/SettingsLib/res/values-zu/strings.xml b/packages/SettingsLib/res/values-zu/strings.xml
index c304c14..d680b66 100644
--- a/packages/SettingsLib/res/values-zu/strings.xml
+++ b/packages/SettingsLib/res/values-zu/strings.xml
@@ -36,6 +36,8 @@
<string name="wifi_no_internet_no_reconnect" msgid="821591791066497347">"Ngeke ize ixhumeke ngokuzenzakalela"</string>
<string name="wifi_no_internet" msgid="1774198889176926299">"Akukho ukufinyelela kwe-inthanethi"</string>
<string name="saved_network" msgid="7143698034077223645">"Kulondolozwe ngu-<xliff:g id="NAME">%1$s</xliff:g>"</string>
+ <!-- no translation found for connected_to_metered_access_point (9179693207918156341) -->
+ <skip />
<string name="connected_via_network_scorer" msgid="7665725527352893558">"Ixhumeke ngokuzenzakalela nge-%1$s"</string>
<string name="connected_via_network_scorer_default" msgid="7973529709744526285">"Kuxhunywe ngokuzenzakalelayo ngomhlinzeki wesilinganiso wenethiwekhi"</string>
<string name="connected_via_passpoint" msgid="7735442932429075684">"Kuxhumeke nge-%1$s"</string>
diff --git a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
index 6d7e86f..34da305 100644
--- a/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
+++ b/packages/SettingsLib/src/com/android/settingslib/fuelgauge/BatterySaverUtils.java
@@ -108,7 +108,7 @@
* - If it's the first time and needFirstTimeWarning, show the first time dialog.
* - If it's 4th time through 8th time, show the schedule suggestion notification.
*
- * @param enable true to disable battery saver.
+ * @param enable true to enable battery saver.
*
* @return true if the request succeeded.
*/
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
index 70021b6..fbabaa4 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/SyncRtSurfaceTransactionApplierCompat.java
@@ -114,10 +114,13 @@
for (int i = params.length - 1; i >= 0; i--) {
SyncRtSurfaceTransactionApplierCompat.SurfaceParams surfaceParams =
params[i];
- t.deferTransactionUntil(surfaceParams.surface, mBarrierSurfaceControl, frame);
surfaceParams.applyTo(t);
}
- t.apply();
+ if (mTargetViewRootImpl != null) {
+ mTargetViewRootImpl.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
Message.obtain(mApplyHandler, MSG_UPDATE_SEQUENCE_NUMBER, toApplySeqNo, 0)
.sendToTarget();
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
index 4a28d56..89c60f1 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/system/ViewRootImplCompat.java
@@ -56,4 +56,12 @@
});
}
}
+
+ public void mergeWithNextTransaction(SurfaceControl.Transaction t, long frame) {
+ if (mViewRoot != null) {
+ mViewRoot.mergeWithNextTransaction(t, frame);
+ } else {
+ t.apply();
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
index 93a8df4..cd3d6a8 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpItem.java
@@ -26,8 +26,7 @@
private String mPackageName;
private long mTimeStarted;
private StringBuilder mState;
- // This is only used for items with mCode == AppOpsManager.OP_RECORD_AUDIO
- private boolean mSilenced;
+ private boolean mIsDisabled;
public AppOpItem(int code, int uid, String packageName, long timeStarted) {
this.mCode = code;
@@ -58,16 +57,16 @@
return mTimeStarted;
}
- public void setSilenced(boolean silenced) {
- mSilenced = silenced;
+ public void setDisabled(boolean misDisabled) {
+ this.mIsDisabled = misDisabled;
}
- public boolean isSilenced() {
- return mSilenced;
+ public boolean isDisabled() {
+ return mIsDisabled;
}
@Override
public String toString() {
- return mState.append(mSilenced).append(")").toString();
+ return mState.append(mIsDisabled).append(")").toString();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
index 1036c99..d8ca639 100644
--- a/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/appops/AppOpsControllerImpl.java
@@ -16,6 +16,8 @@
package com.android.systemui.appops;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
import static android.media.AudioManager.ACTION_MICROPHONE_MUTE_CHANGED;
import android.Manifest;
@@ -45,6 +47,7 @@
import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import com.android.systemui.util.Assert;
import java.io.FileDescriptor;
@@ -64,7 +67,8 @@
@SysUISingleton
public class AppOpsControllerImpl extends BroadcastReceiver implements AppOpsController,
AppOpsManager.OnOpActiveChangedInternalListener,
- AppOpsManager.OnOpNotedListener, Dumpable {
+ AppOpsManager.OnOpNotedListener, IndividualSensorPrivacyController.Callback,
+ Dumpable {
// This is the minimum time that we will keep AppOps that are noted on record. If multiple
// occurrences of the same (op, package, uid) happen in a shorter interval, they will not be
@@ -77,8 +81,8 @@
private final AppOpsManager mAppOps;
private final AudioManager mAudioManager;
private final LocationManager mLocationManager;
- // TODO ntmyren: remove t
private final PackageManager mPackageManager;
+ private final IndividualSensorPrivacyController mSensorPrivacyController;
// mLocationProviderPackages are cached and updated only occasionally
private static final long LOCATION_PROVIDER_UPDATE_FREQUENCY_MS = 30000;
@@ -91,6 +95,7 @@
private final PermissionFlagsCache mFlagsCache;
private boolean mListening;
private boolean mMicMuted;
+ private boolean mCameraDisabled;
@GuardedBy("mActiveItems")
private final List<AppOpItem> mActiveItems = new ArrayList<>();
@@ -118,6 +123,7 @@
DumpManager dumpManager,
PermissionFlagsCache cache,
AudioManager audioManager,
+ IndividualSensorPrivacyController sensorPrivacyController,
BroadcastDispatcher dispatcher
) {
mDispatcher = dispatcher;
@@ -129,7 +135,10 @@
mCallbacksByCode.put(OPS[i], new ArraySet<>());
}
mAudioManager = audioManager;
- mMicMuted = audioManager.isMicrophoneMute();
+ mSensorPrivacyController = sensorPrivacyController;
+ mMicMuted = audioManager.isMicrophoneMute()
+ || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
mLocationManager = context.getSystemService(LocationManager.class);
mPackageManager = context.getPackageManager();
dumpManager.registerDumpable(TAG, this);
@@ -147,6 +156,12 @@
mAppOps.startWatchingActive(OPS, this);
mAppOps.startWatchingNoted(OPS, this);
mAudioManager.registerAudioRecordingCallback(mAudioRecordingCallback, mBGHandler);
+ mSensorPrivacyController.addCallback(this);
+
+ mMicMuted = mAudioManager.isMicrophoneMute()
+ || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+ mCameraDisabled = mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA);
+
mBGHandler.post(() -> mAudioRecordingCallback.onRecordingConfigChanged(
mAudioManager.getActiveRecordingConfigurations()));
mDispatcher.registerReceiverWithHandler(this,
@@ -156,6 +171,7 @@
mAppOps.stopWatchingActive(this);
mAppOps.stopWatchingNoted(this);
mAudioManager.unregisterAudioRecordingCallback(mAudioRecordingCallback);
+ mSensorPrivacyController.removeCallback(this);
mBGHandler.removeCallbacksAndMessages(null); // null removes all
mDispatcher.unregisterReceiver(this);
@@ -235,11 +251,13 @@
if (item == null && active) {
item = new AppOpItem(code, uid, packageName, System.currentTimeMillis());
if (code == AppOpsManager.OP_RECORD_AUDIO) {
- item.setSilenced(isAnyRecordingPausedLocked(uid));
+ item.setDisabled(isAnyRecordingPausedLocked(uid));
+ } else if (code == AppOpsManager.OP_CAMERA) {
+ item.setDisabled(mCameraDisabled);
}
mActiveItems.add(item);
if (DEBUG) Log.w(TAG, "Added item: " + item.toString());
- return !item.isSilenced();
+ return !item.isDisabled();
} else if (item != null && !active) {
mActiveItems.remove(item);
if (DEBUG) Log.w(TAG, "Removed item: " + item.toString());
@@ -409,7 +427,7 @@
AppOpItem item = mActiveItems.get(i);
if ((userId == UserHandle.USER_ALL
|| UserHandle.getUserId(item.getUid()) == userId)
- && isUserVisible(item) && !item.isSilenced()) {
+ && isUserVisible(item) && !item.isDisabled()) {
list.add(item);
}
}
@@ -512,22 +530,27 @@
return false;
}
- private void updateRecordingPausedStatus() {
+ private void updateSensorDisabledStatus() {
synchronized (mActiveItems) {
int size = mActiveItems.size();
for (int i = 0; i < size; i++) {
AppOpItem item = mActiveItems.get(i);
+
+ boolean paused = false;
if (item.getCode() == AppOpsManager.OP_RECORD_AUDIO) {
- boolean paused = isAnyRecordingPausedLocked(item.getUid());
- if (item.isSilenced() != paused) {
- item.setSilenced(paused);
- notifySuscribers(
- item.getCode(),
- item.getUid(),
- item.getPackageName(),
- !item.isSilenced()
- );
- }
+ paused = isAnyRecordingPausedLocked(item.getUid());
+ } else if (item.getCode() == AppOpsManager.OP_CAMERA) {
+ paused = mCameraDisabled;
+ }
+
+ if (item.isDisabled() != paused) {
+ item.setDisabled(paused);
+ notifySuscribers(
+ item.getCode(),
+ item.getUid(),
+ item.getPackageName(),
+ !item.isDisabled()
+ );
}
}
}
@@ -552,14 +575,27 @@
recordings.add(recording);
}
}
- updateRecordingPausedStatus();
+ updateSensorDisabledStatus();
}
};
@Override
public void onReceive(Context context, Intent intent) {
- mMicMuted = mAudioManager.isMicrophoneMute();
- updateRecordingPausedStatus();
+ mMicMuted = mAudioManager.isMicrophoneMute()
+ || mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_MICROPHONE);
+ updateSensorDisabledStatus();
+ }
+
+ @Override
+ public void onSensorBlockedChanged(int sensor, boolean blocked) {
+ mBGHandler.post(() -> {
+ if (sensor == INDIVIDUAL_SENSOR_CAMERA) {
+ mCameraDisabled = blocked;
+ } else if (sensor == INDIVIDUAL_SENSOR_MICROPHONE) {
+ mMicMuted = mAudioManager.isMicrophoneMute() || blocked;
+ }
+ updateSensorDisabledStatus();
+ });
}
protected class H extends Handler {
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
index fb281169..63e27796 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tiles/NfcTile.java
@@ -47,6 +47,7 @@
/** Quick settings tile: Enable/Disable NFC **/
public class NfcTile extends QSTileImpl<BooleanState> {
+ private static final String NFC = "nfc";
private final Icon mIcon = ResourceIcon.get(R.drawable.ic_qs_nfc);
private NfcAdapter mAdapter;
@@ -89,7 +90,13 @@
@Override
public boolean isAvailable() {
- return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+ String stockTiles = mContext.getString(R.string.quick_settings_tiles_stock);
+ // For the restore from backup case
+ // Return false when "nfc" is not listed in quick_settings_tiles_stock.
+ if (stockTiles.contains(NFC)) {
+ return mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_NFC);
+ }
+ return false;
}
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
index c2c6790..9be3566 100644
--- a/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
+++ b/packages/SystemUI/src/com/android/systemui/screenshot/ScrollCaptureController.java
@@ -23,7 +23,6 @@
import android.net.Uri;
import android.os.UserHandle;
import android.util.Log;
-import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
@@ -59,7 +58,6 @@
private final Executor mBgExecutor;
private final ImageExporter mImageExporter;
private final ImageTileSet mImageTileSet;
- private final LayoutInflater mLayoutInflater;
private ZonedDateTime mCaptureTime;
private UUID mRequestId;
@@ -81,7 +79,6 @@
mBgExecutor = bgExecutor;
mImageExporter = exporter;
mImageTileSet = new ImageTileSet();
- mLayoutInflater = mContext.getSystemService(LayoutInflater.class);
}
/**
@@ -114,7 +111,7 @@
mEdit.setOnClickListener(this::onClicked);
mShare.setOnClickListener(this::onClicked);
- mPreview.setImageDrawable(mImageTileSet.getDrawable());
+ //mPreview.setImageDrawable(mImageTileSet.getDrawable());
mConnection.start(this::startCapture);
}
@@ -242,6 +239,7 @@
if (mImageTileSet.isEmpty()) {
session.end(mCallback::onFinish);
} else {
+ mPreview.setImageDrawable(mImageTileSet.getDrawable());
mExportFuture = mImageExporter.export(
mBgExecutor, mRequestId, mImageTileSet.toBitmap(), mCaptureTime);
// The user chose an action already, link it to the result
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 d4a2b41..b20c457 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -2331,11 +2331,11 @@
&& mStatusBarWindowState != state) {
mStatusBarWindowState = state;
if (DEBUG_WINDOW_STATE) Log.d(TAG, "Status bar " + windowStateToString(state));
- if (!showing && mState == StatusBarState.SHADE) {
- mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
- 1.0f /* speedUpFactor */);
- }
if (mStatusBarView != null) {
+ if (!showing && mState == StatusBarState.SHADE) {
+ mStatusBarView.collapsePanel(false /* animate */, false /* delayed */,
+ 1.0f /* speedUpFactor */);
+ }
mStatusBarWindowHidden = state == WINDOW_STATE_HIDDEN;
updateHideIconsForBouncer(false /* animate */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
index 231fe08..32d15ed 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/IndividualSensorPrivacyControllerImpl.java
@@ -16,8 +16,8 @@
package com.android.systemui.statusbar.policy;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.CAMERA;
-import static android.service.SensorPrivacyIndividualEnabledSensorProto.MICROPHONE;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
import android.hardware.SensorPrivacyManager;
import android.hardware.SensorPrivacyManager.IndividualSensor;
@@ -30,7 +30,8 @@
public class IndividualSensorPrivacyControllerImpl implements IndividualSensorPrivacyController {
- private static final int[] SENSORS = new int[] {CAMERA, MICROPHONE};
+ private static final int[] SENSORS = new int[] {INDIVIDUAL_SENSOR_CAMERA,
+ INDIVIDUAL_SENSOR_MICROPHONE};
private final @NonNull SensorPrivacyManager mSensorPrivacyManager;
private final SparseBooleanArray mState = new SparseBooleanArray();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
index 02143a7..bc322f7 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/appops/AppOpsControllerTest.java
@@ -16,6 +16,9 @@
package com.android.systemui.appops;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_CAMERA;
+import static android.hardware.SensorPrivacyManager.INDIVIDUAL_SENSOR_MICROPHONE;
+
import static junit.framework.TestCase.assertFalse;
import static org.junit.Assert.assertEquals;
@@ -49,6 +52,7 @@
import com.android.systemui.SysuiTestCase;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.statusbar.policy.IndividualSensorPrivacyController;
import org.junit.Before;
import org.junit.Test;
@@ -81,6 +85,8 @@
private PermissionFlagsCache mFlagsCache;
@Mock
private PackageManager mPackageManager;
+ @Mock
+ private IndividualSensorPrivacyController mSensorPrivacyController;
@Mock(stubOnly = true)
private AudioManager mAudioManager;
@Mock()
@@ -118,12 +124,18 @@
when(mAudioManager.getActiveRecordingConfigurations())
.thenReturn(List.of(mPausedMockRecording));
+ when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+ .thenReturn(false);
+ when(mSensorPrivacyController.isSensorBlocked(INDIVIDUAL_SENSOR_CAMERA))
+ .thenReturn(false);
+
mController = new AppOpsControllerImpl(
mContext,
mTestableLooper.getLooper(),
mDumpManager,
mFlagsCache,
mAudioManager,
+ mSensorPrivacyController,
mDispatcher
);
}
@@ -133,6 +145,7 @@
mController.setListening(true);
verify(mAppOpsManager, times(1)).startWatchingActive(AppOpsControllerImpl.OPS, mController);
verify(mDispatcher, times(1)).registerReceiverWithHandler(eq(mController), any(), any());
+ verify(mSensorPrivacyController, times(1)).addCallback(mController);
}
@Test
@@ -140,6 +153,7 @@
mController.setListening(false);
verify(mAppOpsManager, times(1)).stopWatchingActive(mController);
verify(mDispatcher, times(1)).unregisterReceiver(mController);
+ verify(mSensorPrivacyController, times(1)).removeCallback(mController);
}
@Test
@@ -476,6 +490,71 @@
AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, false);
}
+ @Test
+ public void testAudioFilteredWhenMicDisabled() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
+ mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+ assertFalse(list.get(0).isDisabled());
+
+ // Add a camera op, and disable the microphone. The camera op should be the only op returned
+ mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, true);
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+
+
+ // Re enable the microphone, and verify the op returns
+ mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_MICROPHONE, false);
+ mTestableLooper.processAllMessages();
+
+ list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+ int micIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 1 : 0;
+ assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(micIdx).getCode());
+ }
+
+ @Test
+ public void testCameraFilteredWhenCameraDisabled() {
+ mController.addCallback(new int[]{AppOpsManager.OP_RECORD_AUDIO, AppOpsManager.OP_CAMERA},
+ mCallback);
+ mTestableLooper.processAllMessages();
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_CAMERA, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ List<AppOpItem> list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_CAMERA, list.get(0).getCode());
+ assertFalse(list.get(0).isDisabled());
+
+ // Add an audio op, and disable the camera. The audio op should be the only op returned
+ mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, true);
+ mController.onOpActiveChanged(
+ AppOpsManager.OP_RECORD_AUDIO, TEST_UID_OTHER, TEST_PACKAGE_NAME, true);
+ mTestableLooper.processAllMessages();
+ list = mController.getActiveAppOps();
+ assertEquals(1, list.size());
+ assertEquals(AppOpsManager.OP_RECORD_AUDIO, list.get(0).getCode());
+
+ // Re enable the camera, and verify the op returns
+ mController.onSensorBlockedChanged(INDIVIDUAL_SENSOR_CAMERA, false);
+ mTestableLooper.processAllMessages();
+
+ list = mController.getActiveAppOps();
+ assertEquals(2, list.size());
+ int cameraIdx = list.get(0).getCode() == AppOpsManager.OP_CAMERA ? 0 : 1;
+ assertEquals(AppOpsManager.OP_CAMERA, list.get(cameraIdx).getCode());
+ }
+
private class TestHandler extends AppOpsControllerImpl.H {
TestHandler(Looper looper) {
mController.super(looper);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
new file mode 100644
index 0000000..b37ac4a
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tiles/NfcTileTest.java
@@ -0,0 +1,110 @@
+/*
+ * 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.tiles;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Handler;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import androidx.test.filters.SmallTest;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.systemui.R;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.broadcast.BroadcastDispatcher;
+import com.android.systemui.plugins.ActivityStarter;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.qs.QSTileHost;
+import com.android.systemui.qs.logging.QSLogger;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+public class NfcTileTest extends SysuiTestCase {
+
+ private static final String TILES_STOCK_WITHOUT_NFC = "wifi,cell,battery,dnd,flashlight,bt";
+ private static final String TILES_STOCK_WITH_NFC = "wifi,cell,battery,dnd,nfc,flashlight,bt";
+
+ @Mock
+ private Context mMockContext;
+ @Mock
+ private PackageManager mPackageManager;
+ @Mock
+ private ActivityStarter mActivityStarter;
+ @Mock
+ private QSTileHost mHost;
+ @Mock
+ private MetricsLogger mMetricsLogger;
+ @Mock
+ private StatusBarStateController mStatusBarStateController;
+ @Mock
+ private QSLogger mQSLogger;
+ @Mock
+ private BroadcastDispatcher mBroadcastDispatcher;
+
+ private TestableLooper mTestableLooper;
+ private NfcTile mNfcTile;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ mTestableLooper = TestableLooper.get(this);
+
+ when(mHost.getContext()).thenReturn(mMockContext);
+ when(mMockContext.getPackageManager()).thenReturn(mPackageManager);
+
+ mNfcTile = new NfcTile(
+ mHost,
+ mTestableLooper.getLooper(),
+ new Handler(mTestableLooper.getLooper()),
+ mMetricsLogger,
+ mStatusBarStateController,
+ mActivityStarter,
+ mQSLogger,
+ mBroadcastDispatcher
+ );
+ }
+
+ @Test
+ public void testIsAvailable_stockWithoutNfc_returnsFalse() {
+ when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn(
+ TILES_STOCK_WITHOUT_NFC);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true);
+ assertFalse(mNfcTile.isAvailable());
+ }
+
+ @Test
+ public void testIsAvailable_stockWithNfc_returnsTrue() {
+ when(mMockContext.getString(R.string.quick_settings_tiles_stock)).thenReturn(
+ TILES_STOCK_WITH_NFC);
+ when(mPackageManager.hasSystemFeature(PackageManager.FEATURE_NFC)).thenReturn(true);
+ assertTrue(mNfcTile.isAvailable());
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
index 1a63dde..8253927 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/FaceService.java
@@ -218,7 +218,7 @@
@Override // Binder call
public void enroll(int userId, final IBinder token, final byte[] hardwareAuthToken,
final IFaceServiceReceiver receiver, final String opPackageName,
- final int[] disabledFeatures, Surface surface) {
+ final int[] disabledFeatures, Surface surface, boolean debugConsent) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
@@ -229,7 +229,7 @@
provider.second.scheduleEnroll(provider.first, token, hardwareAuthToken, userId,
receiver, opPackageName, disabledFeatures,
- convertSurfaceToNativeHandle(surface));
+ convertSurfaceToNativeHandle(surface), debugConsent);
}
@Override // Binder call
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
index 32428ac1..cc24b89 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/ServiceProvider.java
@@ -94,7 +94,8 @@
void scheduleEnroll(int sensorId, @NonNull IBinder token, @NonNull byte[] hardwareAuthToken,
int userId, @NonNull IFaceServiceReceiver receiver, @NonNull String opPackageName,
- @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle);
+ @NonNull int[] disabledFeatures, @Nullable NativeHandle surfaceHandle,
+ boolean debugConsent);
void cancelEnrollment(int sensorId, @NonNull IBinder token);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
index 211d79c..d2673d2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/BiometricTestSessionImpl.java
@@ -142,7 +142,8 @@
Utils.checkPermission(mContext, TEST_BIOMETRIC);
mProvider.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
- mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */);
+ mContext.getOpPackageName(), new int[0] /* disabledFeatures */, null /* surface */,
+ false /* debugConsent */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
index d60bb79..afc7f64 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceEnrollClient.java
@@ -23,6 +23,7 @@
import android.hardware.biometrics.BiometricsProtoEnums;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.face.EnrollmentType;
+import android.hardware.biometrics.face.Feature;
import android.hardware.biometrics.face.IFace;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
@@ -55,12 +56,14 @@
@Nullable private ICancellationSignal mCancellationSignal;
@Nullable private android.hardware.common.NativeHandle mPreviewSurface;
private final int mMaxTemplatesPerUser;
+ private final boolean mDebugConsent;
FaceEnrollClient(@NonNull Context context, @NonNull LazyDaemon<ISession> lazyDaemon,
@NonNull IBinder token, @NonNull ClientMonitorCallbackConverter listener, int userId,
@NonNull byte[] hardwareAuthToken, @NonNull String opPackageName,
@NonNull BiometricUtils<Face> utils, @NonNull int[] disabledFeatures, int timeoutSec,
- @Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser) {
+ @Nullable NativeHandle previewSurface, int sensorId, int maxTemplatesPerUser,
+ boolean debugConsent) {
super(context, lazyDaemon, token, listener, userId, hardwareAuthToken, opPackageName, utils,
timeoutSec, BiometricsProtoEnums.MODALITY_FACE, sensorId,
false /* shouldVibrate */);
@@ -69,6 +72,7 @@
mEnrollIgnoreListVendor = getContext().getResources()
.getIntArray(R.array.config_face_acquire_vendor_enroll_ignorelist);
mMaxTemplatesPerUser = maxTemplatesPerUser;
+ mDebugConsent = debugConsent;
try {
// We must manually close the duplicate handle after it's no longer needed.
// The caller is responsible for closing the original handle.
@@ -116,9 +120,17 @@
try {
// TODO(b/172593978): Pass features.
// TODO(b/174619156): Handle accessibility enrollment.
+ byte[] features;
+ if (mDebugConsent) {
+ features = new byte[1];
+ features[0] = Feature.DEBUG;
+ } else {
+ features = new byte[0];
+ }
+
mCancellationSignal = getFreshDaemon().enroll(mSequentialId,
HardwareAuthTokenUtils.toHardwareAuthToken(mHardwareAuthToken),
- EnrollmentType.DEFAULT, new byte[0], mPreviewSurface);
+ EnrollmentType.DEFAULT, features, mPreviewSurface);
} catch (RemoteException e) {
Slog.e(TAG, "Remote exception when requesting enroll", e);
onError(BiometricFaceConstants.FACE_ERROR_UNABLE_TO_PROCESS, 0 /* vendorCode */);
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
index f7feffd..e685ee2 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceProvider.java
@@ -382,7 +382,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable NativeHandle previewSurface) {
+ @Nullable NativeHandle previewSurface, boolean debugConsent) {
mHandler.post(() -> {
final IFace daemon = getHalInstance();
if (daemon == null) {
@@ -404,7 +404,8 @@
mSensors.get(sensorId).getLazySession(), token,
new ClientMonitorCallbackConverter(receiver), userId, hardwareAuthToken,
opPackageName, FaceUtils.getInstance(sensorId), disabledFeatures,
- ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser);
+ ENROLL_TIMEOUT_SEC, previewSurface, sensorId, maxTemplatesPerUser,
+ debugConsent);
scheduleForSensor(sensorId, client, new BaseClientMonitor.Callback() {
@Override
public void onClientFinished(@NonNull BaseClientMonitor clientMonitor,
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
index 9ed8f78..4142a52 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/BiometricTestSessionImpl.java
@@ -131,7 +131,7 @@
mFace10.scheduleEnroll(mSensorId, new Binder(), new byte[69], userId, mReceiver,
mContext.getOpPackageName(), new int[0] /* disabledFeatures */,
- null /* surfaceHandle */);
+ null /* surfaceHandle */, false /* debugConsent */);
}
@Override
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
index 775d8d4..e46661a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/Face10.java
@@ -602,7 +602,7 @@
public void scheduleEnroll(int sensorId, @NonNull IBinder token,
@NonNull byte[] hardwareAuthToken, int userId, @NonNull IFaceServiceReceiver receiver,
@NonNull String opPackageName, @NonNull int[] disabledFeatures,
- @Nullable NativeHandle surfaceHandle) {
+ @Nullable NativeHandle surfaceHandle, boolean debugConsent) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
diff --git a/services/core/java/com/android/server/display/DisplayPowerController.java b/services/core/java/com/android/server/display/DisplayPowerController.java
index d9ee9a3..13dc0b9 100644
--- a/services/core/java/com/android/server/display/DisplayPowerController.java
+++ b/services/core/java/com/android/server/display/DisplayPowerController.java
@@ -710,9 +710,9 @@
}
}
- private void initialize() {
+ private void initialize(int displayState) {
mPowerState = new DisplayPowerState(mBlanker,
- mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId);
+ mColorFadeEnabled ? new ColorFade(mDisplayId) : null, mDisplayId, displayState);
if (mColorFadeEnabled) {
mColorFadeOnAnimator = ObjectAnimator.ofFloat(
@@ -812,11 +812,6 @@
mustNotify = !mDisplayReadyLocked;
}
- // Initialize things the first time the power state is changed.
- if (mustInitialize) {
- initialize();
- }
-
// Compute the basic display state using the policy.
// We might override this below based on other factors.
// Initialise brightness as invalid.
@@ -850,6 +845,11 @@
}
assert(state != Display.STATE_UNKNOWN);
+ // Initialize things the first time the power state is changed.
+ if (mustInitialize) {
+ initialize(state);
+ }
+
// Apply the proximity sensor.
if (mProximitySensor != null) {
if (mPowerRequest.useProximitySensor && state != Display.STATE_OFF) {
diff --git a/services/core/java/com/android/server/display/DisplayPowerState.java b/services/core/java/com/android/server/display/DisplayPowerState.java
index 54f30a9..173adce 100644
--- a/services/core/java/com/android/server/display/DisplayPowerState.java
+++ b/services/core/java/com/android/server/display/DisplayPowerState.java
@@ -72,7 +72,8 @@
private Runnable mCleanListener;
- public DisplayPowerState(DisplayBlanker blanker, ColorFade colorFade, int displayId) {
+ DisplayPowerState(
+ DisplayBlanker blanker, ColorFade colorFade, int displayId, int displayState) {
mHandler = new Handler(true /*async*/);
mChoreographer = Choreographer.getInstance();
mBlanker = blanker;
@@ -81,14 +82,14 @@
mPhotonicModulator.start();
mDisplayId = displayId;
- // At boot time, we know that the screen is on and the electron beam
- // animation is not playing. We don't know the screen's brightness though,
+ // At boot time, we don't know the screen's brightness,
// so prepare to set it to a known state when the state is next applied.
- // Although we set the brightness to full on here, the display power controller
+ // Although we set the brightness here, the display power controller
// will reset the brightness to a new level immediately before the changes
// actually have a chance to be applied.
- mScreenState = Display.STATE_ON;
- mScreenBrightness = PowerManager.BRIGHTNESS_MAX;
+ mScreenState = displayState;
+ mScreenBrightness = (displayState != Display.STATE_OFF) ? PowerManager.BRIGHTNESS_MAX
+ : PowerManager.BRIGHTNESS_OFF_FLOAT;
scheduleScreenUpdate();
mColorFadePrepared = false;
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
index 5b3db01..5f7d938 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerService.java
@@ -63,7 +63,7 @@
@Override
public FontConfig getFontConfig() throws RemoteException {
- return getCurrentFontSettings().getSystemFontConfig();
+ return getSystemFontConfig();
}
/* package */ static class SystemFontException extends AndroidException {
@@ -103,7 +103,7 @@
if (!Typeface.ENABLE_LAZY_TYPEFACE_INITIALIZATION) {
return null;
}
- return mService.getCurrentFontSettings().getSerializedSystemFontMap();
+ return mService.getCurrentFontMap();
}
});
publishBinderService(Context.FONT_SERVICE, mService);
@@ -162,7 +162,7 @@
@GuardedBy("FontManagerService.this")
@Nullable
- private SystemFontSettings mCurrentFontSettings = null;
+ private SharedMemory mSerializedFontMap = null;
private FontManagerService(Context context) {
mContext = context;
@@ -188,12 +188,12 @@
return mContext;
}
- @NonNull /* package */ SystemFontSettings getCurrentFontSettings() {
+ @NonNull /* package */ SharedMemory getCurrentFontMap() {
synchronized (FontManagerService.this) {
- if (mCurrentFontSettings == null) {
- mCurrentFontSettings = SystemFontSettings.create(mUpdatableFontDir);
+ if (mSerializedFontMap == null) {
+ mSerializedFontMap = buildNewSerializedFontMap();
}
- return mCurrentFontSettings;
+ return mSerializedFontMap;
}
}
@@ -207,7 +207,7 @@
synchronized (FontManagerService.this) {
mUpdatableFontDir.installFontFile(fd, pkcs7Signature);
// Create updated font map in the next getSerializedSystemFontMap() call.
- mCurrentFontSettings = null;
+ mSerializedFontMap = null;
}
}
@@ -245,69 +245,44 @@
new FontManagerShellCommand(this).exec(this, in, out, err, args, callback, result);
}
- /* package */ static class SystemFontSettings {
- private final @NonNull SharedMemory mSerializedSystemFontMap;
- private final @NonNull FontConfig mSystemFontConfig;
- private final @NonNull Map<String, FontFamily[]> mSystemFallbackMap;
- private final @NonNull Map<String, Typeface> mSystemTypefaceMap;
+ /**
+ * Returns an active system font configuration.
+ */
+ public @NonNull FontConfig getSystemFontConfig() {
+ if (mUpdatableFontDir != null) {
+ return mUpdatableFontDir.getSystemFontConfig();
+ } else {
+ return SystemFonts.getSystemPreinstalledFontConfig();
+ }
+ }
- SystemFontSettings(
- @NonNull SharedMemory serializedSystemFontMap,
- @NonNull FontConfig systemFontConfig,
- @NonNull Map<String, FontFamily[]> systemFallbackMap,
- @NonNull Map<String, Typeface> systemTypefaceMap) {
- mSerializedSystemFontMap = serializedSystemFontMap;
- mSystemFontConfig = systemFontConfig;
- mSystemFallbackMap = systemFallbackMap;
- mSystemTypefaceMap = systemTypefaceMap;
+ /**
+ * Make new serialized font map data.
+ */
+ public @Nullable SharedMemory buildNewSerializedFontMap() {
+ try {
+ final FontConfig fontConfig = getSystemFontConfig();
+ final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
+ final Map<String, Typeface> typefaceMap =
+ SystemFonts.buildSystemTypefaces(fontConfig, fallback);
+
+ return Typeface.serializeFontMap(typefaceMap);
+ } catch (IOException | ErrnoException e) {
+ Slog.w(TAG, "Failed to serialize updatable font map. "
+ + "Retrying with system image fonts.", e);
}
- public @NonNull SharedMemory getSerializedSystemFontMap() {
- return mSerializedSystemFontMap;
- }
-
- public @NonNull FontConfig getSystemFontConfig() {
- return mSystemFontConfig;
- }
-
- public @NonNull Map<String, FontFamily[]> getSystemFallbackMap() {
- return mSystemFallbackMap;
- }
-
- public @NonNull Map<String, Typeface> getSystemTypefaceMap() {
- return mSystemTypefaceMap;
- }
-
- public static @Nullable SystemFontSettings create(
- @Nullable UpdatableFontDir updatableFontDir) {
- if (updatableFontDir != null) {
- final FontConfig fontConfig = updatableFontDir.getSystemFontConfig();
- final Map<String, FontFamily[]> fallback =
- SystemFonts.buildSystemFallback(fontConfig);
- final Map<String, Typeface> typefaceMap =
- SystemFonts.buildSystemTypefaces(fontConfig, fallback);
-
- try {
- final SharedMemory shm = Typeface.serializeFontMap(typefaceMap);
- return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap);
- } catch (IOException | ErrnoException e) {
- Slog.w(TAG, "Failed to serialize updatable font map. "
- + "Retrying with system image fonts.", e);
- }
- }
-
+ try {
final FontConfig fontConfig = SystemFonts.getSystemPreinstalledFontConfig();
final Map<String, FontFamily[]> fallback = SystemFonts.buildSystemFallback(fontConfig);
final Map<String, Typeface> typefaceMap =
SystemFonts.buildSystemTypefaces(fontConfig, fallback);
- try {
- final SharedMemory shm = Typeface.serializeFontMap(typefaceMap);
- return new SystemFontSettings(shm, fontConfig, fallback, typefaceMap);
- } catch (IOException | ErrnoException e) {
- Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
- }
- return null;
+
+ return Typeface.serializeFontMap(typefaceMap);
+ } catch (IOException | ErrnoException e) {
+ Slog.e(TAG, "Failed to serialize SystemServer system font map", e);
}
+ return null;
}
}
diff --git a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
index fd5c020..5a01a97 100644
--- a/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
+++ b/services/core/java/com/android/server/graphics/fonts/FontManagerShellCommand.java
@@ -25,6 +25,7 @@
import android.graphics.fonts.FontFamily;
import android.graphics.fonts.FontManager;
import android.graphics.fonts.FontVariationAxis;
+import android.graphics.fonts.SystemFonts;
import android.os.Binder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
@@ -95,8 +96,8 @@
}
/* package */ void dumpAll(@NonNull IndentingPrintWriter w) {
- final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
- dumpFontConfig(w, settings.getSystemFontConfig());
+ FontConfig fontConfig = mService.getSystemFontConfig();
+ dumpFontConfig(w, fontConfig);
}
private void dumpSingleFontConfig(
@@ -276,19 +277,19 @@
private int dump(ShellCommand shell) {
final Context ctx = mService.getContext();
- final FontManagerService.SystemFontSettings settings =
- mService.getCurrentFontSettings();
+
if (!DumpUtils.checkDumpPermission(ctx, TAG, shell.getErrPrintWriter())) {
return 1;
}
final IndentingPrintWriter writer =
new IndentingPrintWriter(shell.getOutPrintWriter(), " ");
String nextArg = shell.getNextArg();
+ FontConfig fontConfig = mService.getSystemFontConfig();
if (nextArg == null) {
- dumpFontConfig(writer, settings.getSystemFontConfig());
+ dumpFontConfig(writer, fontConfig);
} else {
final Map<String, FontFamily[]> fallbackMap =
- settings.getSystemFallbackMap();
+ SystemFonts.buildSystemFallback(fontConfig);
FontFamily[] families = fallbackMap.get(nextArg);
if (families == null) {
writer.println("Font Family \"" + nextArg + "\" not found");
@@ -364,10 +365,9 @@
}
private int status(ShellCommand shell) throws SystemFontException {
- final FontManagerService.SystemFontSettings settings = mService.getCurrentFontSettings();
final IndentingPrintWriter writer =
new IndentingPrintWriter(shell.getOutPrintWriter(), " ");
- FontConfig config = settings.getSystemFontConfig();
+ FontConfig config = mService.getSystemFontConfig();
writer.println("Current Version: " + config.getConfigVersion());
LocalDateTime dt = LocalDateTime.ofEpochSecond(config.getLastModifiedDate(), 0,
diff --git a/services/core/java/com/android/server/location/LocationManagerService.java b/services/core/java/com/android/server/location/LocationManagerService.java
index b5b93d6..142f64f 100644
--- a/services/core/java/com/android/server/location/LocationManagerService.java
+++ b/services/core/java/com/android/server/location/LocationManagerService.java
@@ -74,7 +74,6 @@
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.os.UserHandle;
import android.os.WorkSource;
import android.os.WorkSource.WorkChain;
@@ -1297,10 +1296,7 @@
return null;
}
- long currentNanos = SystemClock.elapsedRealtimeNanos();
- long deltaMs = NANOSECONDS.toMillis(
- location.getElapsedRealtimeAgeNanos(currentNanos));
- return new LocationTime(location.getTime() + deltaMs, currentNanos);
+ return new LocationTime(location.getTime(), location.getElapsedRealtimeNanos());
}
}
diff --git a/services/core/java/com/android/server/pm/PackageInstallerSession.java b/services/core/java/com/android/server/pm/PackageInstallerSession.java
index e218dc1..4eaac2e4 100644
--- a/services/core/java/com/android/server/pm/PackageInstallerSession.java
+++ b/services/core/java/com/android/server/pm/PackageInstallerSession.java
@@ -70,7 +70,6 @@
import android.content.pm.IPackageInstallObserver2;
import android.content.pm.IPackageInstallerSession;
import android.content.pm.IPackageInstallerSessionFileSystemConnector;
-import android.content.pm.IPackageLoadingProgressCallback;
import android.content.pm.InstallationFile;
import android.content.pm.InstallationFileParcel;
import android.content.pm.PackageInfo;
@@ -322,8 +321,6 @@
private float mProgress = 0;
@GuardedBy("mLock")
private float mReportedProgress = -1;
- @GuardedBy("mLock")
- private float mIncrementalProgress = 0;
/** State of the session. */
@GuardedBy("mLock")
@@ -1202,12 +1199,7 @@
@GuardedBy("mLock")
private void computeProgressLocked(boolean forcePublish) {
- // This method is triggered when the client progress is updated or the incremental progress
- // is updated. For incremental installs, ignore the progress values reported from client.
- // Instead, only use the progress reported by IncFs as the percentage of loading completion.
- final float loadingProgress =
- isIncrementalInstallation() ? mIncrementalProgress : mClientProgress;
- mProgress = MathUtils.constrain(loadingProgress * 0.8f, 0f, 0.8f)
+ mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
+ MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
// Only publish when meaningful change
@@ -3767,16 +3759,7 @@
try {
mIncrementalFileStorages = IncrementalFileStorages.initialize(mContext, stageDir,
params, statusListener, healthCheckParams, healthListener, addedFiles,
- perUidReadTimeouts,
- new IPackageLoadingProgressCallback.Stub() {
- @Override
- public void onPackageLoadingProgressChanged(float progress) {
- synchronized (mLock) {
- mIncrementalProgress = progress;
- computeProgressLocked(true);
- }
- }
- });
+ perUidReadTimeouts);
return false;
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_MEDIA_UNAVAILABLE, e.getMessage(),
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index c27e670..d2fc5b4 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -2153,6 +2153,10 @@
void enforceCrossUserPermission(int callingUid, @UserIdInt int userId,
boolean requireFullPermission, boolean checkShell,
boolean requirePermissionWhenSameUser, String message);
+ SigningDetails getSigningDetails(@NonNull String packageName);
+ SigningDetails getSigningDetails(int uid);
+ boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId);
+ boolean filterAppAccess(String packageName, int callingUid, int userId);
}
/**
@@ -4578,6 +4582,40 @@
throw new SecurityException(errorMessage);
}
+ public SigningDetails getSigningDetails(@NonNull String packageName) {
+ AndroidPackage p = mPackages.get(packageName);
+ if (p == null) {
+ return null;
+ }
+ return p.getSigningDetails();
+ }
+
+ public SigningDetails getSigningDetails(int uid) {
+ final int appId = UserHandle.getAppId(uid);
+ final Object obj = mSettings.getSettingLPr(appId);
+ if (obj != null) {
+ if (obj instanceof SharedUserSetting) {
+ return ((SharedUserSetting) obj).signatures.mSigningDetails;
+ } else if (obj instanceof PackageSetting) {
+ final PackageSetting ps = (PackageSetting) obj;
+ return ps.signatures.mSigningDetails;
+ }
+ }
+ return SigningDetails.UNKNOWN;
+ }
+
+ public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+ PackageSetting ps = getPackageSetting(pkg.getPackageName());
+ return shouldFilterApplicationLocked(ps, callingUid,
+ userId);
+ }
+
+ public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ PackageSetting ps = getPackageSetting(packageName);
+ return shouldFilterApplicationLocked(ps, callingUid,
+ userId);
+ }
+
}
/**
@@ -4728,6 +4766,26 @@
return super.getPackageUidInternal(packageName, flags, userId, callingUid);
}
}
+ public SigningDetails getSigningDetails(@NonNull String packageName) {
+ synchronized (mLock) {
+ return super.getSigningDetails(packageName);
+ }
+ }
+ public SigningDetails getSigningDetails(int uid) {
+ synchronized (mLock) {
+ return super.getSigningDetails(uid);
+ }
+ }
+ public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+ synchronized (mLock) {
+ return super.filterAppAccess(pkg, callingUid, userId);
+ }
+ }
+ public boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ synchronized (mLock) {
+ return super.filterAppAccess(packageName, callingUid, userId);
+ }
+ }
}
@@ -26560,6 +26618,22 @@
return snapshotComputer().getPackage(uid);
}
+ private SigningDetails getSigningDetails(@NonNull String packageName) {
+ return snapshotComputer().getSigningDetails(packageName);
+ }
+
+ private SigningDetails getSigningDetails(int uid) {
+ return snapshotComputer().getSigningDetails(uid);
+ }
+
+ private boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
+ return snapshotComputer().filterAppAccess(pkg, callingUid, userId);
+ }
+
+ private boolean filterAppAccess(String packageName, int callingUid, int userId) {
+ return snapshotComputer().filterAppAccess(packageName, callingUid, userId);
+ }
+
private class PackageManagerInternalImpl extends PackageManagerInternal {
@Override
public List<ApplicationInfo> getInstalledApplications(int flags, int userId,
@@ -26615,29 +26689,11 @@
}
private SigningDetails getSigningDetails(@NonNull String packageName) {
- synchronized (mLock) {
- AndroidPackage p = mPackages.get(packageName);
- if (p == null) {
- return null;
- }
- return p.getSigningDetails();
- }
+ return PackageManagerService.this.getSigningDetails(packageName);
}
private SigningDetails getSigningDetails(int uid) {
- synchronized (mLock) {
- final int appId = UserHandle.getAppId(uid);
- final Object obj = mSettings.getSettingLPr(appId);
- if (obj != null) {
- if (obj instanceof SharedUserSetting) {
- return ((SharedUserSetting) obj).signatures.mSigningDetails;
- } else if (obj instanceof PackageSetting) {
- final PackageSetting ps = (PackageSetting) obj;
- return ps.signatures.mSigningDetails;
- }
- }
- return SigningDetails.UNKNOWN;
- }
+ return PackageManagerService.this.getSigningDetails(uid);
}
@Override
@@ -26652,20 +26708,12 @@
@Override
public boolean filterAppAccess(AndroidPackage pkg, int callingUid, int userId) {
- synchronized (mLock) {
- PackageSetting ps = getPackageSetting(pkg.getPackageName());
- return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
- userId);
- }
+ return PackageManagerService.this.filterAppAccess(pkg, callingUid, userId);
}
@Override
public boolean filterAppAccess(String packageName, int callingUid, int userId) {
- synchronized (mLock) {
- PackageSetting ps = getPackageSetting(packageName);
- return PackageManagerService.this.shouldFilterApplicationLocked(ps, callingUid,
- userId);
- }
+ return PackageManagerService.this.filterAppAccess(packageName, callingUid, userId);
}
@Override
@@ -28304,6 +28352,13 @@
}
continue;
}
+ if (ps.appId < Process.FIRST_APPLICATION_UID) {
+ if (DEBUG_PER_UID_READ_TIMEOUTS) {
+ Slog.i(TAG, "PerUidReadTimeouts: package is system, appId=" + ps.appId);
+ }
+ continue;
+ }
+
final AndroidPackage pkg = ps.getPkg();
if (pkg.getLongVersionCode() < perPackage.versionCodes.minVersionCode
|| pkg.getLongVersionCode() > perPackage.versionCodes.maxVersionCode) {
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 8c31d88..aff87111 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -3356,11 +3356,12 @@
// - or its signing certificate was rotated from the source package's certificate
// - or its signing certificate is a previous signing certificate of the defining
// package, and the defining package still trusts the old certificate for permissions
+ // - or it shares a common signing certificate in its lineage with the defining package,
+ // and the defining package still trusts the old certificate for permissions
// - or it shares the above relationships with the system package
final PackageParser.SigningDetails sourceSigningDetails =
getSourcePackageSigningDetails(bp);
- return pkg.getSigningDetails().hasAncestorOrSelf(sourceSigningDetails)
- || sourceSigningDetails.checkCapability(
+ return sourceSigningDetails.hasCommonSignerWithCapability(
pkg.getSigningDetails(),
PackageParser.SigningDetails.CertCapabilities.PERMISSION)
|| pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails())
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index c073b43..89e7986 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3491,15 +3491,27 @@
/** {@inheritDoc} */
@Override
public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
+ final int keyCode = event.getKeyCode();
+ final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
+ boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
+ || event.isWakeKey();
+
if (!mSystemBooted) {
// If we have not yet booted, don't let key events do anything.
+ // Exception: Wake and power key events are forwarded to PowerManager to allow it to
+ // wake from quiescent mode during boot.
+ if (down && (keyCode == KeyEvent.KEYCODE_POWER
+ || keyCode == KeyEvent.KEYCODE_TV_POWER)) {
+ wakeUpFromPowerKey(event.getDownTime());
+ } else if (down && (isWakeKey || keyCode == KeyEvent.KEYCODE_WAKEUP)
+ && isWakeKeyWhenScreenOff(keyCode)) {
+ wakeUpFromWakeKey(event);
+ }
return 0;
}
final boolean interactive = (policyFlags & FLAG_INTERACTIVE) != 0;
- final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;
final boolean canceled = event.isCanceled();
- final int keyCode = event.getKeyCode();
final int displayId = event.getDisplayId();
final boolean isInjected = (policyFlags & WindowManagerPolicy.FLAG_INJECTED) != 0;
@@ -3518,8 +3530,6 @@
// Basic policy based on interactive state.
int result;
- boolean isWakeKey = (policyFlags & WindowManagerPolicy.FLAG_WAKE) != 0
- || event.isWakeKey();
if (interactive || (isInjected && !isWakeKey)) {
// When the device is interactive or the key is injected pass the
// key to the application.
@@ -4740,7 +4750,7 @@
}
startedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
finishedWakingUp(PowerManager.WAKE_REASON_UNKNOWN);
- screenTurningOn(DEFAULT_DISPLAY, null);
+ screenTurningOn(DEFAULT_DISPLAY, mDefaultDisplayPolicy.getScreenOnListener());
screenTurnedOn(DEFAULT_DISPLAY);
}
diff --git a/services/core/java/com/android/server/power/PowerManagerService.java b/services/core/java/com/android/server/power/PowerManagerService.java
index 54d0512..db4b6d0 100644
--- a/services/core/java/com/android/server/power/PowerManagerService.java
+++ b/services/core/java/com/android/server/power/PowerManagerService.java
@@ -1036,12 +1036,12 @@
userActivityNoUpdateLocked(
now, PowerManager.USER_ACTIVITY_EVENT_OTHER, 0, Process.SYSTEM_UID);
+ updatePowerStateLocked();
if (sQuiescent) {
goToSleepNoUpdateLocked(mClock.uptimeMillis(),
PowerManager.GO_TO_SLEEP_REASON_QUIESCENT,
PowerManager.GO_TO_SLEEP_FLAG_NO_DOZE, Process.SYSTEM_UID);
}
- updatePowerStateLocked();
}
}
}
@@ -1679,8 +1679,15 @@
Slog.d(TAG, "wakeUpNoUpdateLocked: eventTime=" + eventTime + ", uid=" + reasonUid);
}
- if (eventTime < mLastSleepTime || getWakefulnessLocked() == WAKEFULNESS_AWAKE
- || mForceSuspendActive || !mSystemReady) {
+ if (eventTime < mLastSleepTime || mForceSuspendActive || !mSystemReady) {
+ return false;
+ }
+
+ if (getWakefulnessLocked() == WAKEFULNESS_AWAKE) {
+ if (!mBootCompleted && sQuiescent) {
+ mDirty |= DIRTY_QUIESCENT;
+ return true;
+ }
return false;
}
@@ -2821,7 +2828,7 @@
*
* This function recalculates the display power state each time.
*
- * @return True if the display became ready.
+ * @return true if the display became ready.
*/
private boolean updateDisplayPowerStateLocked(int dirty) {
final boolean oldDisplayReady = mDisplayReady;
@@ -2830,7 +2837,11 @@
| DIRTY_SETTINGS | DIRTY_SCREEN_BRIGHTNESS_BOOST | DIRTY_VR_MODE_CHANGED |
DIRTY_QUIESCENT)) != 0) {
if ((dirty & DIRTY_QUIESCENT) != 0) {
- sQuiescent = false;
+ if (mDisplayReady) {
+ sQuiescent = false;
+ } else {
+ mDirty |= DIRTY_QUIESCENT;
+ }
}
final DisplayPowerRequest displayPowerRequest = mDisplayPowerRequestMapper.get(
@@ -5605,7 +5616,7 @@
* ignore the proximity sensor. We don't turn off the proximity sensor because
* we still want it to be reenabled if it's state changes.
*
- * @return True if the proximity sensor was successfully ignored and we should
+ * @return true if the proximity sensor was successfully ignored and we should
* consume the key event.
*/
private boolean interceptPowerKeyDownInternal(KeyEvent event) {
diff --git a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
index 3e39b4b..eb9df75 100644
--- a/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
+++ b/services/core/java/com/android/server/powerstats/PowerStatsHALWrapper.java
@@ -295,14 +295,14 @@
@Override
public android.hardware.power.stats.EnergyConsumer[] getEnergyConsumerInfo() {
if (DEBUG) Slog.d(TAG, "Energy consumer info is not supported");
- return null;
+ return new android.hardware.power.stats.EnergyConsumer[0];
}
@Override
public android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(
int[] energyConsumerIds) {
if (DEBUG) Slog.d(TAG, "Energy consumer results are not supported");
- return null;
+ return new android.hardware.power.stats.EnergyConsumerResult[0];
}
@Override
diff --git a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
index 43f8a3a..57e39b6 100644
--- a/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
+++ b/services/core/java/com/android/server/rotationresolver/RemoteRotationResolverService.java
@@ -21,6 +21,9 @@
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED;
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_TIMED_OUT;
+import static com.android.server.rotationresolver.RotationResolverManagerService.RESOLUTION_FAILURE;
+import static com.android.server.rotationresolver.RotationResolverManagerService.logRotationStats;
+
import android.annotation.NonNull;
import android.content.ComponentName;
import android.content.Context;
@@ -29,6 +32,7 @@
import android.os.Handler;
import android.os.ICancellationSignal;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.rotationresolver.RotationResolverInternal;
import android.service.rotationresolver.IRotationResolverCallback;
import android.service.rotationresolver.IRotationResolverService;
@@ -112,6 +116,7 @@
boolean mIsDispatched;
private final Object mLock = new Object();
+ private final long mRequestStartTimeMillis;
RotationRequest(
@NonNull RotationResolverInternal.RotationResolverCallbackInternal
@@ -125,6 +130,7 @@
mPackageName = packageName;
mIRotationResolverCallback = new RotationResolverCallback();
mCancellationSignalInternal = cancellationSignal;
+ mRequestStartTimeMillis = SystemClock.elapsedRealtime();
}
@@ -164,7 +170,10 @@
}
mIsFulfilled = true;
mCallbackInternal.onSuccess(rotation);
- logStats(rotation);
+ final long timeToCalculate =
+ SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
+ logRotationStats(mProposedRotation, mCurrentRotation, rotation,
+ timeToCalculate);
}
}
@@ -177,7 +186,10 @@
}
mIsFulfilled = true;
mCallbackInternal.onFailure(error);
- logStats(error);
+ final long timeToCalculate =
+ SystemClock.elapsedRealtime() - mRequestStartTimeMillis;
+ logRotationStats(mProposedRotation, mCurrentRotation, RESOLUTION_FAILURE,
+ timeToCalculate);
}
}
@@ -196,10 +208,6 @@
}
}
-
- private void logStats(int result) {
- // TODO FrameworkStatsLog
- }
}
}
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
index f0e2d79..8a1c778 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerPerUserService.java
@@ -18,7 +18,9 @@
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED;
+import static com.android.server.rotationresolver.RotationResolverManagerService.RESOLUTION_UNAVAILABLE;
import static com.android.server.rotationresolver.RotationResolverManagerService.getServiceConfigPackage;
+import static com.android.server.rotationresolver.RotationResolverManagerService.logRotationStats;
import android.Manifest;
import android.annotation.NonNull;
@@ -98,6 +100,7 @@
if (!isServiceAvailableLocked()) {
Slog.w(TAG, "Service is not available at this moment.");
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
+ logRotationStats(proposedRotation, currentRotation, RESOLUTION_UNAVAILABLE);
return;
}
diff --git a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
index 0377d23..4a37e79 100644
--- a/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
+++ b/services/core/java/com/android/server/rotationresolver/RotationResolverManagerService.java
@@ -19,6 +19,11 @@
import static android.provider.DeviceConfig.NAMESPACE_ROTATION_RESOLVER;
import static android.service.rotationresolver.RotationResolverService.ROTATION_RESULT_FAILURE_CANCELLED;
+import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0;
+import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_180;
+import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_270;
+import static com.android.internal.util.FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_90;
+
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.UserIdInt;
@@ -33,9 +38,11 @@
import android.text.TextUtils;
import android.util.IndentingPrintWriter;
import android.util.Slog;
+import android.view.Surface;
import com.android.internal.R;
import com.android.internal.util.DumpUtils;
+import com.android.internal.util.FrameworkStatsLog;
import com.android.server.SystemService;
import com.android.server.infra.AbstractMasterSystemService;
import com.android.server.infra.FrameworkResourcesServiceNameResolver;
@@ -61,6 +68,15 @@
/** Default value in absence of {@link DeviceConfig} override. */
private static final boolean DEFAULT_SERVICE_ENABLED = false;
+ static final int ORIENTATION_UNKNOWN =
+ FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNKNOWN;
+ static final int RESOLUTION_DISABLED =
+ FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__DISABLED;
+ static final int RESOLUTION_UNAVAILABLE =
+ FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__UNAVAILABLE;
+ static final int RESOLUTION_FAILURE =
+ FrameworkStatsLog.AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__FAILURE;
+
private final Context mContext;
boolean mIsServiceEnabled;
@@ -147,6 +163,7 @@
} else {
Slog.w(TAG, "Rotation Resolver service is disabled.");
callbackInternal.onFailure(ROTATION_RESULT_FAILURE_CANCELLED);
+ logRotationStats(proposedRotation, currentRotation, RESOLUTION_DISABLED);
}
}
}
@@ -178,4 +195,36 @@
resultReceiver);
}
}
+
+ static void logRotationStats(int proposedRotation, int currentRotation,
+ int resolvedRotation, long timeToCalculate) {
+ FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED,
+ /* previous_orientation= */ surfaceRotationToProto(currentRotation),
+ /* proposed_orientation= */ surfaceRotationToProto(proposedRotation),
+ /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation),
+ /* process_duration_millis= */ timeToCalculate);
+ }
+
+ static void logRotationStats(int proposedRotation, int currentRotation,
+ int resolvedRotation) {
+ FrameworkStatsLog.write(FrameworkStatsLog.AUTO_ROTATE_REPORTED,
+ /* previous_orientation= */ surfaceRotationToProto(currentRotation),
+ /* proposed_orientation= */ surfaceRotationToProto(proposedRotation),
+ /* resolved_orientation= */ surfaceRotationToProto(resolvedRotation));
+ }
+
+ private static int surfaceRotationToProto(@Surface.Rotation int rotationPoseResult) {
+ switch (rotationPoseResult) {
+ case Surface.ROTATION_0:
+ return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_0;
+ case Surface.ROTATION_90:
+ return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_90;
+ case Surface.ROTATION_180:
+ return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_180;
+ case Surface.ROTATION_270:
+ return AUTO_ROTATE_REPORTED__PROPOSED_ORIENTATION__ROTATION_270;
+ default:
+ return ORIENTATION_UNKNOWN;
+ }
+ }
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 509cbde..3bb4c74 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -1355,11 +1355,13 @@
final boolean surfaceReady = w.isDrawn() // Regular case
|| w.mWinAnimator.mSurfaceDestroyDeferred // The preserved surface is still ready.
|| w.isDragResizeChanged(); // Waiting for relayoutWindow to call preserveSurface.
- final boolean needsLetterbox = surfaceReady && w.isLetterboxedAppWindow() && fillsParent();
+ final boolean needsLetterbox = surfaceReady && isLetterboxed(w);
+ updateRoundedCorners(w);
if (needsLetterbox) {
if (mLetterbox == null) {
mLetterbox = new Letterbox(() -> makeChildSurface(null),
- mWmService.mTransactionFactory);
+ mWmService.mTransactionFactory,
+ mWmService::isLetterboxActivityCornersRounded);
mLetterbox.attachInput(w);
}
getPosition(mTmpPoint);
@@ -1371,7 +1373,7 @@
final Rect spaceToFill = transformedBounds != null
? transformedBounds
: inMultiWindowMode()
- ? task.getBounds()
+ ? getRootTask().getBounds()
: getRootTask().getParent().getBounds();
mLetterbox.layout(spaceToFill, w.getFrame(), mTmpPoint);
} else if (mLetterbox != null) {
@@ -1379,6 +1381,27 @@
}
}
+ /** @return {@code true} when main window is letterboxed and activity isn't transparent. */
+ private boolean isLetterboxed(WindowState mainWindow) {
+ return mainWindow.isLetterboxedAppWindow() && fillsParent();
+ }
+
+ private void updateRoundedCorners(WindowState mainWindow) {
+ int cornersRadius =
+ // Don't round corners if letterboxed only for display cutout.
+ isLetterboxed(mainWindow) && !mainWindow.isLetterboxedForDisplayCutout()
+ ? Math.max(0, mWmService.getLetterboxActivityCornersRadius()) : 0;
+ setCornersRadius(mainWindow, cornersRadius);
+ }
+
+ private void setCornersRadius(WindowState mainWindow, int cornersRadius) {
+ final SurfaceControl windowSurface = mainWindow.getClientViewRootSurface();
+ if (windowSurface != null && windowSurface.isValid()) {
+ Transaction transaction = getPendingTransaction();
+ transaction.setCornerRadius(windowSurface, cornersRadius);
+ }
+ }
+
void updateLetterboxSurface(WindowState winHint) {
final WindowState w = findMainWindow();
if (w != winHint && winHint != null && w != null) {
@@ -1408,10 +1431,14 @@
}
/**
- * @see Letterbox#notIntersectsOrFullyContains(Rect)
+ * @return {@code true} if bar shown within a given rectangle is allowed to be transparent
+ * when the current activity is displayed.
*/
- boolean letterboxNotIntersectsOrFullyContains(Rect rect) {
- return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect);
+ boolean isTransparentBarAllowed(Rect rect) {
+ // TODO(b/175482966): Allow status and navigation bars to be semi-transparent black
+ // in letterbox mode.
+ return mLetterbox == null || mLetterbox.notIntersectsOrFullyContains(rect)
+ || mWmService.isLetterboxActivityCornersRounded();
}
/**
@@ -6589,8 +6616,7 @@
// which point, the activity type is still undefined if it will be standard.
// For other non-standard types, the type is set in the constructor, so this should
// not be a problem.
- && isActivityTypeStandardOrUndefined()
- && !mAtmService.mForceResizableActivities;
+ && isActivityTypeStandardOrUndefined();
}
@Override
diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
index f0db3f9..404773d 100644
--- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
+++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
@@ -35,6 +35,7 @@
import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.ActivityTaskManager.RESIZE_MODE_PRESERVE_WINDOW;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_DREAM;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -1877,6 +1878,12 @@
synchronized (mGlobalLock) {
final long ident = Binder.clearCallingIdentity();
try {
+ if (isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) {
+ Slog.w(TAG, "setTaskWindowingMode: Is in lock task mode="
+ + getLockTaskModeState());
+ return false;
+ }
+
if (WindowConfiguration.isSplitScreenWindowingMode(windowingMode)) {
return setTaskWindowingModeSplitScreen(taskId, windowingMode, toTop);
}
@@ -2141,11 +2148,6 @@
throw new IllegalArgumentException("Calling setTaskWindowingModeSplitScreen with non"
+ "split-screen mode: " + windowingMode);
}
- if (isInLockTaskMode()) {
- Slog.w(TAG, "setTaskWindowingModeSplitScreen: Is in lock task mode="
- + getLockTaskModeState());
- return false;
- }
final Task task = mRootWindowContainer.anyTaskForId(taskId,
MATCH_ATTACHED_TASK_ONLY);
diff --git a/services/core/java/com/android/server/wm/BarController.java b/services/core/java/com/android/server/wm/BarController.java
index 4a90bbc..eee27c7 100644
--- a/services/core/java/com/android/server/wm/BarController.java
+++ b/services/core/java/com/android/server/wm/BarController.java
@@ -56,6 +56,6 @@
if (win == null) {
return true;
}
- return win.letterboxNotIntersectsOrFullyContains(getContentFrame(win));
+ return win.isTransparentBarAllowed(getContentFrame(win));
}
}
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index 2a40500..0aaa1a1 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -190,6 +190,7 @@
import android.util.proto.ProtoOutputStream;
import android.view.Display;
import android.view.DisplayCutout;
+import android.view.DisplayCutout.CutoutPathParserInfo;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.IDisplayWindowInsetsController;
@@ -1934,18 +1935,22 @@
if (cutout == null || cutout == DisplayCutout.NO_CUTOUT) {
return WmDisplayCutout.NO_CUTOUT;
}
- final Insets waterfallInsets =
- RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
if (rotation == ROTATION_0) {
return WmDisplayCutout.computeSafeInsets(
cutout, mInitialDisplayWidth, mInitialDisplayHeight);
}
+ final Insets waterfallInsets =
+ RotationUtils.rotateInsets(cutout.getWaterfallInsets(), rotation);
final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);
final Rect[] newBounds = mRotationUtil.getRotatedBounds(
cutout.getBoundingRectsAll(),
rotation, mInitialDisplayWidth, mInitialDisplayHeight);
+ final CutoutPathParserInfo info = cutout.getCutoutPathParserInfo();
+ final CutoutPathParserInfo newInfo = new CutoutPathParserInfo(
+ info.getDisplayWidth(), info.getDisplayHeight(), info.getDensity(),
+ info.getCutoutSpec(), rotation, info.getScale());
return WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(newBounds, waterfallInsets),
+ DisplayCutout.constructDisplayCutout(newBounds, waterfallInsets, newInfo),
rotated ? mInitialDisplayHeight : mInitialDisplayWidth,
rotated ? mInitialDisplayWidth : mInitialDisplayHeight);
}
@@ -3604,7 +3609,7 @@
&& mImeLayeringTarget.mActivityRecord.matchParentBounds()
// IME is attached to non-Letterboxed app windows, other than windows with
// LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER flag. (Refer to WS.isLetterboxedAppWindow())
- && mImeLayeringTarget.matchesRootDisplayAreaBounds();
+ && mImeLayeringTarget.matchesDisplayAreaBounds();
}
/**
@@ -4102,8 +4107,15 @@
* Callbacks when the given type of {@link WindowContainer} animation finished running in the
* hierarchy.
*/
- void onWindowAnimationFinished(int type) {
+ void onWindowAnimationFinished(@NonNull WindowContainer wc, int type) {
if (type == ANIMATION_TYPE_APP_TRANSITION || type == ANIMATION_TYPE_RECENTS) {
+ // Unfreeze the insets state of the frozen target when the animation finished if exists.
+ final Task task = wc.asTask();
+ if (task != null) {
+ task.forAllWindows(w -> {
+ w.clearFrozenInsetsState();
+ }, true /* traverseTopToBottom */);
+ }
removeImeSurfaceImmediately();
}
}
diff --git a/services/core/java/com/android/server/wm/InsetsStateController.java b/services/core/java/com/android/server/wm/InsetsStateController.java
index 398049f..267f677 100644
--- a/services/core/java/com/android/server/wm/InsetsStateController.java
+++ b/services/core/java/com/android/server/wm/InsetsStateController.java
@@ -74,7 +74,7 @@
private final ArraySet<InsetsControlTarget> mPendingControlChanged = new ArraySet<>();
private final Consumer<WindowState> mDispatchInsetsChanged = w -> {
- if (w.isVisible()) {
+ if (w.isReadyToDispatchInsetsState()) {
w.notifyInsetsChanged();
}
};
@@ -117,7 +117,8 @@
final @InternalInsetsType int type = provider != null
? provider.getSource().getType() : ITYPE_INVALID;
return getInsetsForTarget(type, target.getWindowingMode(), target.isAlwaysOnTop(),
- isAboveIme(target));
+ isAboveIme(target),
+ target.getFrozenInsetsState() != null ? target.getFrozenInsetsState() : mState);
}
InsetsState getInsetsForWindowMetrics(@NonNull WindowManager.LayoutParams attrs) {
@@ -132,7 +133,7 @@
final @WindowingMode int windowingMode = token != null
? token.getWindowingMode() : WINDOWING_MODE_UNDEFINED;
final boolean alwaysOnTop = token != null && token.isAlwaysOnTop();
- return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token));
+ return getInsetsForTarget(type, windowingMode, alwaysOnTop, isAboveIme(token), mState);
}
private boolean isAboveIme(WindowContainer target) {
@@ -180,9 +181,8 @@
* @see #getInsetsForWindowMetrics
*/
private InsetsState getInsetsForTarget(@InternalInsetsType int type,
- @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme) {
- InsetsState state = mState;
-
+ @WindowingMode int windowingMode, boolean isAlwaysOnTop, boolean aboveIme,
+ @NonNull InsetsState state) {
if (type != ITYPE_INVALID) {
state = new InsetsState(state);
state.removeSource(type);
diff --git a/services/core/java/com/android/server/wm/Letterbox.java b/services/core/java/com/android/server/wm/Letterbox.java
index 44ce4de..02a43b7 100644
--- a/services/core/java/com/android/server/wm/Letterbox.java
+++ b/services/core/java/com/android/server/wm/Letterbox.java
@@ -44,12 +44,17 @@
private final Supplier<SurfaceControl.Builder> mSurfaceControlFactory;
private final Supplier<SurfaceControl.Transaction> mTransactionFactory;
+ private final Supplier<Boolean> mAreCornersRounded;
private final Rect mOuter = new Rect();
private final Rect mInner = new Rect();
private final LetterboxSurface mTop = new LetterboxSurface("top");
private final LetterboxSurface mLeft = new LetterboxSurface("left");
private final LetterboxSurface mBottom = new LetterboxSurface("bottom");
private final LetterboxSurface mRight = new LetterboxSurface("right");
+ // Prevents wallpaper from peeking through near rounded corners. It's not included in
+ // mSurfaces array since it isn't needed in methods like notIntersectsOrFullyContains
+ // or attachInput.
+ private final LetterboxSurface mBehind = new LetterboxSurface("behind");
private final LetterboxSurface[] mSurfaces = { mLeft, mTop, mRight, mBottom };
/**
@@ -58,9 +63,11 @@
* @param surfaceControlFactory a factory for creating the managed {@link SurfaceControl}s
*/
public Letterbox(Supplier<SurfaceControl.Builder> surfaceControlFactory,
- Supplier<SurfaceControl.Transaction> transactionFactory) {
+ Supplier<SurfaceControl.Transaction> transactionFactory,
+ Supplier<Boolean> areCornersRounded) {
mSurfaceControlFactory = surfaceControlFactory;
mTransactionFactory = transactionFactory;
+ mAreCornersRounded = areCornersRounded;
}
/**
@@ -82,6 +89,7 @@
mLeft.layout(outer.left, outer.top, inner.left, outer.bottom, surfaceOrigin);
mBottom.layout(outer.left, inner.bottom, outer.right, outer.bottom, surfaceOrigin);
mRight.layout(inner.right, outer.top, outer.right, outer.bottom, surfaceOrigin);
+ mBehind.layout(inner.left, inner.top, inner.right, inner.bottom, surfaceOrigin);
}
@@ -157,6 +165,7 @@
for (LetterboxSurface surface : mSurfaces) {
surface.remove();
}
+ mBehind.remove();
}
/** Returns whether a call to {@link #applySurfaceChanges} would change the surface. */
@@ -166,6 +175,9 @@
return true;
}
}
+ if (mBehind.needsApplySurfaceChanges()) {
+ return true;
+ }
return false;
}
@@ -173,6 +185,11 @@
for (LetterboxSurface surface : mSurfaces) {
surface.applySurfaceChanges(t);
}
+ if (mAreCornersRounded.get()) {
+ mBehind.applySurfaceChanges(t);
+ } else {
+ mBehind.remove();
+ }
}
/** Enables touches to slide into other neighboring surfaces. */
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index ec1588d..6a3110f 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -35,6 +35,7 @@
import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED;
import static android.app.WindowConfiguration.activityTypeToString;
+import static android.app.WindowConfiguration.isSplitScreenWindowingMode;
import static android.app.WindowConfiguration.windowingModeToString;
import static android.content.Intent.FLAG_ACTIVITY_NEW_DOCUMENT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
@@ -2859,41 +2860,61 @@
adjustForMinimalTaskDimensions(outOverrideBounds, previousBounds, newParentConfig);
if (windowingMode == WINDOWING_MODE_FREEFORM) {
- // by policy, make sure the window remains within parent somewhere
- final float density =
- ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
- final Rect parentBounds =
- new Rect(newParentConfig.windowConfiguration.getBounds());
- final DisplayContent display = getDisplayContent();
- if (display != null) {
- // If a freeform window moves below system bar, there is no way to move it again
- // by touch. Because its caption is covered by system bar. So we exclude them
- // from root task bounds. and then caption will be shown inside stable area.
- final Rect stableBounds = new Rect();
- display.getStableRect(stableBounds);
- parentBounds.intersect(stableBounds);
- }
+ computeFreeformBounds(outOverrideBounds, newParentConfig);
+ return;
+ }
- fitWithinBounds(outOverrideBounds, parentBounds,
- (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
- (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+ if (isSplitScreenWindowingMode(windowingMode)
+ || windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
+ // This is to compute whether the task should be letterboxed to handle non-resizable app
+ // in multi window. There is no split screen only logic.
+ computeLetterboxBounds(outOverrideBounds, newParentConfig);
+ }
+ }
- // Prevent to overlap caption with stable insets.
- final int offsetTop = parentBounds.top - outOverrideBounds.top;
- if (offsetTop > 0) {
- outOverrideBounds.offset(0, offsetTop);
- }
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN}. */
+ @VisibleForTesting
+ void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
+ // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
+ outBounds.setEmpty();
+ computeLetterboxBounds(outBounds, newParentConfig);
+ }
+
+ /** Computes bounds for {@link WindowConfiguration#WINDOWING_MODE_FREEFORM}. */
+ private void computeFreeformBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
+ // by policy, make sure the window remains within parent somewhere
+ final float density =
+ ((float) newParentConfig.densityDpi) / DisplayMetrics.DENSITY_DEFAULT;
+ final Rect parentBounds =
+ new Rect(newParentConfig.windowConfiguration.getBounds());
+ final DisplayContent display = getDisplayContent();
+ if (display != null) {
+ // If a freeform window moves below system bar, there is no way to move it again
+ // by touch. Because its caption is covered by system bar. So we exclude them
+ // from root task bounds. and then caption will be shown inside stable area.
+ final Rect stableBounds = new Rect();
+ display.getStableRect(stableBounds);
+ parentBounds.intersect(stableBounds);
+ }
+
+ fitWithinBounds(outBounds, parentBounds,
+ (int) (density * WindowState.MINIMUM_VISIBLE_WIDTH_IN_DP),
+ (int) (density * WindowState.MINIMUM_VISIBLE_HEIGHT_IN_DP));
+
+ // Prevent to overlap caption with stable insets.
+ final int offsetTop = parentBounds.top - outBounds.top;
+ if (offsetTop > 0) {
+ outBounds.offset(0, offsetTop);
}
}
/**
- * Compute bounds (letterbox or pillarbox) for
- * {@link WindowConfiguration#WINDOWING_MODE_FULLSCREEN} when the parent doesn't handle the
- * orientation change and the requested orientation is different from the parent.
+ * Computes bounds (letterbox or pillarbox) when the parent doesn't handle the orientation
+ * change and the requested orientation is different from the parent.
*/
- void computeFullscreenBounds(@NonNull Rect outBounds, @NonNull Configuration newParentConfig) {
- // In FULLSCREEN mode, always start with empty bounds to indicate "fill parent".
- outBounds.setEmpty();
+ private void computeLetterboxBounds(@NonNull Rect outBounds,
+ @NonNull Configuration newParentConfig) {
if (handlesOrientationChangeFromDescendant()) {
// No need to letterbox at task level. Display will handle fixed-orientation requests.
return;
@@ -2951,6 +2972,8 @@
aspect = letterboxAspectRatioOverride > MIN_TASK_LETTERBOX_ASPECT_RATIO
? letterboxAspectRatioOverride : aspect;
+ // Store the current bounds to be able to revert to size compat mode values below if needed.
+ mTmpFullBounds.set(outBounds);
if (forcedOrientation == ORIENTATION_LANDSCAPE) {
final int height = (int) Math.rint(parentWidth / aspect);
final int top = parentBounds.centerY() - height / 2;
@@ -2969,7 +2992,7 @@
// The app shouldn't be resized, we only do task letterboxing if the compat bounds
// is also from the same task letterbox. Otherwise, clear the task bounds to show
// app in size compat mode.
- outBounds.setEmpty();
+ outBounds.set(mTmpFullBounds);
}
}
}
@@ -3355,8 +3378,9 @@
@Override
boolean handlesOrientationChangeFromDescendant() {
return super.handlesOrientationChangeFromDescendant()
- // Display won't rotate for the orientation request if the TaskDisplayArea can't
- // specify orientation.
+ // Display won't rotate for the orientation request if the Task/TaskDisplayArea
+ // can't specify orientation.
+ && canSpecifyOrientation()
&& getDisplayArea().canSpecifyOrientation();
}
@@ -3869,7 +3893,9 @@
}
boolean isTaskLetterboxed() {
- return getWindowingMode() == WINDOWING_MODE_FULLSCREEN && !matchParentBounds();
+ // No letterbox for multi window root task
+ return !matchParentBounds()
+ && (getWindowingMode() == WINDOWING_MODE_FULLSCREEN || !isRootTask());
}
@Override
@@ -6041,7 +6067,9 @@
mInResumeTopActivity = true;
if (isLeafTask()) {
- someActivityResumed = resumeTopActivityInnerLocked(prev, options);
+ if (isFocusableAndVisible()) {
+ someActivityResumed = resumeTopActivityInnerLocked(prev, options);
+ }
} else {
int idx = mChildren.size() - 1;
while (idx >= 0) {
diff --git a/services/core/java/com/android/server/wm/WindowContainer.java b/services/core/java/com/android/server/wm/WindowContainer.java
index 03fca11..dd4ee877 100644
--- a/services/core/java/com/android/server/wm/WindowContainer.java
+++ b/services/core/java/com/android/server/wm/WindowContainer.java
@@ -2684,6 +2684,14 @@
@Nullable ArrayList<WindowContainer> sources) {
final Task task = asTask();
if (task != null && !enter && !task.isHomeOrRecentsRootTask()) {
+ if (AppTransition.isClosingTransitOld(transit)) {
+ // Freezes the insets state when the window is in app exiting transition, to
+ // ensure the exiting window won't receive unexpected insets changes from the
+ // next window.
+ task.forAllWindows(w -> {
+ w.freezeInsetsState();
+ }, true /* traverseTopToBottom */);
+ }
mDisplayContent.showImeScreenshot();
}
final Pair<AnimationAdapter, AnimationAdapter> adapters = getAnimationAdapter(lp,
@@ -2831,7 +2839,7 @@
}
mSurfaceAnimationSources.clear();
if (mDisplayContent != null) {
- mDisplayContent.onWindowAnimationFinished(type);
+ mDisplayContent.onWindowAnimationFinished(this, type);
}
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index b6fabee3..8e6a778 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -1012,6 +1012,9 @@
// ignored.
private float mTaskLetterboxAspectRatio;
+ // Corners radius for activities presented in the letterbox mode, values < 0 will be ignored.
+ private int mLetterboxActivityCornersRadius;
+
final InputManagerService mInputManager;
final DisplayManagerInternal mDisplayManagerInternal;
final DisplayManager mDisplayManager;
@@ -1239,6 +1242,8 @@
com.android.internal.R.bool.config_assistantOnTopOfDream);
mTaskLetterboxAspectRatio = context.getResources().getFloat(
com.android.internal.R.dimen.config_taskLetterboxAspectRatio);
+ mLetterboxActivityCornersRadius = context.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
mInputManager = inputManager; // Must be before createDisplayContentLocked.
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
@@ -3936,6 +3941,60 @@
}
}
+ /**
+ * Overrides corners raidus for activities presented in the letterbox mode. If given value < 0,
+ * both it and a value of {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius} will be ignored and
+ * and corners of the activity won't be rounded.
+ */
+ void setLetterboxActivityCornersRadius(int cornersRadius) {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mLetterboxActivityCornersRadius = cornersRadius;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
+ * Resets corners raidus for activities presented in the letterbox mode to {@link
+ * com.android.internal.R.integer.config_letterboxActivityCornersRadius}.
+ */
+ void resetLetterboxActivityCornersRadius() {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ mLetterboxActivityCornersRadius = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_letterboxActivityCornersRadius);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
+ /**
+ * Whether corners of letterboxed activities are rounded.
+ */
+ boolean isLetterboxActivityCornersRounded() {
+ return getLetterboxActivityCornersRadius() > 0;
+ }
+
+ /**
+ * Gets corners raidus for activities presented in the letterbox mode.
+ */
+ int getLetterboxActivityCornersRadius() {
+ final long origId = Binder.clearCallingIdentity();
+ try {
+ synchronized (mGlobalLock) {
+ return mLetterboxActivityCornersRadius;
+ }
+ } finally {
+ Binder.restoreCallingIdentity(origId);
+ }
+ }
+
@Override
public void setIgnoreOrientationRequest(int displayId, boolean ignoreOrientationRequest) {
mAtmInternal.enforceCallerIsRecentsOrHasPermission(
diff --git a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
index a3a9c1c..badd29a 100644
--- a/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
+++ b/services/core/java/com/android/server/wm/WindowManagerShellCommand.java
@@ -115,6 +115,10 @@
return runSetTaskLetterboxAspectRatio(pw);
case "get-task-letterbox-aspect-ratio":
return runGetTaskLetterboxAspectRatio(pw);
+ case "set-letterbox-activity-corners-radius":
+ return runSetLetterboxActivityCornersRadius(pw);
+ case "get-letterbox-activity-corners-radius":
+ return runGetLetterboxActivityCornersRadius(pw);
case "reset":
return runReset(pw);
default:
@@ -545,6 +549,38 @@
return 0;
}
+ private int runSetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+ final int cornersRadius;
+ try {
+ String arg = getNextArgRequired();
+ if ("reset".equals(arg)) {
+ mInternal.resetLetterboxActivityCornersRadius();
+ return 0;
+ }
+ cornersRadius = Integer.parseInt(arg);
+ } catch (NumberFormatException e) {
+ getErrPrintWriter().println("Error: bad corners radius format " + e);
+ return -1;
+ } catch (IllegalArgumentException e) {
+ getErrPrintWriter().println(
+ "Error: 'reset' or corners radius should be provided as an argument " + e);
+ return -1;
+ }
+
+ mInternal.setLetterboxActivityCornersRadius(cornersRadius);
+ return 0;
+ }
+
+ private int runGetLetterboxActivityCornersRadius(PrintWriter pw) throws RemoteException {
+ final int cornersRadius = mInternal.getLetterboxActivityCornersRadius();
+ if (cornersRadius < 0) {
+ pw.println("Letterbox corners radius is not set");
+ } else {
+ pw.println("Letterbox corners radius is " + cornersRadius);
+ }
+ return 0;
+ }
+
private int runReset(PrintWriter pw) throws RemoteException {
int displayId = getDisplayId(getNextArg());
@@ -572,6 +608,9 @@
// set-task-letterbox-aspect-ratio
mInternal.resetTaskLetterboxAspectRatio();
+ // set-letterbox-activity-corners-radius
+ mInternal.resetLetterboxActivityCornersRadius();
+
pw.println("Reset all settings for displayId=" + displayId);
return 0;
}
@@ -608,6 +647,11 @@
+ WindowManagerService.MIN_TASK_LETTERBOX_ASPECT_RATIO);
pw.println(" both it and R.dimen.config_taskLetterboxAspectRatio will be ignored");
pw.println(" and framework implementation will be used to determine aspect ratio.");
+ pw.println(" set-letterbox-activity-corners-radius [reset|cornersRadius]");
+ pw.println(" get-letterbox-activity-corners-radius");
+ pw.println(" Corners radius for activities in the letterbox mode. If radius < 0,");
+ pw.println(" both it and R.integer.config_letterboxActivityCornersRadius will be");
+ pw.println(" ignored and corners of the activity won't be rounded.");
pw.println(" reset [-d DISPLAY_ID]");
pw.println(" Reset all override settings.");
if (!IS_USER) {
diff --git a/services/core/java/com/android/server/wm/WindowOrganizerController.java b/services/core/java/com/android/server/wm/WindowOrganizerController.java
index 043844b..1b81914 100644
--- a/services/core/java/com/android/server/wm/WindowOrganizerController.java
+++ b/services/core/java/com/android/server/wm/WindowOrganizerController.java
@@ -17,6 +17,7 @@
package com.android.server.wm;
import static android.Manifest.permission.READ_FRAME_BUFFER;
+import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER;
import static android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REPARENT;
@@ -264,57 +265,63 @@
}
// Hierarchy changes
final List<WindowContainerTransaction.HierarchyOp> hops = t.getHierarchyOps();
- for (int i = 0, n = hops.size(); i < n; ++i) {
- final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
- switch (hop.getType()) {
- case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
- final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
- final Task task = wc != null ? wc.asTask() : null;
- if (task != null) {
- task.getDisplayArea().setLaunchRootTask(task,
- hop.getWindowingModes(), hop.getActivityTypes());
- } else {
- throw new IllegalArgumentException(
- "Cannot set non-task as launch root: " + wc);
+ if (!hops.isEmpty() && mService.isInLockTaskMode()) {
+ Slog.w(TAG, "Attempt to perform hierarchy operations while in lock task mode...");
+ } else {
+ for (int i = 0, n = hops.size(); i < n; ++i) {
+ final WindowContainerTransaction.HierarchyOp hop = hops.get(i);
+ switch (hop.getType()) {
+ case HIERARCHY_OP_TYPE_SET_LAUNCH_ROOT: {
+ final WindowContainer wc = WindowContainer.fromBinder(
+ hop.getContainer());
+ final Task task = wc != null ? wc.asTask() : null;
+ if (task != null) {
+ task.getDisplayArea().setLaunchRootTask(task,
+ hop.getWindowingModes(), hop.getActivityTypes());
+ } else {
+ throw new IllegalArgumentException(
+ "Cannot set non-task as launch root: " + wc);
+ }
+ break;
}
- break;
- }
- case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
- effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
- break;
- case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
- effects |= setAdjacentRootsHierarchyOp(hop);
- break;
- case HIERARCHY_OP_TYPE_REORDER:
- case HIERARCHY_OP_TYPE_REPARENT:
- final WindowContainer wc = WindowContainer.fromBinder(hop.getContainer());
- if (wc == null || !wc.isAttached()) {
- Slog.e(TAG, "Attempt to operate on detached container: " + wc);
- continue;
- }
- if (syncId >= 0) {
- addToSyncSet(syncId, wc);
- }
- if (transition != null) {
- transition.collect(wc);
- if (hop.isReparent()) {
- if (wc.getParent() != null) {
- // Collect the current parent. It's visibility may change as
- // a result of this reparenting.
- transition.collect(wc.getParent());
- }
- if (hop.getNewParent() != null) {
- final WindowContainer parentWc =
- WindowContainer.fromBinder(hop.getNewParent());
- if (parentWc == null) {
- Slog.e(TAG, "Can't resolve parent window from token");
- continue;
+ case HIERARCHY_OP_TYPE_CHILDREN_TASKS_REPARENT:
+ effects |= reparentChildrenTasksHierarchyOp(hop, transition, syncId);
+ break;
+ case HIERARCHY_OP_TYPE_SET_ADJACENT_ROOTS:
+ effects |= setAdjacentRootsHierarchyOp(hop);
+ break;
+ case HIERARCHY_OP_TYPE_REORDER:
+ case HIERARCHY_OP_TYPE_REPARENT:
+ final WindowContainer wc = WindowContainer.fromBinder(
+ hop.getContainer());
+ if (wc == null || !wc.isAttached()) {
+ Slog.e(TAG, "Attempt to operate on detached container: " + wc);
+ continue;
+ }
+ if (syncId >= 0) {
+ addToSyncSet(syncId, wc);
+ }
+ if (transition != null) {
+ transition.collect(wc);
+ if (hop.isReparent()) {
+ if (wc.getParent() != null) {
+ // Collect the current parent. It's visibility may change as
+ // a result of this reparenting.
+ transition.collect(wc.getParent());
}
- transition.collect(parentWc);
+ if (hop.getNewParent() != null) {
+ final WindowContainer parentWc =
+ WindowContainer.fromBinder(hop.getNewParent());
+ if (parentWc == null) {
+ Slog.e(TAG, "Can't resolve parent window from token");
+ continue;
+ }
+ transition.collect(parentWc);
+ }
}
}
- }
- effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ effects |= sanitizeAndApplyHierarchyOp(wc, hop);
+ }
}
}
// Queue-up bounds-change transactions for tasks which are now organized. Do
@@ -412,6 +419,10 @@
}
if (windowingMode > -1) {
+ if (mService.isInLockTaskMode() && windowingMode != WINDOWING_MODE_FULLSCREEN) {
+ throw new UnsupportedOperationException("Not supported to set non-fullscreen"
+ + " windowing mode during locked task mode.");
+ }
container.setWindowingMode(windowingMode);
}
return effects;
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 093106f..9a7823e 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -713,6 +713,12 @@
private @Nullable InsetsSourceProvider mControllableInsetProvider;
private final InsetsState mRequestedInsetsState = new InsetsState();
+ /**
+ * Freeze the insets state in some cases that not necessarily keeps up-to-date to the client.
+ * (e.g app exiting transition)
+ */
+ private InsetsState mFrozenInsetsState;
+
@Nullable InsetsSourceProvider mPendingPositionChanged;
private static final float DEFAULT_DIM_AMOUNT_DEAD_WINDOW = 0.5f;
@@ -758,6 +764,33 @@
}
}
+ /**
+ * Set a freeze state for the window to ignore dispatching its insets state to the client.
+ *
+ * Used to keep the insets state for some use cases. (e.g. app exiting transition)
+ */
+ void freezeInsetsState() {
+ if (mFrozenInsetsState == null) {
+ mFrozenInsetsState = new InsetsState(getInsetsState(), true /* copySources */);
+ }
+ }
+
+ void clearFrozenInsetsState() {
+ mFrozenInsetsState = null;
+ }
+
+ InsetsState getFrozenInsetsState() {
+ return mFrozenInsetsState;
+ }
+
+ /**
+ * Check if the insets state of the window is ready to dispatch to the client when invoking
+ * {@link InsetsStateController#notifyInsetsChanged}.
+ */
+ boolean isReadyToDispatchInsetsState() {
+ return isVisible() && mFrozenInsetsState == null;
+ }
+
void seamlesslyRotateIfAllowed(Transaction transaction, @Rotation int oldRotation,
@Rotation int rotation, boolean requested) {
// Invisible windows and the wallpaper do not participate in the seamless rotation animation
@@ -2110,12 +2143,12 @@
return getDisplayContent().getBounds().equals(getBounds());
}
- boolean matchesRootDisplayAreaBounds() {
- RootDisplayArea root = getRootDisplayArea();
- if (root == null || root == getDisplayContent()) {
+ boolean matchesDisplayAreaBounds() {
+ final DisplayArea displayArea = getDisplayArea();
+ if (displayArea == null) {
return matchesDisplayBounds();
}
- return root.getBounds().equals(getBounds());
+ return displayArea.getBounds().equals(getBounds());
}
/**
@@ -3762,16 +3795,20 @@
return getDisplayContent().mCurrentFocus == this;
}
-
/** Is this window in a container that takes up the entire screen space? */
private boolean inAppWindowThatMatchesParentBounds() {
return mActivityRecord == null || (mActivityRecord.matchParentBounds() && !inMultiWindowMode());
}
- /** @return true when the window is in fullscreen mode, but has non-fullscreen bounds set, or
- * is transitioning into/out-of fullscreen. */
+ /** @return true when the window should be letterboxed. */
boolean isLetterboxedAppWindow() {
- return !inMultiWindowMode() && !matchesRootDisplayAreaBounds()
+ // Fullscreen mode but doesn't fill display area.
+ return (!inMultiWindowMode() && !matchesDisplayAreaBounds())
+ // Activity in size compat.
+ || (mActivityRecord != null && mActivityRecord.inSizeCompatMode())
+ // Task letterboxed.
+ || (getTask() != null && getTask().isTaskLetterboxed())
+ // Letterboxed for display cutout.
|| isLetterboxedForDisplayCutout();
}
@@ -3809,11 +3846,11 @@
}
/**
- * @see Letterbox#notIntersectsOrFullyContains(Rect)
+ * @return {@code true} if bar shown within a given frame is allowed to be transparent
+ * when the current window is displayed.
*/
- boolean letterboxNotIntersectsOrFullyContains(Rect rect) {
- return mActivityRecord == null
- || mActivityRecord.letterboxNotIntersectsOrFullyContains(rect);
+ boolean isTransparentBarAllowed(Rect frame) {
+ return mActivityRecord == null || mActivityRecord.isTransparentBarAllowed(frame);
}
public boolean isLetterboxedOverlappingWith(Rect rect) {
diff --git a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
index 1208354..3f54529 100644
--- a/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
+++ b/services/core/jni/com_android_server_powerstats_PowerStatsService.cpp
@@ -335,7 +335,8 @@
field_EM_timestampMs,
energyData[i].timestamp);
env->SetLongField(energyMeasurement,
- field_EM_durationMs, -1);
+ field_EM_durationMs,
+ energyData[i].timestamp);
env->SetLongField(energyMeasurement,
field_EM_energyUWs,
energyData[i].energy);
diff --git a/services/incremental/ServiceWrappers.cpp b/services/incremental/ServiceWrappers.cpp
index 6fabc58..dfa6083 100644
--- a/services/incremental/ServiceWrappers.cpp
+++ b/services/incremental/ServiceWrappers.cpp
@@ -210,7 +210,17 @@
ErrorCode setUidReadTimeouts(const Control& control,
const std::vector<android::os::incremental::PerUidReadTimeouts>&
perUidReadTimeouts) const final {
- return -ENOTSUP;
+ std::vector<incfs::UidReadTimeouts> timeouts;
+ timeouts.resize(perUidReadTimeouts.size());
+ for (int i = 0, size = perUidReadTimeouts.size(); i < size; ++i) {
+ auto&& timeout = timeouts[i];
+ const auto& perUidTimeout = perUidReadTimeouts[i];
+ timeout.uid = perUidTimeout.uid;
+ timeout.minTimeUs = perUidTimeout.minTimeUs;
+ timeout.minPendingTimeUs = perUidTimeout.minPendingTimeUs;
+ timeout.maxPendingTimeUs = perUidTimeout.maxPendingTimeUs;
+ }
+ return incfs::setUidReadTimeouts(control, timeouts);
}
};
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index fd8dfc3..d28c3cc 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -1705,15 +1705,6 @@
}
t.traceEnd();
- t.traceBegin("StartVcnManagementService");
- try {
- vcnManagement = VcnManagementService.create(context);
- ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
- } catch (Throwable e) {
- reportWtf("starting VCN Management Service", e);
- }
- t.traceEnd();
-
t.traceBegin("StartFontManagerService");
mSystemServiceManager.startService(FontManagerService.Lifecycle.class);
t.traceEnd();
@@ -1815,6 +1806,15 @@
networkPolicy.bindConnectivityManager(connectivity);
t.traceEnd();
+ t.traceBegin("StartVcnManagementService");
+ try {
+ vcnManagement = VcnManagementService.create(context);
+ ServiceManager.addService(Context.VCN_MANAGEMENT_SERVICE, vcnManagement);
+ } catch (Throwable e) {
+ reportWtf("starting VCN Management Service", e);
+ }
+ t.traceEnd();
+
t.traceBegin("StartNsdService");
try {
serviceDiscovery = NsdService.create(context);
@@ -2632,15 +2632,6 @@
reportWtf("making IpSec Service ready", e);
}
t.traceEnd();
- t.traceBegin("MakeVcnManagementServiceReady");
- try {
- if (vcnManagementF != null) {
- vcnManagementF.systemReady();
- }
- } catch (Throwable e) {
- reportWtf("making VcnManagementService ready", e);
- }
- t.traceEnd();
t.traceBegin("MakeNetworkStatsServiceReady");
try {
if (networkStatsF != null) {
@@ -2659,6 +2650,15 @@
reportWtf("making Connectivity Service ready", e);
}
t.traceEnd();
+ t.traceBegin("MakeVcnManagementServiceReady");
+ try {
+ if (vcnManagementF != null) {
+ vcnManagementF.systemReady();
+ }
+ } catch (Throwable e) {
+ reportWtf("making VcnManagementService ready", e);
+ }
+ t.traceEnd();
t.traceBegin("MakeNetworkPolicyServiceReady");
try {
if (networkPolicyF != null) {
diff --git a/services/people/java/com/android/server/people/PeopleService.java b/services/people/java/com/android/server/people/PeopleService.java
index 091e688..5453de1 100644
--- a/services/people/java/com/android/server/people/PeopleService.java
+++ b/services/people/java/com/android/server/people/PeopleService.java
@@ -45,7 +45,6 @@
import com.android.server.SystemService;
import com.android.server.people.data.DataManager;
-import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
@@ -156,6 +155,13 @@
final IBinder mService = new IPeopleManager.Stub() {
@Override
+ public ConversationChannel getConversation(
+ String packageName, int userId, String shortcutId) {
+ enforceSystemRootOrSystemUI(getContext(), "get conversation");
+ return mDataManager.getConversation(packageName, userId, shortcutId);
+ }
+
+ @Override
public ParceledListSlice<ConversationChannel> getRecentConversations() {
enforceSystemRootOrSystemUI(getContext(), "get recent conversations");
return new ParceledListSlice<>(
diff --git a/services/people/java/com/android/server/people/data/DataManager.java b/services/people/java/com/android/server/people/data/DataManager.java
index 7521415..9a9a171 100644
--- a/services/people/java/com/android/server/people/data/DataManager.java
+++ b/services/people/java/com/android/server/people/data/DataManager.java
@@ -222,33 +222,65 @@
mContext.getPackageName(), intentFilter, callingUserId);
}
+ /**
+ * Returns a {@link ConversationChannel} with the associated {@code shortcutId} if existent.
+ * Otherwise, returns null.
+ */
+ @Nullable
+ public ConversationChannel getConversation(String packageName, int userId, String shortcutId) {
+ UserData userData = getUnlockedUserData(userId);
+ if (userData != null) {
+ PackageData packageData = userData.getPackageData(packageName);
+ // App may have been uninstalled.
+ if (packageData != null) {
+ return getConversationChannel(packageData, shortcutId);
+ }
+ }
+ return null;
+ }
+
+ @Nullable
+ private ConversationChannel getConversationChannel(PackageData packageData, String shortcutId) {
+ ConversationInfo conversationInfo = packageData.getConversationInfo(shortcutId);
+ if (conversationInfo == null) {
+ return null;
+ }
+ int userId = packageData.getUserId();
+ String packageName = packageData.getPackageName();
+ ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
+ if (shortcutInfo == null) {
+ return null;
+ }
+ int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
+ NotificationChannel parentChannel =
+ mNotificationManagerInternal.getNotificationChannel(packageName, uid,
+ conversationInfo.getParentNotificationChannelId());
+ NotificationChannelGroup parentChannelGroup = null;
+ if (parentChannel != null) {
+ parentChannelGroup =
+ mNotificationManagerInternal.getNotificationChannelGroup(packageName,
+ uid, parentChannel.getId());
+ }
+ return new ConversationChannel(shortcutInfo, uid, parentChannel,
+ parentChannelGroup,
+ conversationInfo.getLastEventTimestamp(),
+ hasActiveNotifications(packageName, userId, shortcutId));
+ }
+
/** Returns the cached non-customized recent conversations. */
public List<ConversationChannel> getRecentConversations(@UserIdInt int callingUserId) {
List<ConversationChannel> conversationChannels = new ArrayList<>();
forPackagesInProfile(callingUserId, packageData -> {
- String packageName = packageData.getPackageName();
- int userId = packageData.getUserId();
packageData.forAllConversations(conversationInfo -> {
if (!isCachedRecentConversation(conversationInfo)) {
return;
}
String shortcutId = conversationInfo.getShortcutId();
- ShortcutInfo shortcutInfo = getShortcut(packageName, userId, shortcutId);
- int uid = mPackageManagerInternal.getPackageUid(packageName, 0, userId);
- NotificationChannel parentChannel =
- mNotificationManagerInternal.getNotificationChannel(packageName, uid,
- conversationInfo.getParentNotificationChannelId());
- if (shortcutInfo == null || parentChannel == null) {
+ ConversationChannel channel = getConversationChannel(packageData, shortcutId);
+ if (channel == null || channel.getParentNotificationChannel() == null) {
return;
}
- NotificationChannelGroup parentChannelGroup =
- mNotificationManagerInternal.getNotificationChannelGroup(packageName,
- uid, parentChannel.getId());
- conversationChannels.add(
- new ConversationChannel(shortcutInfo, uid, parentChannel,
- parentChannelGroup,
- conversationInfo.getLastEventTimestamp(),
- hasActiveNotifications(packageName, userId, shortcutId)));
+ conversationChannels.add(channel);
});
});
return conversationChannels;
diff --git a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
similarity index 79%
rename from services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java
rename to services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
index e5529cb..15a9bcf 100644
--- a/services/tests/servicestests/src/com/android/server/job/JobCountTrackerTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkCountTrackerTest.java
@@ -16,36 +16,42 @@
package com.android.server.job;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_NONE;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+
import static com.google.common.truth.Truth.assertThat;
import android.util.Log;
+import android.util.Pair;
-import com.android.server.job.JobConcurrencyManager.JobCountTracker;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.server.job.JobConcurrencyManager.WorkCountTracker;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
import java.util.Random;
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
/**
- * Test for {@link com.android.server.job.JobConcurrencyManager.JobCountTracker}.
+ * Test for {@link WorkCountTracker}.
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
-public class JobCountTrackerTest {
- private static final String TAG = "JobCountTrackerTest";
+public class WorkCountTrackerTest {
+ private static final String TAG = "WorkerCountTrackerTest";
private Random mRandom;
- private JobCountTracker mJobCountTracker;
+ private WorkCountTracker mWorkCountTracker;
@Before
public void setUp() {
mRandom = new Random(1); // Always use the same series of pseudo random values.
- mJobCountTracker = new JobCountTracker();
+ mWorkCountTracker = new WorkCountTracker();
}
/**
@@ -83,44 +89,57 @@
private void startPendingJobs(Jobs jobs, int totalMax, int maxBg, int minBg) {
- mJobCountTracker.reset(totalMax, maxBg, minBg);
+ mWorkCountTracker.setConfig(new JobConcurrencyManager.WorkTypeConfig("critical",
+ totalMax,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, totalMax - maxBg),
+ Pair.create(WORK_TYPE_BG, minBg)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, maxBg))));
+ mWorkCountTracker.resetCounts();
for (int i = 0; i < jobs.runningFg; i++) {
- mJobCountTracker.incrementRunningJobCount(true);
+ mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_TOP);
}
for (int i = 0; i < jobs.runningBg; i++) {
- mJobCountTracker.incrementRunningJobCount(false);
+ mWorkCountTracker.incrementRunningJobCount(WORK_TYPE_BG);
}
for (int i = 0; i < jobs.pendingFg; i++) {
- mJobCountTracker.incrementPendingJobCount(true);
+ mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_TOP);
}
for (int i = 0; i < jobs.pendingBg; i++) {
- mJobCountTracker.incrementPendingJobCount(false);
+ mWorkCountTracker.incrementPendingJobCount(WORK_TYPE_BG);
}
- mJobCountTracker.onCountDone();
+ mWorkCountTracker.onCountDone();
- while ((jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true))
- || (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false))) {
+ while ((jobs.pendingFg > 0
+ && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE)
+ || (jobs.pendingBg > 0
+ && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE)) {
final boolean isStartingFg = mRandom.nextBoolean();
if (isStartingFg) {
- if (jobs.pendingFg > 0 && mJobCountTracker.canJobStart(true)) {
+ if (jobs.pendingFg > 0
+ && mWorkCountTracker.canJobStart(WORK_TYPE_TOP) != WORK_TYPE_NONE) {
jobs.pendingFg--;
jobs.runningFg++;
- mJobCountTracker.onStartingNewJob(true);
+ mWorkCountTracker.stageJob(WORK_TYPE_TOP);
+ mWorkCountTracker.onJobStarted(WORK_TYPE_TOP);
}
} else {
- if (jobs.pendingBg > 0 && mJobCountTracker.canJobStart(false)) {
+ if (jobs.pendingBg > 0
+ && mWorkCountTracker.canJobStart(WORK_TYPE_BG) != WORK_TYPE_NONE) {
jobs.pendingBg--;
jobs.runningBg++;
- mJobCountTracker.onStartingNewJob(false);
+ mWorkCountTracker.stageJob(WORK_TYPE_BG);
+ mWorkCountTracker.onJobStarted(WORK_TYPE_BG);
}
}
}
- Log.i(TAG, "" + mJobCountTracker);
+ Log.i(TAG, "" + mWorkCountTracker);
}
/**
@@ -277,6 +296,7 @@
startPendingJobs(jobs, totalMax, maxBg, minBg);
+// fail(mWorkerCountTracker.toString());
assertThat(jobs.runningFg).isEqualTo(resultRunningFg);
assertThat(jobs.runningBg).isEqualTo(resultRunningBg);
@@ -300,6 +320,8 @@
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 1, /*res run/pen=*/ 5, 1, 5, 0);
checkSimple(6, 4, 2, /*run=*/ 0, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 4, 2, 6, 1);
+ checkSimple(8, 6, 2, /*run=*/ 0, 0, /*pen=*/ 0, 49, /*res run/pen=*/ 0, 6, 0, 43);
+
checkSimple(6, 4, 2, /*run=*/ 6, 0, /*pen=*/ 10, 3, /*res run/pen=*/ 6, 0, 10, 3);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
similarity index 63%
rename from services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
rename to services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
index 4c36747..fba36cb 100644
--- a/services/tests/servicestests/src/com/android/server/job/MaxJobCountsTest.java
+++ b/services/tests/servicestests/src/com/android/server/job/WorkTypeConfigTest.java
@@ -15,22 +15,34 @@
*/
package com.android.server.job;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_BG;
+import static com.android.server.job.JobConcurrencyManager.WORK_TYPE_TOP;
+
import android.annotation.Nullable;
import android.provider.DeviceConfig;
+import android.util.Pair;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;
-import com.android.server.job.JobSchedulerService.MaxJobCounts;
+import com.android.server.job.JobConcurrencyManager.WorkTypeConfig;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.util.List;
+
@RunWith(AndroidJUnit4.class)
@SmallTest
-public class MaxJobCountsTest {
+public class WorkTypeConfigTest {
+ private static final String KEY_MAX_TOTAL = "concurrency_max_total_test";
+ private static final String KEY_MAX_TOP = "concurrency_max_top_test";
+ private static final String KEY_MAX_BG = "concurrency_max_bg_test";
+ private static final String KEY_MIN_TOP = "concurrency_min_top_test";
+ private static final String KEY_MIN_BG = "concurrency_min_bg_test";
+
@After
public void tearDown() throws Exception {
resetConfig();
@@ -38,9 +50,11 @@
private void resetConfig() {
// DeviceConfig.resetToDefaults() doesn't work here. Need to reset constants manually.
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "total", "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "maxbg", "", false);
- DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, "minbg", "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOTAL, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MAX_BG, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_TOP, "", false);
+ DeviceConfig.setProperty(DeviceConfig.NAMESPACE_JOB_SCHEDULER, KEY_MIN_BG, "", false);
}
private void check(@Nullable DeviceConfig.Properties config,
@@ -51,16 +65,19 @@
DeviceConfig.setProperties(config);
}
- final MaxJobCounts counts = new JobSchedulerService.MaxJobCounts(
- defaultTotal, "total",
- defaultMaxBg, "maxbg",
- defaultMinBg, "minbg");
+ final WorkTypeConfig counts = new WorkTypeConfig("test",
+ defaultTotal,
+ // defaultMin
+ List.of(Pair.create(WORK_TYPE_TOP, defaultTotal - defaultMaxBg),
+ Pair.create(WORK_TYPE_BG, defaultMinBg)),
+ // defaultMax
+ List.of(Pair.create(WORK_TYPE_BG, defaultMaxBg)));
- counts.update();
+ counts.update(DeviceConfig.getProperties(DeviceConfig.NAMESPACE_JOB_SCHEDULER));
Assert.assertEquals(expectedTotal, counts.getMaxTotal());
- Assert.assertEquals(expectedMaxBg, counts.getMaxBg());
- Assert.assertEquals(expectedMinBg, counts.getMinBg());
+ Assert.assertEquals(expectedMaxBg, counts.getMax(WORK_TYPE_BG));
+ Assert.assertEquals(expectedMinBg, counts.getMinReserved(WORK_TYPE_BG));
}
@Test
@@ -80,19 +97,19 @@
// Test for overriding with a setting string.
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
- .setInt("total", 5)
- .setInt("maxbg", 4)
- .setInt("minbg", 3)
+ .setInt(KEY_MAX_TOTAL, 5)
+ .setInt(KEY_MAX_BG, 4)
+ .setInt(KEY_MIN_BG, 3)
.build(),
/*default*/ 9, 9, 9, /*expected*/ 5, 4, 3);
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
- .setInt("total", 5).build(),
+ .setInt(KEY_MAX_TOTAL, 5).build(),
/*default*/ 9, 9, 9, /*expected*/ 5, 5, 4);
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
- .setInt("maxbg", 4).build(),
+ .setInt(KEY_MAX_BG, 4).build(),
/*default*/ 9, 9, 9, /*expected*/ 9, 4, 4);
check(new DeviceConfig.Properties.Builder(DeviceConfig.NAMESPACE_JOB_SCHEDULER)
- .setInt("minbg", 3).build(),
+ .setInt(KEY_MIN_BG, 3).build(),
/*default*/ 9, 9, 9, /*expected*/ 9, 9, 3);
}
}
diff --git a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
index 63330d5..161d316 100644
--- a/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
+++ b/services/tests/servicestests/src/com/android/server/people/data/DataManagerTest.java
@@ -515,6 +515,85 @@
}
@Test
+ public void testGetConversationReturnsCustomizedConversation() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+
+ listenerService.onNotificationPosted(mStatusBarNotification);
+ shortcut.setCached(ShortcutInfo.FLAG_CACHED_NOTIFICATIONS);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNotNull();
+
+ listenerService.onNotificationChannelModified(TEST_PKG_NAME, UserHandle.of(USER_ID_PRIMARY),
+ mNotificationChannel, NOTIFICATION_CHANNEL_OR_GROUP_UPDATED);
+
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNotNull();
+ }
+
+ @Test
+ public void testGetConversation() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNull();
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID)).isNotNull();
+ assertThat(mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID + "1")).isNull();
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+ assertThat(result).isNotNull();
+ assertEquals(shortcut.getId(), result.getShortcutInfo().getId());
+ assertEquals(1, result.getShortcutInfo().getPersons().length);
+ assertEquals(CONTACT_URI, result.getShortcutInfo().getPersons()[0].getUri());
+ assertEquals(mParentNotificationChannel.getId(),
+ result.getParentNotificationChannel().getId());
+ assertEquals(mStatusBarNotification.getPostTime(), result.getLastEventTimestamp());
+ assertTrue(result.hasActiveNotifications());
+ }
+
+ @Test
+ public void testGetConversationGetsPersonsData() {
+ mDataManager.onUserUnlocked(USER_ID_PRIMARY);
+
+ ShortcutInfo shortcut = buildShortcutInfo(TEST_PKG_NAME, USER_ID_PRIMARY, TEST_SHORTCUT_ID,
+ buildPerson());
+ shortcut.setCached(ShortcutInfo.FLAG_PINNED);
+ mDataManager.addOrUpdateConversationInfo(shortcut);
+
+ NotificationListenerService listenerService =
+ mDataManager.getNotificationListenerServiceForTesting(USER_ID_PRIMARY);
+ listenerService.onNotificationPosted(mStatusBarNotification);
+
+ ConversationChannel result = mDataManager.getConversation(TEST_PKG_NAME, USER_ID_PRIMARY,
+ TEST_SHORTCUT_ID);
+
+ verify(mShortcutServiceInternal).getShortcuts(
+ anyInt(), anyString(), anyLong(), anyString(), anyList(), any(), any(),
+ mQueryFlagsCaptor.capture(), anyInt(), anyInt(), anyInt());
+ Integer queryFlags = mQueryFlagsCaptor.getValue();
+ assertThat(hasFlag(queryFlags, ShortcutQuery.FLAG_GET_PERSONS_DATA)).isTrue();
+ }
+
+ @Test
public void testNotificationChannelCreated() {
mDataManager.onUserUnlocked(USER_ID_PRIMARY);
mDataManager.onUserUnlocked(USER_ID_SECONDARY);
diff --git a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
index 533dc17..1d0b595 100644
--- a/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/power/PowerManagerServiceTest.java
@@ -877,6 +877,22 @@
}
@Test
+ public void testQuiescentBoot_WakeKeyBeforeBootCompleted_AwakeAfterBootCompleted()
+ throws Exception {
+ when(mSystemPropertiesMock.get(eq(SYSTEM_PROPERTY_QUIESCENT), any())).thenReturn("1");
+ createService();
+ mService.systemReady(null);
+
+ mService.getBinderServiceInstance().wakeUp(mClock.now(),
+ PowerManager.WAKE_REASON_UNKNOWN, "testing IPowerManager.wakeUp()", "pkg.name");
+
+ mService.onBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+ assertThat(mService.getWakefulnessLocked()).isEqualTo(WAKEFULNESS_AWAKE);
+ assertThat(mService.getDesiredScreenPolicyLocked()).isEqualTo(
+ DisplayPowerRequest.POLICY_BRIGHT);
+ }
+
+ @Test
public void testIsAmbientDisplayAvailable_available() throws Exception {
createService();
when(mAmbientDisplayConfigurationMock.ambientDisplayAvailable()).thenReturn(true);
diff --git a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
index 83282a5..4bea9a2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DisplayContentTests.java
@@ -27,7 +27,6 @@
import static android.os.Build.VERSION_CODES.Q;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.FLAG_PRIVATE;
-import static android.view.DisplayCutout.BOUNDS_POSITION_LEFT;
import static android.view.DisplayCutout.BOUNDS_POSITION_TOP;
import static android.view.DisplayCutout.fromBoundingRect;
import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
@@ -96,6 +95,7 @@
import android.app.WindowConfiguration;
import android.app.servertransaction.FixedRotationAdjustmentsItem;
import android.content.res.Configuration;
+import android.graphics.Insets;
import android.graphics.Rect;
import android.graphics.Region;
import android.metrics.LogMaker;
@@ -558,7 +558,7 @@
// hence isLetterboxedAppWindow() returns true.
ws.mActivityRecord.getConfiguration().windowConfiguration.setBounds(new Rect(1, 1, 1, 1));
assertFalse("matchesRootDisplayAreaBounds() should return false",
- ws.matchesRootDisplayAreaBounds());
+ ws.matchesDisplayAreaBounds());
assertTrue("isLetterboxedAppWindow() should return true", ws.isLetterboxedAppWindow());
assertTrue("IME shouldn't be attached to app",
dc.computeImeParent() != dc.getImeTarget(IME_TARGET_LAYERING).getWindow()
@@ -707,6 +707,7 @@
// same width and height.
final int displayWidth = dc.mInitialDisplayWidth;
final int displayHeight = dc.mInitialDisplayHeight;
+ final float density = dc.mInitialDisplayDensity;
final int cutoutWidth = 40;
final int cutoutHeight = 10;
final int left = (displayWidth - cutoutWidth) / 2;
@@ -714,9 +715,13 @@
final int right = (displayWidth + cutoutWidth) / 2;
final int bottom = cutoutHeight;
- final Rect r1 = new Rect(left, top, right, bottom);
+ final Rect zeroRect = new Rect();
+ final Rect[] bounds = new Rect[]{zeroRect, new Rect(left, top, right, bottom), zeroRect,
+ zeroRect};
+ final DisplayCutout.CutoutPathParserInfo info = new DisplayCutout.CutoutPathParserInfo(
+ displayWidth, displayHeight, density, "", Surface.ROTATION_0, 1f);
final DisplayCutout cutout = new WmDisplayCutout(
- fromBoundingRect(r1.left, r1.top, r1.right, r1.bottom, BOUNDS_POSITION_TOP), null)
+ DisplayCutout.constructDisplayCutout(bounds, Insets.NONE, info), null)
.computeSafeInsets(displayWidth, displayHeight).getDisplayCutout();
dc.mInitialDisplayCutout = cutout;
@@ -731,9 +736,12 @@
// | | ---o
// | | |
// | | -------------
- final Rect r = new Rect(top, left, bottom, right);
+ final Rect[] bounds90 = new Rect[]{new Rect(top, left, bottom, right), zeroRect, zeroRect,
+ zeroRect};
+ final DisplayCutout.CutoutPathParserInfo info90 = new DisplayCutout.CutoutPathParserInfo(
+ displayWidth, displayHeight, density, "", Surface.ROTATION_90, 1f);
assertEquals(new WmDisplayCutout(
- fromBoundingRect(r.left, r.top, r.right, r.bottom, BOUNDS_POSITION_LEFT), null)
+ DisplayCutout.constructDisplayCutout(bounds90, Insets.NONE, info90), null)
.computeSafeInsets(displayHeight, displayWidth).getDisplayCutout(),
dc.getDisplayInfo().displayCutout);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
index 2f3004b..a045100 100644
--- a/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/LetterboxTest.java
@@ -17,6 +17,9 @@
package com.android.server.wm;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.clearInvocations;
@@ -47,10 +50,12 @@
SurfaceControlMocker mSurfaces;
SurfaceControl.Transaction mTransaction;
+ private boolean mAreCornersRounded = false;
+
@Before
public void setUp() throws Exception {
mSurfaces = new SurfaceControlMocker();
- mLetterbox = new Letterbox(mSurfaces, StubTransaction::new);
+ mLetterbox = new Letterbox(mSurfaces, StubTransaction::new, () -> mAreCornersRounded);
mTransaction = spy(StubTransaction.class);
}
@@ -64,6 +69,7 @@
private static final int BOTTOM_BAR = 0x2;
private static final int LEFT_BAR = 0x4;
private static final int RIGHT_BAR = 0x8;
+
@Test
public void testNotIntersectsOrFullyContains_usesGlobalCoordinates() {
final Rect outer = new Rect(0, 0, 10, 50);
@@ -165,6 +171,41 @@
}
@Test
+ public void testApplySurfaceChanges_cornersNotRounded_surfaceBehindNotCreated() {
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertNull(mSurfaces.behind);
+ }
+
+ @Test
+ public void testApplySurfaceChanges_cornersRounded_surfaceBehindCreated() {
+ mAreCornersRounded = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertNotNull(mSurfaces.behind);
+ }
+
+ @Test
+ public void testIsOverlappingWith_cornersRounded_doesNotCheckSurfaceBehind() {
+ mAreCornersRounded = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertFalse(mLetterbox.isOverlappingWith(new Rect(1, 2, 9, 9)));
+ }
+
+ @Test
+ public void testNotIntersectsOrFullyContains_cornersRounded_doesNotCheckSurfaceBehind() {
+ mAreCornersRounded = true;
+ mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(0, 0));
+ mLetterbox.applySurfaceChanges(mTransaction);
+
+ assertTrue(mLetterbox.notIntersectsOrFullyContains(new Rect(1, 2, 9, 9)));
+ }
+
+ @Test
public void testSurfaceOrigin_changeCausesReapply() {
mLetterbox.layout(new Rect(0, 0, 10, 10), new Rect(0, 1, 10, 10), new Point(1000, 2000));
mLetterbox.applySurfaceChanges(mTransaction);
@@ -184,6 +225,8 @@
public SurfaceControl right;
private SurfaceControl.Builder mBottomBuilder;
public SurfaceControl bottom;
+ private SurfaceControl.Builder mBehindBuilder;
+ public SurfaceControl behind;
@Override
public SurfaceControl.Builder get() {
@@ -198,6 +241,8 @@
mRightBuilder = (SurfaceControl.Builder) i.getMock();
} else if (((String) i.getArgument(0)).contains("bottom")) {
mBottomBuilder = (SurfaceControl.Builder) i.getMock();
+ } else if (((String) i.getArgument(0)).contains("behind")) {
+ mBehindBuilder = (SurfaceControl.Builder) i.getMock();
}
return i.getMock();
});
@@ -212,6 +257,8 @@
right = control;
} else if (i.getMock() == mBottomBuilder) {
bottom = control;
+ } else if (i.getMock() == mBehindBuilder) {
+ behind = control;
}
return control;
}).when(builder).build();
diff --git a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
index 371e680..942e1c9 100644
--- a/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/SizeCompatTests.java
@@ -16,15 +16,16 @@
package com.android.server.wm;
+import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
import static android.content.pm.ActivityInfo.RESIZE_MODE_RESIZEABLE;
import static android.content.pm.ActivityInfo.RESIZE_MODE_UNRESIZEABLE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.res.Configuration.ORIENTATION_LANDSCAPE;
+import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
-import static android.view.SurfaceProto.ROTATION_180;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -35,6 +36,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.DisplayContent.IME_TARGET_LAYERING;
import static com.android.server.wm.Task.ActivityState.STOPPED;
+import static com.android.server.wm.WindowContainer.POSITION_TOP;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -905,6 +907,57 @@
assertEquals(1000, activityBounds.height());
}
+ @Test
+ public void testSupportsNonResizableInSplitScreen() {
+ // Support non resizable in multi window
+ mAtm.mSupportsNonResizableMultiWindow = true;
+ setUpDisplaySizeWithApp(1000, 2800);
+ final TestSplitOrganizer organizer =
+ new TestSplitOrganizer(mAtm, mActivity.getDisplayContent());
+
+ // Non-resizable landscape activity
+ prepareUnresizable(mActivity, SCREEN_ORIENTATION_LANDSCAPE);
+ final Rect originalBounds = new Rect(mActivity.getBounds());
+
+ // Move activity to split screen
+ mTask.reparent(organizer.mPrimary, POSITION_TOP,
+ false /*moveParents*/, "test");
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mTask.getWindowingMode());
+ assertEquals(WINDOWING_MODE_SPLIT_SCREEN_PRIMARY, mActivity.getWindowingMode());
+
+ // Non-resizable activity in size compat mode
+ assertScaled();
+ assertEquals(originalBounds,
+ mActivity.getConfiguration().windowConfiguration.getBounds());
+
+ // Recompute the natural configuration of the non-resizable activity and the split screen.
+ mActivity.clearSizeCompatMode();
+
+ // Draw letterbox.
+ mActivity.setVisible(false);
+ mActivity.mDisplayContent.prepareAppTransition(WindowManager.TRANSIT_OPEN);
+ mActivity.mDisplayContent.mOpeningApps.add(mActivity);
+ addWindowToActivity(mActivity);
+ mActivity.mRootWindowContainer.performSurfacePlacement();
+
+ // Split screen is also in portrait [1000,1400], so Task should be in letterbox, and
+ // activity fills task.
+ assertEquals(ORIENTATION_LANDSCAPE, mTask.getConfiguration().orientation);
+ assertEquals(ORIENTATION_LANDSCAPE, mActivity.getConfiguration().orientation);
+ assertFitted();
+ assertTrue(mTask.isTaskLetterboxed());
+
+ // Letterbox should fill the gap between the split screen and the letterboxed task.
+ final Rect primarySplitBounds = new Rect(organizer.mPrimary.getBounds());
+ final Rect letterboxedTaskBounds = new Rect(mTask.getBounds());
+ assertTrue(primarySplitBounds.contains(letterboxedTaskBounds));
+ assertEquals(new Rect(letterboxedTaskBounds.left - primarySplitBounds.left,
+ letterboxedTaskBounds.top - primarySplitBounds.top,
+ primarySplitBounds.right - letterboxedTaskBounds.right,
+ primarySplitBounds.bottom - letterboxedTaskBounds.bottom),
+ mActivity.getLetterboxInsets());
+ }
+
private static WindowState addWindowToActivity(ActivityRecord activity) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams();
params.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
index b0b8afd..df5b48a 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowContainerTests.java
@@ -22,6 +22,8 @@
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSET;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.TRANSIT_CLOSE;
+import static android.view.WindowManager.TRANSIT_OLD_TASK_CLOSE;
import static android.view.WindowManager.TRANSIT_OLD_TASK_OPEN;
import static android.view.WindowManager.TRANSIT_OPEN;
import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER;
@@ -978,6 +980,31 @@
assertEquals(200, listener.mConfiguration.densityDpi);
}
+ @Test
+ public void testFreezeInsetsStateWhenAppTransition() {
+ final Task stack = createTaskStackOnDisplay(mDisplayContent);
+ final Task task = createTaskInStack(stack, 0 /* userId */);
+ final ActivityRecord activity = createActivityRecord(mDisplayContent, task);
+ final WindowState win = createWindow(null, TYPE_BASE_APPLICATION, activity, "win");
+ task.getDisplayContent().prepareAppTransition(TRANSIT_CLOSE);
+ spyOn(win);
+ doReturn(true).when(task).okToAnimate();
+ ArrayList<WindowContainer> sources = new ArrayList<>();
+ sources.add(activity);
+
+ // Simulate the task applying the exit transition, verify the main window of the task
+ // will be set the frozen insets state.
+ task.applyAnimation(null, TRANSIT_OLD_TASK_CLOSE, false /* enter */,
+ false /* isVoiceInteraction */, sources);
+ verify(win).freezeInsetsState();
+
+ // Simulate the task transition finished, verify the frozen insets state of the window
+ // will be reset.
+ task.onAnimationFinished(ANIMATION_TYPE_APP_TRANSITION,
+ task.mSurfaceAnimator.getAnimation());
+ verify(win).clearFrozenInsetsState();
+ }
+
/* Used so we can gain access to some protected members of the {@link WindowContainer} class */
private static class TestWindowContainer extends WindowContainer<TestWindowContainer> {
private final int mLayer;
diff --git a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
index 263aa19..3231f8b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/WindowStateTests.java
@@ -810,4 +810,27 @@
WINDOWING_MODE_SPLIT_SCREEN_PRIMARY);
assertFalse(sameTokenWindow.needsRelativeLayeringToIme());
}
+
+ @Test
+ public void testSetFreezeInsetsState() {
+ final WindowState app = createWindow(null, TYPE_APPLICATION, "app");
+ spyOn(app);
+ doReturn(true).when(app).isVisible();
+
+ // Set freezing the insets state to make the window ignore to dispatch insets changed.
+ final InsetsState expectedState = new InsetsState(app.getInsetsState(),
+ true /* copySources */);
+ app.freezeInsetsState();
+ assertEquals(expectedState, app.getFrozenInsetsState());
+ assertFalse(app.isReadyToDispatchInsetsState());
+ assertEquals(expectedState, app.getInsetsState());
+ mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+ verify(app, never()).notifyInsetsChanged();
+
+ // Unfreeze the insets state to make the window can dispatch insets changed.
+ app.clearFrozenInsetsState();
+ assertTrue(app.isReadyToDispatchInsetsState());
+ mDisplayContent.getInsetsStateController().notifyInsetsChanged();
+ verify(app).notifyInsetsChanged();
+ }
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
index 39976a5..b2646f2 100644
--- a/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/utils/WmDisplayCutoutTest.java
@@ -150,9 +150,9 @@
@Test
public void computeSafeInsets_waterfall() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(1, 2, 3, 4)),
+ Insets.of(1, 2, 3, 4), null),
200, 400);
assertEquals(new Rect(1, 2, 3, 4), cutout.getDisplayCutout().getSafeInsets());
@@ -161,9 +161,9 @@
@Test
public void computeSafeInsets_cutoutTop_greaterThan_waterfallTop() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
- Insets.of(0, 20, 0, 0)),
+ Insets.of(0, 20, 0, 0), null),
200, 400);
assertEquals(new Rect(0, 30, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -172,9 +172,9 @@
@Test
public void computeSafeInsets_cutoutTop_lessThan_waterfallTop() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, new Rect(80, 0, 120, 30), ZERO_RECT, ZERO_RECT},
- Insets.of(0, 40, 0, 0)),
+ Insets.of(0, 40, 0, 0), null),
200, 400);
assertEquals(new Rect(0, 40, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -183,9 +183,9 @@
@Test
public void computeSafeInsets_cutoutLeft_greaterThan_waterfallLeft() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(20, 0, 0, 0)),
+ Insets.of(20, 0, 0, 0), null),
200, 400);
assertEquals(new Rect(30, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -194,9 +194,9 @@
@Test
public void computeSafeInsets_cutoutLeft_lessThan_waterfallLeft() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {new Rect(0, 180, 30, 220), ZERO_RECT, ZERO_RECT, ZERO_RECT},
- Insets.of(40, 0, 0, 0)),
+ Insets.of(40, 0, 0, 0), null),
200, 400);
assertEquals(new Rect(40, 0, 0, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -205,9 +205,9 @@
@Test
public void computeSafeInsets_cutoutBottom_greaterThan_waterfallBottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
- Insets.of(0, 0, 0, 20)),
+ Insets.of(0, 0, 0, 20), null),
200, 400);
assertEquals(new Rect(0, 0, 0, 30), cutout.getDisplayCutout().getSafeInsets());
@@ -216,9 +216,9 @@
@Test
public void computeSafeInsets_cutoutBottom_lessThan_waterfallBottom() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, ZERO_RECT, new Rect(80, 370, 120, 400)},
- Insets.of(0, 0, 0, 40)),
+ Insets.of(0, 0, 0, 40), null),
200, 400);
assertEquals(new Rect(0, 0, 0, 40), cutout.getDisplayCutout().getSafeInsets());
@@ -227,9 +227,9 @@
@Test
public void computeSafeInsets_cutoutRight_greaterThan_waterfallRight() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
- Insets.of(0, 0, 20, 0)),
+ Insets.of(0, 0, 20, 0), null),
200, 400);
assertEquals(new Rect(0, 0, 30, 0), cutout.getDisplayCutout().getSafeInsets());
@@ -238,9 +238,9 @@
@Test
public void computeSafeInsets_cutoutRight_lessThan_waterfallRight() {
WmDisplayCutout cutout = WmDisplayCutout.computeSafeInsets(
- DisplayCutout.fromBoundsAndWaterfall(
+ DisplayCutout.constructDisplayCutout(
new Rect[] {ZERO_RECT, ZERO_RECT, new Rect(170, 180, 200, 220), ZERO_RECT},
- Insets.of(0, 0, 40, 0)),
+ Insets.of(0, 0, 40, 0), null),
200, 400);
assertEquals(new Rect(0, 0, 40, 0), cutout.getDisplayCutout().getSafeInsets());
diff --git a/telecomm/java/android/telecom/TelecomManager.java b/telecomm/java/android/telecom/TelecomManager.java
index 5b03863..472d639 100644
--- a/telecomm/java/android/telecom/TelecomManager.java
+++ b/telecomm/java/android/telecom/TelecomManager.java
@@ -314,20 +314,22 @@
public static final String EXTRA_HAS_PICTURE = "android.telecom.extra.HAS_PICTURE";
/**
- * A URI representing the picture that was downloaded when a call is received.
+ * A URI representing the picture that was downloaded when a call is received or uploaded
+ * when a call is placed.
+ *
* This is a content URI within the call log provider which can be used to open a file
* descriptor. This could be set a short time after a call is added to the Dialer app if the
- * download is delayed for some reason. The Dialer app will receive a callback via
+ * download/upload is delayed for some reason. The Dialer app will receive a callback via
* {@link Call.Callback#onDetailsChanged} when this value has changed.
*
* Reference: RCC.20 Section 2.4.3.2
*/
- public static final String EXTRA_INCOMING_PICTURE = "android.telecom.extra.INCOMING_PICTURE";
+ public static final String EXTRA_PICTURE_URI = "android.telecom.extra.PICTURE_URI";
- // TODO(hallliu), This UUID is obtained from TelephonyManager#uploadCallComposerPicture.
/**
* A ParcelUuid used as a token to represent a picture that was uploaded prior to the call
- * being placed.
+ * being placed. The value of this extra should be set using the {@link android.os.ParcelUuid}
+ * obtained from the callback in {@link TelephonyManager#uploadCallComposerPicture}.
*/
public static final String EXTRA_OUTGOING_PICTURE = "android.telecom.extra.OUTGOING_PICTURE";
diff --git a/telephony/java/android/telephony/PhysicalChannelConfig.java b/telephony/java/android/telephony/PhysicalChannelConfig.java
index b359ebe..fadf0e1 100644
--- a/telephony/java/android/telephony/PhysicalChannelConfig.java
+++ b/telephony/java/android/telephony/PhysicalChannelConfig.java
@@ -554,7 +554,8 @@
}
public @NonNull Builder setFrequencyRange(int frequencyRange) {
- if (!ServiceState.isFrequencyRangeValid(frequencyRange)) {
+ if (!ServiceState.isFrequencyRangeValid(frequencyRange)
+ && frequencyRange != ServiceState.FREQUENCY_RANGE_UNKNOWN) {
throw new IllegalArgumentException("Frequency range: " + frequencyRange +
" is invalid.");
}
diff --git a/telephony/java/android/telephony/TelephonyLocalConnection.java b/telephony/java/android/telephony/TelephonyLocalConnection.java
new file mode 100644
index 0000000..1cab267
--- /dev/null
+++ b/telephony/java/android/telephony/TelephonyLocalConnection.java
@@ -0,0 +1,47 @@
+/*
+ * 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.telephony;
+
+import java.util.UUID;
+
+/**
+ * Shim used for code in frameworks/opt/telephony to be able to call code in
+ * packages/services/Telephony. A singleton instance of this class is set when the phone process
+ * is brought up.
+ * @hide
+ */
+public class TelephonyLocalConnection {
+ public interface ConnectionImpl {
+ String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid);
+ }
+ private static ConnectionImpl sInstance;
+
+ public static String getCallComposerServerUrlForHandle(int subscriptionId, UUID uuid) {
+ checkInstance();
+ return sInstance.getCallComposerServerUrlForHandle(subscriptionId, uuid);
+ }
+
+ private static void checkInstance() {
+ if (sInstance == null) {
+ throw new IllegalStateException("Connection impl is null!");
+ }
+ }
+
+ public static void setInstance(ConnectionImpl impl) {
+ sInstance = impl;
+ }
+}
diff --git a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
index 59e375f..f474ec2 100644
--- a/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
+++ b/tests/FlickerTests/src/com/android/server/wm/flicker/ime/CloseImeWindowToHomeTest.kt
@@ -114,7 +114,8 @@
enabled = !configuration.startRotation.isRotated())
navBarLayerIsAlwaysVisible()
statusBarLayerIsAlwaysVisible()
- visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE))
+ visibleLayersShownMoreThanOneConsecutiveEntry(listOf(IME_WINDOW_TITLE),
+ enabled = false)
imeLayerBecomesInvisible()
imeAppLayerBecomesInvisible(testApp)
diff --git a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
index 24b1854..1d479fc 100644
--- a/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
+++ b/wifi/java/src/android/net/wifi/nl80211/SingleScanSettings.java
@@ -32,6 +32,7 @@
private static final String TAG = "SingleScanSettings";
public int scanType;
+ public boolean enable6GhzRnr;
public ArrayList<ChannelSettings> channelSettings;
public ArrayList<HiddenNetwork> hiddenNetworks;
@@ -50,6 +51,7 @@
return false;
}
return scanType == settings.scanType
+ && enable6GhzRnr == settings.enable6GhzRnr
&& channelSettings.equals(settings.channelSettings)
&& hiddenNetworks.equals(settings.hiddenNetworks);
}
@@ -57,7 +59,7 @@
/** override hash code */
@Override
public int hashCode() {
- return Objects.hash(scanType, channelSettings, hiddenNetworks);
+ return Objects.hash(scanType, channelSettings, hiddenNetworks, enable6GhzRnr);
}
@@ -83,6 +85,7 @@
Log.wtf(TAG, "Invalid scan type " + scanType);
}
out.writeInt(scanType);
+ out.writeBoolean(enable6GhzRnr);
out.writeTypedList(channelSettings);
out.writeTypedList(hiddenNetworks);
}
@@ -100,6 +103,7 @@
if (!isValidScanType(result.scanType)) {
Log.wtf(TAG, "Invalid scan type " + result.scanType);
}
+ result.enable6GhzRnr = in.readBoolean();
result.channelSettings = new ArrayList<ChannelSettings>();
in.readTypedList(result.channelSettings, ChannelSettings.CREATOR);
result.hiddenNetworks = new ArrayList<HiddenNetwork>();
diff --git a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
index db2eb99..ef26532 100644
--- a/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
+++ b/wifi/java/src/android/net/wifi/nl80211/WifiNl80211Manager.java
@@ -28,6 +28,7 @@
import android.net.wifi.WifiAnnotations;
import android.net.wifi.WifiScanner;
import android.os.Binder;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -90,6 +91,10 @@
*/
public static final int SCAN_TYPE_PNO_SCAN = 1;
+ // Extra scanning parameter used to enable 6Ghz RNR (Reduced Neighbour Support).
+ public static final String SCANNING_PARAM_ENABLE_6GHZ_RNR =
+ "android.net.wifi.nl80211.SCANNING_PARAM_ENABLE_6GHZ_RNR";
+
private AlarmManager mAlarmManager;
private Handler mEventHandler;
@@ -911,6 +916,15 @@
}
/**
+ * @deprecated replaced by {@link #startScan(String, int, Set, List, Bundle)}
+ **/
+ @Deprecated
+ public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
+ @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
+ return startScan(ifaceName, scanType, freqs, hiddenNetworkSSIDs, null);
+ }
+
+ /**
* Start a scan using the specified parameters. A scan is an asynchronous operation. The
* result of the operation is returned in the {@link ScanEventCallback} registered when
* setting up an interface using
@@ -929,11 +943,13 @@
* @param freqs list of frequencies to scan for, if null scan all supported channels.
* @param hiddenNetworkSSIDs List of hidden networks to be scanned for, a null indicates that
* no hidden frequencies will be scanned for.
+ * @param extraScanningParams bundle of extra scanning parameters.
* @return Returns true on success, false on failure (e.g. when called before the interface
* has been set up).
*/
public boolean startScan(@NonNull String ifaceName, @WifiAnnotations.ScanType int scanType,
- @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs) {
+ @Nullable Set<Integer> freqs, @Nullable List<byte[]> hiddenNetworkSSIDs,
+ @Nullable Bundle extraScanningParams) {
IWifiScannerImpl scannerImpl = getScannerImpl(ifaceName);
if (scannerImpl == null) {
Log.e(TAG, "No valid wificond scanner interface handler for iface=" + ifaceName);
@@ -948,6 +964,9 @@
}
settings.channelSettings = new ArrayList<>();
settings.hiddenNetworks = new ArrayList<>();
+ if (extraScanningParams != null) {
+ settings.enable6GhzRnr = extraScanningParams.getBoolean(SCANNING_PARAM_ENABLE_6GHZ_RNR);
+ }
if (freqs != null) {
for (Integer freq : freqs) {
diff --git a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
index 9059208..fd595fa 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/SingleScanSettingsTest.java
@@ -74,6 +74,7 @@
new ArrayList<>(Arrays.asList(mChannelSettings1, mChannelSettings2));
scanSettings.hiddenNetworks =
new ArrayList<>(Arrays.asList(mHiddenNetwork1, mHiddenNetwork2));
+ scanSettings.enable6GhzRnr = true;
Parcel parcel = Parcel.obtain();
scanSettings.writeToParcel(parcel, 0);
diff --git a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
index 9ee0acbf..4b03a49 100644
--- a/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/nl80211/WifiNl80211ManagerTest.java
@@ -44,6 +44,7 @@
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiScanner;
import android.net.wifi.util.HexEncoding;
+import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
@@ -506,7 +507,51 @@
SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ }
+
+ /**
+ * Verify the new startScan() API can convert input parameters to SingleScanSettings correctly.
+ */
+ @Test
+ public void testScanWithBundle() throws Exception {
+ when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(WifiNl80211Manager.SCANNING_PARAM_ENABLE_6GHZ_RNR, true);
+ assertTrue(mWificondControl.startScan(
+ TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, bundle));
+ verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
+ IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, true)));
+ }
+
+ /**
+ * Verify default values in SingleScanSettings when the input Bundle to startScan is null.
+ */
+ @Test
+ public void testScanWithNullBundle() throws Exception {
+ when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
+ assertTrue(mWificondControl.startScan(
+ TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, null));
+ verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
+ IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
+ }
+
+ /**
+ * Verify default values in SingleScanSettings when the input Bundle to startScan is empty.
+ */
+ @Test
+ public void testScanWithEmptyBundle() throws Exception {
+ when(mWifiScannerImpl.scan(any(SingleScanSettings.class))).thenReturn(true);
+ assertTrue(mWificondControl.startScan(
+ TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, new Bundle()));
+ verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
+ IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
}
/**
@@ -527,7 +572,7 @@
// But the argument passed down should have the duplicate removed.
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
IWifiScannerImpl.SCAN_TYPE_LOW_POWER,
- SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST)));
+ SCAN_FREQ_SET, SCAN_HIDDEN_NETWORK_SSID_LIST, false)));
}
/**
@@ -539,7 +584,7 @@
assertTrue(mWificondControl.startScan(
TEST_INTERFACE_NAME, WifiScanner.SCAN_TYPE_HIGH_ACCURACY, null, null));
verify(mWifiScannerImpl).scan(argThat(new ScanMatcher(
- IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null)));
+ IWifiScannerImpl.SCAN_TYPE_HIGH_ACCURACY, null, null, false)));
}
/**
@@ -1068,11 +1113,14 @@
int mExpectedScanType;
private final Set<Integer> mExpectedFreqs;
private final List<byte[]> mExpectedSsids;
+ private final boolean mExpectedEnable6GhzRnr;
- ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids) {
+ ScanMatcher(int expectedScanType, Set<Integer> expectedFreqs, List<byte[]> expectedSsids,
+ boolean expectedEnable6GhzRnr) {
this.mExpectedScanType = expectedScanType;
this.mExpectedFreqs = expectedFreqs;
this.mExpectedSsids = expectedSsids;
+ this.mExpectedEnable6GhzRnr = expectedEnable6GhzRnr;
}
@Override
@@ -1080,6 +1128,9 @@
if (settings.scanType != mExpectedScanType) {
return false;
}
+ if (settings.enable6GhzRnr != mExpectedEnable6GhzRnr) {
+ return false;
+ }
ArrayList<ChannelSettings> channelSettings = settings.channelSettings;
ArrayList<HiddenNetwork> hiddenNetworks = settings.hiddenNetworks;
if (mExpectedFreqs != null) {